django搭建一个博客,登录登出

74 篇文章 2 订阅
  • 用的是pycharm专业版本,并没有使用虚拟环境,使用的是django2.1,python3.6
  • 写这个项目需要你了解django基本结构与语法,不然在映射和函数之间,你会晕头转向
  • 了解简单的html css JavaScript代码

第一课堂:简单创建项目和app

  • 创建项目
  1. pycharm 人性化,必须专业版
  2. 命令行,进入此目录,写下如下代码
python3 manage.py startproject 项目名字
# manage.py则是每个Django项目中自动生成的一个用于管理项目的脚本文件,需要在cmd窗口中cd到Django项目的manage.py所在的目录后通过python命令执行。其中的command是Django内置的或者你自定义的命令
  • 创建app
命令行进入项目目录
python3 manegy.py startapp app名字
  • 由于我们设计的登录和检测,肯定是一个需要记录时间的,所以我们在settings文件里面设置中国时区
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

第二课堂:设计数据模型

  • 数据库模型设计
  1. 明确我们设计的模型需要那些输入:用户名,密码,邮箱地址,创建时间
  2. 有了目标,在models里面,进行相应的设计,一些属性简单易懂
class User(models.Model):

    gender = (
        ('male', "男"),
        ('female', "女"),
    )

    name = models.CharFiel(max_length=128unique=True)
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.CharField(max_length=32choices=gender, default="男")
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["-c_time"]
        verbose_name = "用户"
        verbose_name_plural = "用户"
  • 连接数据库,这里我有几个问题需要解决
  1. 把你的数据库编码全部解决,进入你的数据库中
show variables like '%char%';#显示你数据库字符集列表
set character_set_client=utf8;
  set character_set_connection=utf8;
  set character_set_database=utf8;
  set character_set_results=utf8;
  set character_set_server=utf8;
  set character_set_system=utf8;
  set collation_connection=utf8;
  set collation_database=utf8;
  set collation_server=utf8;
上面代码分行写,分别运行,不要管警告
--------------------- 
作者:胖虎艾春辉 
来源:CSDN 
原文:https://blog.csdn.net/weixin_42479155/article/details/85345973 
版权声明:本文为博主原创文章,转载请附上博文链接!
  1. 如果你是使用navicat强烈建议,你重新检查一遍,字符集一定选utf8
  2. 将你的新建app全部加入settings里面的app注册表里
  3. 激活模型:
python3 manage.py migrate

第三课堂:创建后台:管理人员,路由和视图

  • 在admin里面与你创建的模型进行对接,在admin里面输入
admin.site.register(models.User)
  • 创建超级管理员权限
python3 manage.py createsuperuser
  • 路由需求:主页,登录,注册,登出
  • 搭建映射与函数(在app的views.py
urls里面的分配:
from django.contrib import admin
from django.urls import path
from login import views
from django.conf.urls import include
from django.urls import re_path
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index, name = 'index'),
    path('login/',views.login,name = 'login'),
    #path('',views.base,name ='base'),
    path('',views.index),
    path('register/',views.register,name = 'register'),
    path('logout/',views.logout,name = 'logout'),
]
#**这里我需要提醒,为了避免函数调用出现问题,我们给每一个映射都设计一个命名空间**
  • 在login/view.py里面构建简单映射
from django.shortcuts import render
from django.shortcuts import redirect

# Create your views here.


def index(request):
    pass
    return render(request, 'login/index.html')


def login(request):
    pass
    return render(request, 'login/login.html')


def register(request):
    pass
    return render(request, 'login/register.html')


def logout(request):
    pass
    return redirect("/index/")
# 这里我提醒:导入redirect是为了重定向
render函数有两个参数,第一个request准备返回,固定参数,不要随意改变,第二个参数是需要渲染的页面

ansfdj

第四课堂:前端页面设计

  • 如果不继承的话是这样的:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>

    <div style="margin: 15% 40%;">
        <h1>欢迎登录!</h1>
       <form action="/login/" method="post">
            <p>
                <label for="id_username">用户名:</label>
                <input type="text" id="id_username" name="username" placeholder="用户名" autofocus required />
            </p>
            <p>
                <label for="id_password">密码:</label>
                <input type="password" id="id_password" placeholder="密码" name="password" required >
            </p>
            <input type="submit" value="确定">
        </form>
    </div>

</body>
</html>
这样的页面非常丑
  • 使用bootstrap开源框架
  1. 这个框架弥补你对前端开发的不足,

  2. 同时相匹配的jquery下载
    http://www.bootcss.com/)

  3. 对于静态文件的引入,在settings文件中,它有一个STATICFILES_DIRS的模块,所以在最下面一行加入引导路径

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]
  • 同事,前端页面不可能都各自编写,而是要有复用性,也就是说你这个html文件可以被继承和扩展
  • 在bootstap文档中,为我们这些小白提供了一个入门模板:
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>你好,世界!</h1>

    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="js/bootstrap.min.js"></script>
  </body>
</html>
  • 最后我们设计的网页基础模板,里面加入了自己改造的导航条:
{% load staticfiles %}
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>{% block title %}base{% endblock %}</title>

    <!-- Bootstrap -->
    <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet">

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
      <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
    {% block css %}{% endblock %}
  </head>
  <body>
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#my-nav" aria-expanded="false">
            <span class="sr-only">切换导航条</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Mysite</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">主页</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="/login/">登录</a></li>
            <li><a href="/register/">注册</a></li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>

    {% block content %}{% endblock %}


    <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
    <script src="{% static 'js/jquery-3.2.1.js' %}"></script>
    <!-- Include all compiled plugins (below), or include individual files as needed -->
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
  </body>
</html>

  1. 通过页面顶端的{% load staticfiles %}加载后,才可以使用static方法;
  2. 通过{% block title %}base{% endblock %},设置了一个动态的页面title块;
  3. 通过{% block css %}{% endblock %},设置了一个动态的css加载块;
  4. 通过{% block content %}{% endblock %},为具体页面的主体内容留下接口;
  5. 通过{% static ‘bootstrap-3.3.7-dist/css/bootstrap.min.css’ %}将样式文件指向了我们的实际静态文件,下面的js脚本也是同样的道理
  • 修改login.html文件对base文件进行拓展:
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">
              <h2 class="text-center">欢迎登录</h2>
              <div class="form-group">
                <label for="id_username">用户名:</label>
                <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
              </div>
              <div class="form-group">
                <label for="id_password">密码:</label>
                <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
              </div>
              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>
          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}
  1. 通过{% extends ‘base.html’ %}继承了‘base.html’模板的内容;
  2. 通过{% block title %}登录{% endblock %}设置了专门的title;
  3. 通过block css引入了针对性的login.css样式文件;
  4. 主体内容定义在block content内部
  5. 添加了一个重置按钮
  • 在static目录下新建一个css文件夹,在里面新建login.css文件,在里面设置css样式,具体样式是这样
body{
    background-image: url("8b13632762d0f703a7484cf103fa513d2697c521.jpg");
}
.form-login{
    max-width: 330px;
    padding: 15px;
    margin: 0 auto;
}
.form-login .form-control{
    position: relative;
    height: auto;
    -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
    box-sizing: border-box;
    padding: 10px;
    font-size: 16px;
}
.form-login .form-control:focus{
    z-index: 2;
}
.form-login input[type = "text"]{
    margin-bottom: -1px;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
.form-login input[type = password]{
    margin-bottom: 10px;
    border-bottom-left-radius: 0;
    border-bottom-left-radius: 0;
}
  • 同时,如果你想加入背景的话,可以在body属性的css文件下添加background-image属性 = url(建议你的背景图片放在同一个文件夹下,这样就没有必要去寻找了

第五课堂:登录视图

  • 我们希望用户通过login页面填写表单和用户名密码,并且是POST的方式发送到服务器/login/地址,服务器通过视图函数,接受并处理这些请求:
def login(request):
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username, password)
        return redirect('/index/')
    return render(request, 'login/login.html')
解释:
1. 每一个视图函数都至少接受一个参数,并且是第一位置参数,该参数封装了当前请求的所有数据
2. request.method分装了数据请求大写POST,将执行if语句
3. get('username')的键名字是html表单中input元素中name属性定义的值,所以不能忘记添加name属性
4. 利用redirect方法,将页面重定向到index页
5. 但是你这样也是不成功的,为什么呢?因为django有一部分的内置防御功能,解决问题就是在所有的form里面,添加:{% csrf_token %}
  • 数据验证,实现必填字段不能为空,密码用圆点代替隐藏
def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        if username and password:  # 确保用户名和密码都不为空
            username = username.strip()
            # 用户名字符合法性验证
            # 密码长度验证
            # 更多的其它验证.....            
            return redirect('/index/')
    return render(request, 'login/login.html')
  • 连接数据库并且进行验证:
def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        if username and password:  # 确保用户名和密码都不为空
            username = username.strip()
            # 用户名字符合法性验证
            # 密码长度验证
            # 更多的其它验证.....
            try:
                user = models.User.objects.get(name=username)
            except:
                return render(request, 'login/login.html')
            if user.password == password:
                return redirect('/index/')
    return render(request, 'login/login.html')
解释:
1. 导入models模块
2. models.User.objects.get(name=username)是Django提供的最常用的数据查询API,因为我们之前设计好了数据库的表

  • 添加提示信息
def login(request):
    if request.method == "POST":
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        message = "所有字段都必须填写!"
        if username and password:  # 确保用户名和密码都不为空
            username = username.strip()
            # 用户名字符合法性验证
            # 密码长度验证
            # 更多的其它验证.....
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户名不存在!"
        return render(request, 'login/login.html', {"message": message})
    return render(request, 'login/login.html')
  • 对于登录页面进行修改:
<form class='form-login' action="/login/" method="post">

  {% if message %}
      <div class="alert alert-warning">{{ message }}</div>
  {% endif %}

  {% csrf_token %}
  <h2 class="text-center">欢迎登录</h2>
  <div class="form-group">
    <label for="id_username">用户名:</label>
    <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>
  </div>
  <div class="form-group">
    <label for="id_password">密码:</label>
    <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>
  </div>
  <button type="reset" class="btn btn-default pull-left">重置</button>
  <button type="submit" class="btn btn-primary pull-right">提交</button>
</form>

在这里插入图片描述

第6课堂:设计表单

  • 原理:django在内部集成了一个表单功能,以面对对象的方式,直接使用python代码生成html表单代码:准备和重构数据用于页面渲染;
    为数据创建HTML表单元素;
    接收和处理用户从表单发送过来的数据
  • 创建表单forms,就是在login这个app下创建forms.py文件
from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="用户名", max_length=128)
    password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput)
解释:
1. 导入forms模块
2. 所有的表单都需要继承自forms.Form类
3. 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中<form>内的一个input元素
4. label参数用于设置<label>标签
5. widget=forms.PasswordInput用于指定该字段在form表单里表现为<input type='password' />,也就是密码输入框
  • 相应的对映射进行修改
def login(request):
    if request.method == "POST":
        login_form = forms.UserForm(request.POST)
        message = "请检查填写的内容!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(name=username)
                if user.password == password:
                    return redirect('/index/')
                else:
                    message = "密码不正确!"
            except:
                message = "用户不存在!"
        return render(request, 'login/login.html', locals())

    login_form = forms.UserForm()
    return render(request, 'login/login.html', locals())
1. 对于请求不是post的返回空的表格
2. 对于post表格,接收表单数据,并验证
3. 采用is_valid进行验证,验证过程有:#先来归纳一下整个流程
#(1)首先is_valid()起手,看seld.errors中是否值,只要有值就是flase
#(2)接着分析errors.里面判断_errors是都为空,如果为空返回self.full_clean(),否则返回self._errors
#(3)现在就要看full_clean(),是何方神圣了,里面设置_errors和cleaned_data这两个字典,一个存错误字段,一个存储正确字段。
#(4)在full_clean最后有一句self._clean_fields(),表示校验字段
#(5)在_clean_fields函数中开始循环校验每个字段,真正校验字段的是field.clean(value),怎么校验的不管
#(6)在_clean_fields中可以看到,会将字段分别添加到_errors和cleaned_data这两个字典中
#(7)结尾部分还设置了钩子,找clean_XX形式的,有就执行。执行错误信息也会添加到_errors中
#(8)整个校验过程完成
4. 验证成功后可以从表单对象的cleaned_data数据字典中获取表单的具体值
5. 此外使用了locals这个函数,它返回当前所有的本地变量字典,直接打包,提高了效率
  • 修改html页面:
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}登录{% endblock %}
{% block css %}<link href="{% static 'css/login.css' %}" rel="stylesheet"/>{% endblock %}


{% block content %}
    <div class="container">
        <div class="col-md-4 col-md-offset-4">
          <form class='form-login' action="/login/" method="post">

              {% if message %}
                  <div class="alert alert-warning">{{ message }}</div>
              {% endif %}
              {% csrf_token %}
              <h2 class="text-center">欢迎登录</h2>

              {{ login_form }}

              <button type="reset" class="btn btn-default pull-left">重置</button>
              <button type="submit" class="btn btn-primary pull-right">提交</button>

          </form>
        </div>
    </div> <!-- /container -->
{% endblock %}
1. 使用一个{{login_form}}就直接完成了表单内容的生成工作,而这个变量来自在视图函数中生成的form实例的变量名,同时,locals将其传入
2. 使用post的时候,必添加{% csrf_token %}用于处理csrf安全机制
3. 实际上除了通过{{ login_form }}简单地将表单渲染到HTML页面中了,还有下面几种方式:

{{ login_form.as_table }} 将表单渲染成一个表格元素,每个输入框作为一个<tr>标签
{{ login_form.as_p }} 将表单的每个输入框包裹在一个<p>标签内
{{ login_form.as_ul }} 将表单渲染成一个列表元素,每个输入框作为一个<li>标签
  • 但其实这个一点也不好看,我有强迫症,所以手动渲染:
<div class="form-group">
  {{ login_form.username.label_tag }}
  {{ login_form.username}}
</div>
<div class="form-group">
  {{ login_form.password.label_tag }}
  {{ login_form.password }}
</div>
  • 为了还原bootstarp框架一个清白,我们再次修改forms代码:
from django import forms

class UserForm(forms.Form):
    username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-contro

第七课堂: 图片验证码

  • 为了防止爬虫无限制的爬你的网站,导致你的服务器瘫痪,为此,我们需要提供验证码功能
  • 步骤
  1. 看你的系统,我的系统是ubuntu18.04所以,我使用命令行输入
pip3 install django-simple-captcha
但是我遇到了问题上面显示我的mysqldb不能安装
没办法,只能sudo apt-get install python3-mysql解决了
  1. 注册app,captcha是一个系统内置的app,记住,不需要你取新建这个名字的app,而是只需要你在app列表进行注册就行
  2. 同时,在终端下执行下列语句:这句话的意思是将验证码自己制造的表推送到目的地
python3 manage.py migrate
  1. 使用二级路由,加入内置的路由,这是我个人的设置大家可以仿照一下
from django.contrib import admin
from django.urls import path
from login import views
from django.conf.urls import include
from django.urls import re_path
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index, name = 'index'),
    path('login/',views.login,name = 'login'),
    #path('',views.base,name ='base'),
    path('register/',views.register,name = 'register'),
    path('logout/',views.logout,name = 'logout'),
    re_path(r'^captcha',include('captcha.urls'))#添加captcha相对应的网址
]

  1. 在forms里面添加captchafield,这个文件下代码如下
from django import forms
#添加图形验证码
from captcha.fields import CaptchaField

class UserForm(forms.Form):
    username = forms.CharField(label="用户名:",max_length=128,widget=forms.TextInput(attrs={'class': 'form-control'}))

    password = forms.CharField(label="密码:",max_length=256,widget=forms.PasswordInput(attrs={'class': 'form-control'}))

    captcha = CaptchaField(label='验证码')
  1. 修改login.html
  <div class="form-group">
                  {{ login_form.username.label_tag }}
                  {{ login_form.username }}
              </div>
              <div class="form-group">
                  {{ login_form.password.label_tag }}
                  {{ login_form.password}}
              </div>
              <div class="form-group">
                  {{ login_form.captcha.errors }}#指示用户,你的验证码有问题
                  {{ login_form.captcha.label_tag }}
                  {{ login_form.captcha }}
              </div>
  • 虽然页面有点抽,但是框架还是不错的,is_valid()是内置的forms的验证方法
    在这里插入图片描述

第八课堂: sessions会话

  • 因为http协议特性,每一次来自于用户的浏览器请求是无状态的、独立的,也就是说无法保存用户状态,你可以在网页中尝试你把cookies进行关闭,看看你能不能访问天猫淘宝等
  • 所有现代网站通常将cookies保存不重要的东西,大部分重要的东西通过sessions会话功能进行保存在服务器端
  • django提供了一个通用sessions框架,并且可以使用多种sessions数据保存方式
  1. 保存在数据库里面
  2. 保存到缓存
  3. 保存到文件里
  4. 保存在cookies内
  • django默认session框架,你不要修改就行,大致是这样,在settings文件下
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',#sessions会话窗口,相当于认证你是不是用户还是陌生人
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'login',
    'captcha'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',#启用sessions
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
  • sessions 用法
class backends.base.SessionBase
        # 这是所有会话对象的基类,包含标准的字典方法:
        __getitem__(key)
            Example: fav_color = request.session['fav_color']
        __setitem__(key, value)
            Example: request.session['fav_color'] = 'blue'
        __delitem__(key)
            Example: del request.session['fav_color']  # 如果不存在会抛出异常
        __contains__(key)
            Example: 'fav_color' in request.session
        get(key, default=None)
            Example: fav_color = request.session.get('fav_color', 'red')
        pop(key, default=__not_given)
            Example: fav_color = request.session.pop('fav_color', 'blue')
        # 类似字典数据类型的内置方法
        keys()
        items()
        setdefault()
        clear()


        # 它还有下面的方法:
        flush()
            # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。

        set_test_cookie()
            # 设置一个测试cookie,用于探测用户浏览器是否支持cookies。由于cookie的工作机制,你只有在下次用户请求的时候才可以测试。
        test_cookie_worked()
            # 返回True或者False,取决于用户的浏览器是否接受测试cookie。你必须在之前先调用set_test_cookie()方法。
        delete_test_cookie()
            # 删除测试cookie。
        set_expiry(value)
            # 设置cookie的有效期。可以传递不同类型的参数值:
        • 如果值是一个整数,session将在对应的秒数后失效。例如request.session.set_expiry(300) 将在300秒后失效.
        • 如果值是一个datetime或者timedelta对象, 会话将在指定的日期失效
        • 如果为0,在用户关闭浏览器后失效
        • 如果为None,则将使用全局会话失效策略
        失效时间从上一次会话被修改的时刻开始计时。

        get_expiry_age()
            # 返回多少秒后失效的秒数。对于没有自定义失效时间的会话,这等同于SESSION_COOKIE_AGE.
            # 这个方法接受2个可选的关键字参数
        • modification:会话的最后修改时间(datetime对象)。默认是当前时间。
        •expiry: 会话失效信息,可以是datetime对象,也可以是int或None

        get_expiry_date()
            # 和上面的方法类似,只是返回的是日期

        get_expire_at_browser_close()
            # 返回True或False,根据用户会话是否是浏览器关闭后就结束。

        clear_expired()
            # 删除已经失效的会话数据。
        cycle_key()
            # 创建一个新的会话秘钥用于保持当前的会话数据。django.contrib.auth.login() 会调用这个方法。
  • 步骤
  1. 使用session,修改login视图函数
def login(request):
    if request.session.get('is_login',None):
        return redirect('/index/')
    if request.method == "POST":
        login_form = forms.UserForm(request.POST)
        message = "请检查填写的内容"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']

            #用户名字符合法性验证
            #密码长度验证
            try :
                user = models.User.objects.get(name=username)
                if user.password == password:
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')

                else:
                    message = "密码不正确"
            except:
                message = "用户名不存在"
        return render(request,'login/login.html',locals())
    login_form = forms.UserForm
    return render(request, 'login/login.html',locals())
  • 修改页面,不要担心request没有加载出来,它是内置的,只是不同步,导致没有提示而已

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="my-nav">
          <ul class="nav navbar-nav">
            <li class="active"><a href="/index/">主页</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
              {% if request.session.is_login %}
                  <li><a href="#">当前在线:{{ request.session.user_name }}</a></li>
                  <li><a href="/logout/">登出</a></li>
              {% else %}
                  <li><a href="/login/">登录</a></li>
                  <li><a href="/register/">注册</a></li>
              {% endif %}
          </ul>
        </div><!-- /.navbar-collapse -->
  1. 修改index内容
{% extends 'base.html' %}
{% block title %}主页{% endblock %}
{% block content %}
    {% if request.session.is_login %}
    <h1>你好,{{ request.session.user_name }}!欢迎回来!</h1>
    {% else %}
    <h1>你尚未登录,只能访问公开内容!</h1>
    {% endif %}
{% endblock %}

第九课堂: 注册

  • 竟然登录都已经写好了登录代码,那么你需要一个注册代码,这样才使得和前后端通信完美
  • 步骤
  1. 我们在login的forms文件中在userform的下面新建一个类,那就是我们的注册类
class RegisterForm(forms.Form):
    gender = (
        ('male',"男"),
        ('female',"女"),
    )
    username = forms.CharField(label="用户名",max_length=128,widget=forms.TextInput(attrs={'class':'form-control'}))
    password1 = forms.CharField(label="密码",max_length=256,widget=forms.PasswordInput(attrs={'class':'form-control'}))
    password2 = forms.CharField(label="确认密码",max_length=256,widget=forms.PasswordInput(attrs={'class':'form-control'}))
    email = forms.EmailField(label="邮箱地址",widget=forms.EmailInput(attrs={'class':'form-control'}))
    sex = forms.ChoiceField(label='性别',choices=gender)
    captcha = CaptchaField(label='验证码')
    解释:
    1. gender字典里面和user模型中的一样,可以当成常量使用
    2. password1和2是为了检验两次输入密码的相同性,防止误输入密码
    3. email是一个邮箱输入框
    4. sex是一个select下拉框
  1. 为了与表单相适应,我们在register.html进行对login依样画葫芦
{% extends 'base.html' %}
{% load staticfiles %}
{% block title %}注册{% endblock %}
{% block content %}
   <div class="container">
       <div class="col-md-4 col-md-offset-4">
         <form class='form-register' action="/register/" method="post">

             {% if message %}
                 <div class="alert alert-warning">{{ message }}</div>
             {% endif %}

             {% csrf_token %}

             <h2 class="text-center">欢迎注册</h2>
             <div class="form-group">
                 {{ register_form.username.label_tag }}
                 {{ register_form.username}}
             </div>
             <div class="form-group">
                 {{ register_form.password1.label_tag }}
                 {{ register_form.password1 }}
             </div>
             <div class="form-group">
                 {{ register_form.password2.label_tag }}
                 {{ register_form.password2 }}
             </div>
             <div class="form-group">
                 {{ register_form.email.label_tag }}
                 {{ register_form.email }}
             </div>
             <div class="form-group">
                 {{ register_form.sex.label_tag }}
                 {{ register_form.sex }}
             </div>
             <div class="form-group">
                 {{ register_form.captcha.errors }}
                 {{ register_form.captcha.label_tag }}
                 {{ register_form.captcha }}
             </div>

             <button type="reset" class="btn btn-default pull-left">重置</button>
             <button type="submit" class="btn btn-primary pull-right">提交</button>

         </form>
       </div>
   </div> <!-- /container -->
{% endblock %}
  • 完善views里面的映射函数,和url构建的想对应
def register(request):
    if request.session.get('is_login',None):
        #思考一下,登录状态下注册,肯定不可行

       return  redirect('index/')
    if request.method == "POST":
        register_form = forms.RegisterForm(request.POST)
        message = "请检查填写的内容"
        if register_form.is_valid():
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2:
                message = "两次输入的密码不同"
                return  render(request,'login/register.html',locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用户已经存在,请重新选择用户名'
                    return render(request,'login/register.html',locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '该邮箱地址已经被注册,请使用别的邮箱'
                    return render(request,'login/register.html',locals())
                #当然在上述都没错误的话,我们开始使用我们的东西了
                new_user = models.User()#对于数据库里面表进行实例化,方便使用
                new_user.name = username
                new_user.password =hash_code(password1)
                new_user.email = email
                new_user.sex = sex
                new_user.save()#进行保存
                return  redirect('/login/')#重定向到指定的位置
    register_form = forms.RegisterForm()
    return render(request,'login/register.html',locals())
  • 思考一下,想阿里等大公司,你的密码肯定不可能是显示出来的密码,肯定加密了,对此,我们也使用加密
在views中加入一下
import hashlib

def hash_code(s, salt='mysite'):# 加点盐
   h = hashlib.sha256()
   s += salt
   h.update(s.encode())  # update方法只接收bytes类型
   return h.hexdigest()
   同时对login中password的比较进行修改
   if user.password == hash_code(password):  # 哈希值和数据库内的值进行比对
 new_user.password = hash_code(password1)  # 使用加密密码
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190120121651475.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQ3OTE1NQ==,size_16,color_FFFFFF,t_70)
# 使用sha256算法,将输入密码单向转换为字符串储存在数据库中,这样的话,用户隐私得到很大保护
  • 对相应的登录和注册进行修改
就是对应的密码储存,或者比对需要进行相应的加密就行
def login(request):
    if request.session.get('is_login',None):
        return redirect('/index/')
    if request.method == "POST":
        login_form = forms.UserForm(request.POST)
        message = "请检查填写的内容"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']

            #用户名字符合法性验证
            #密码长度验证
            try :
                user = models.User.objects.get(name=username)
                # 1.
                # 导入models模块
                # 2.
                # models.User.objects.get(name=username)
                # 是Django提供的最常用的数据查询API, 因为我们之前设计好了数据库的表
                if not user.has_confirmed:#进行邮件确认
                    message = "用户还未通过邮件确认"
                    return render(request, 'login/login.html',locals())
                # if user.password == password:将密码与数据库里面值进行比对,而不是提取出来,浪费时间
                # 好像数据库里面的东西拿出来会自动反转
                print(user.password)
                if user.password == hash_code(password):
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')

                else:
                    message = "密码不正确"
            except:
                message = "用户名不存在"
        #  return render(request, 'login/login.html', {"message": message})
        return render(request,'login/login.html',locals())#local更为强大,所有的有关内容,均传递
    login_form = forms.UserForm
    return render(request, 'login/login.html',locals())


def register(request):
    if request.session.get('is_login',None):
        #思考一下,登录状态下注册,肯定不可行
       return  redirect('index/')
    if request.method == "POST":
        register_form = forms.RegisterForm(request.POST)
        message = "请检查填写的内容"
        if register_form.is_valid():
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2:
                message = "两次输入的密码不同"
                return  render(request,'login/register.html',locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用户已经存在,请重新选择用户名'
                    return render(request,'login/register.html',locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '该邮箱地址已经被注册,请使用别的邮箱'
                    return render(request,'login/register.html',locals())
                #当然在上述都没错误的话,我们开始使用我们的东西了
                new_user = models.User()#对于数据库里面表进行实例化,方便使用
                new_user.name = username
                new_user.password =hash_code(password1)
                new_user.email = email
                new_user.sex = sex
                new_user.save()#进行保存


                code = make_confirm_string(new_user)
                send_email(email,code)
                return render(request,'login/confirm.html',locals())#跳转到等待邮件确认页面
    register_form = forms.RegisterForm()
    return render(request,'login/register.html',locals())

在这里插入图片描述

第十课堂: 发送邮件

  • 在django中发送邮件需要在settings中配置参数
# 邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
#是否使用SSL加密,qq企业邮箱要求使用,我们又不是企业邮箱,所以不要使用ssl加密

EMAIL_HOST = 'smtp.163.com'
#指定的smtp服务器名称
EMAIL_PORT = 25
EMAIL_HOST_USER = '15530258872@163.com'
EMAIL_HOST_PASSWORD = 'qq1685715822'#授权码

# 注册有效期天数
CONFIRM_DAYS = 7
  • 先写一个例子,在django里面发邮件的样式:
在mysite文件夹创建一个send_mail.py
import os
from django.core.mail import EmailMultiAlternatives
from django.core.mail import send_mail

os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

if __name__ == '__main__':
    # send_mail(
    #     '测试邮件',
    #     '邮件验证',
    #     '15530258872@163.com',
    #     ['1685715822@qq.com']
    # )
    # 某些邮件公司可能不开放smtp服务
    # 某些公司要求使用ssl安全机制
    # 某些smtp服务对主机名格式有要求
    subject,from_email,to = '测试邮件','15530258872@163.com','1685715822@qq.com'
    text_content = '欢迎'
    html_content = '<p>欢迎访问<a href="www.aichunhui.cn" target="_blank">艾春辉的主页</a></p>'
    msg = EmailMultiAlternatives(subject,text_content,from_email,[to])
    msg.attach_alternative(html_content,"text/html")
    msg.send()

在这里插入图片描述

第十一课堂:确认邮件注册:

  • 我们需要创建一个邮件注册表,用来保存用户的确认码和注册时间
  • 在/login/models.py文件中写入
from django.db import models

# Create your models here.

class User(models.Model):

    gender = (
        ('male', "男"),
        ('female', "女"),
    )

    name = models.CharField(max_length=128, unique=True)
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.CharField(max_length=32, choices=gender, default="男")
    c_time = models.DateTimeField(auto_now_add=True)
    has_confirmed = models.BooleanField(default=False)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["-c_time"]
        verbose_name = "用户"
        verbose_name_plural = "用户"


class ConfirmString(models.Model):
    code = models.CharField(max_length=256)
    user = models.OneToOneField('User')
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.user.name + ":   " + self.code

    class Meta:

        ordering = ["-c_time"]
        verbose_name = "确认码"
        verbose_name_plural = "确认码"
#user模型中增加了确认邮件是否注册的bool型值
#注册模型保存了用户和注册码之间的关系,一对一形式
c_time就是注册时间
  • 在终端里执行:python3 manage.py migrate
  • 在/login/admin里面将模型注册进入
  • 修改注册模型
def register(request):
    if request.session.get('is_login', None):
        # 登录状态不允许注册。你可以修改这条原则!
        return redirect("/index/")
    if request.method == "POST":
        register_form = forms.RegisterForm(request.POST)
        message = "请检查填写的内容!"
        if register_form.is_valid():  # 获取数据
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2:  # 判断两次密码是否相同
                message = "两次输入的密码不同!"
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:  # 用户名唯一
                    message = '用户已经存在,请重新选择用户名!'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:  # 邮箱地址唯一
                    message = '该邮箱地址已被注册,请使用别的邮箱!'
                    return render(request, 'login/register.html', locals())

                # 当一切都OK的情况下,创建新用户

                new_user = models.User()
                new_user.name = username
                new_user.password = hash_code(password1)  # 使用加密密码
                new_user.email = email
                new_user.sex = sex
                new_user.save()

                code = make_confirm_string(new_user)
                send_email(email, code)

                message = '请前往注册邮箱,进行邮件确认!'
                return render(request, 'login/confirm.html', locals())  # 跳转到等待邮件确认页面。
    register_form = forms.RegisterForm()
    return render(request, 'login/register.html', locals())
#其实就是多了两行代码
  • 在views里面添加make_confirm_string(创建确认码对象)
def make_confirm_string(user):
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    code = hash_code(user.name, now)
    models.ConfirmString.objects.create(code=code, user=user,)
    return code
  • 创建一个发送邮件函数:
def send_email(email, code):

    from django.core.mail import EmailMultiAlternatives

    subject = '来自艾春辉的注册确认邮件'

    text_content = '''感谢注册'''

    html_content = '''
                    <p>感谢注册<a href="http://{}/confirm/?code={}" target='_blank';>www.aichunhui.cn</a></p>
                    <p>请点击站点链接完成注册确认!</p>
                    <p>此链接有效期为{}天!</p>
                    '''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)

    msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
    msg.attach_alternative(html_content, "text/html")
    msg.send()
  • 发送注册的邮件都解决了,那么我们现在来处理邮件确认请求
def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''
    try:
        confirm = models.ConfirmString.objects.get(code=code)
    except:
        message = '无效的确认请求!'
        return render(request, 'login/confirm.html', locals())

    c_time = confirm.c_time
    now = datetime.datetime.now()
    if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的邮件已经过期!请重新注册!'
        return render(request, 'login/confirm.html', locals())
    else:
        confirm.user.has_confirmed = True
        confirm.user.save()
        confirm.delete()
        message = '感谢确认,请使用账户登录!'
        return render(request, 'login/confirm.html', locals())

  • 在template里面写入一个confirm.html
{% extends 'base.html' %}
{% block title %}注册确认{% endblock %}
{% block content %}
    <div class="row">
        <h1 class="text-center">{{ message }}</h1>
    </div>
    <script>
        window.setTimeout("window.location = '/login/'",2000)
    </script>
{% endblock %}
  • 在login中比对密码时候进行比对是否注册:插入以下代码

if not user.has_confirmed:
    message = "该用户还未通过邮件确认!"
    return render(request, 'login/login.html', locals())
  • 最后看我的views里面所有的代码:
from django.shortcuts import render
from django.shortcuts import redirect
from . import models
#导入本地的,不是使用默认的from django import forms
from . import forms
from django.conf import settings
import hashlib
import datetime
from mysite import settings
# Create your views here.
def user_confirm(request):
    code = request.GET.get('code', None)
    message = ''
    try:
        confirm = models.ConfirmString.objects.get(code=code)
    except:
        message = '无效的确认请求!'
        return render(request, 'login/confirm.html', locals())

    c_time = confirm.c_time
    now = datetime.datetime.now()
    if now > c_time + datetime.timedelta(settings.CONFIRM_DAYS):
        confirm.user.delete()
        message = '您的邮件已经过期!请重新注册!'
        return render(request, 'login/confirm.html', locals())
    else:
        confirm.user.has_confirmed = True
        confirm.user.save()
        confirm.delete()
        message = '感谢确认,请使用账户登录!'
        return render(request, 'login/confirm.html', locals())


def send_email(email, code):

    from django.core.mail import EmailMultiAlternatives

    subject = '来自艾春辉的注册确认邮件'

    text_content = '''感谢注册'''

    html_content = '''
                    <p>感谢注册<a href="http://{}/confirm/?code={}" target='_blank';>www.aichunhui.cn</a></p>
                    <p>请点击站点链接完成注册确认!</p>
                    <p>此链接有效期为{}天!</p>
                    '''.format('127.0.0.1:8000', code, settings.CONFIRM_DAYS)

    msg = EmailMultiAlternatives(subject, text_content, settings.EMAIL_HOST_USER, [email])
    msg.attach_alternative(html_content, "text/html")
    msg.send()


def make_confirm_string(user):
    now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    code = hash_code(user.name, now)
    models.ConfirmString.objects.create(code=code, user=user,)
    return code
def hash_code(s, salt = 'mysite'):
    #使用sha256
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())#update方法只接受bytes流类型
    return h.hexdigest()


def index(request):
    return render(request, 'login/index.html')




def login(request):
    if request.session.get('is_login',None):
        return redirect('/index/')
    if request.method == "POST":
        login_form = forms.UserForm(request.POST)
        message = "请检查填写的内容"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']

            #用户名字符合法性验证
            #密码长度验证
            try :
                user = models.User.objects.get(name=username)
                # 1.
                # 导入models模块
                # 2.
                # models.User.objects.get(name=username)
                # 是Django提供的最常用的数据查询API, 因为我们之前设计好了数据库的表
                if not user.has_confirmed:#进行邮件确认
                    message = "用户还未通过邮件确认"
                    return render(request, 'login/login.html',locals())
                # if user.password == password:将密码与数据库里面值进行比对,而不是提取出来,浪费时间
                # 好像数据库里面的东西拿出来会自动反转
                print(user.password)
                if user.password == hash_code(password):
                    request.session['is_login'] = True
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.name
                    return redirect('/index/')

                else:
                    message = "密码不正确"
            except:
                message = "用户名不存在"
        #  return render(request, 'login/login.html', {"message": message})
        return render(request,'login/login.html',locals())#local更为强大,所有的有关内容,均传递
    login_form = forms.UserForm
    return render(request, 'login/login.html',locals())


def register(request):
    if request.session.get('is_login',None):
        #思考一下,登录状态下注册,肯定不可行
       return  redirect('index/')
    if request.method == "POST":
        register_form = forms.RegisterForm(request.POST)
        message = "请检查填写的内容"
        if register_form.is_valid():
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            sex = register_form.cleaned_data['sex']
            if password1 != password2:
                message = "两次输入的密码不同"
                return  render(request,'login/register.html',locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用户已经存在,请重新选择用户名'
                    return render(request,'login/register.html',locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '该邮箱地址已经被注册,请使用别的邮箱'
                    return render(request,'login/register.html',locals())
                #当然在上述都没错误的话,我们开始使用我们的东西了
                new_user = models.User()#对于数据库里面表进行实例化,方便使用
                new_user.name = username
                new_user.password =hash_code(password1)
                new_user.email = email
                new_user.sex = sex
                new_user.save()#进行保存


                code = make_confirm_string(new_user)
                send_email(email,code)
                return render(request,'login/confirm.html',locals())#跳转到等待邮件确认页面
    register_form = forms.RegisterForm()
    return render(request,'login/register.html',locals())


def logout(request):
    if not request.session.get('is_login',None):
        #如果本来就没有登录,也就没有等出一说
        return redirect("/index/")
    request.session.flush()
    # 或者使用下面的方法
    # del request.session['is_login']
    # del request.session['user_id']
    # del request.session['user_name']
    return redirect("/index/")
  • url里面代码:
from django.contrib import admin
from django.urls import path
from login import views
from django.conf.urls import include
from django.urls import re_path
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index, name='index'),
    path('login/', views.login, name='login'),
    # path('',views.base,name ='base'),
    path('', views.index),
    path('register/', views.register, name='register'),
    path('logout/', views.logout, name='logout'),
    re_path(r'^captcha/', include('captcha.urls'), name='captchas'),  # 添加captcha相对应的网址
    re_path(r'^confirm/$', views.user_confirm, name='cofirms')
]

结课:

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值