实战01 login
实战01 login
一,首先进行
- 项目搭建, 
 
1 2 3 4 5 6 7 8
   | mkvirtualenv -p python3 py3_django_bj  
 
  pip3 install django
  mkdir test_django
 
 
   | 
 
- 项目创建
 
1 2 3 4 5
   | django-admin startproject test_django . 
  python manage.py startapp app  
 
 
   | 
 
设置 时区
在项目的settings文件中,如下所示
 


设计数据模型
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | from django.db import models
 
  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)
      def __str__(self):         return self.name
      class Meta:         ordering = ["-c_time"]         verbose_name = "用户"         verbose_name_plural = "用户"
   | 
 
设置数据库后端
默认是sqlite数据库, 
 
1 2 3 4 5 6 7 8 9
   | 
 
  DATABASES = {     'default': {         'ENGINE': 'django.db.backends.sqlite3',         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),     } }
 
  | 
 
MySQL的配置如下
1 2 3 4 5 6 7 8 9 10
   | DATABASES = {     'default': {         'ENGINE': 'django.db.backends.mysql',            'NAME': 'mysite',           'USER': 'root',              'PASSWORD': '****',           'HOST': '192.168.1.123',           'PORT': '3306',              } }
  | 
 
- 注册app
 

创建记录和数据表
进入pycharm 的terminal终端, 执行以下命令
1
   | python manage.py makemigrations
   | 
 
 

 Django自动为我们创建了app\migrations\0001_initial.py文件,保存了我们的第一次数据迁移工作,也就是创建了User模型。 
接着执行下面的命令:
1
   | python manage.py migrate
   | 
 
二. admin后台
在我们开发的初期,没有真实的用户数据,也没有完整的测试环境,为了测试和开发的方便,通常我们会频繁地使用Django给我们提供的Admin后台管理界面,创建测试用例,观察模型效果等等
因为admin 已经注册了, 所以无需再次注册,
 
进入app/admin.py 文件, 代码如下
1 2 3 4 5 6 7
   | from django.contrib import admin
 
 
  from . import models
  admin.site.register(models.User)
   | 
 
暂时简单点, 直接注册就好了
 
创建超级管理员
Django的admin后台拥有完整的较为安全的用户认证和授权机制,防护等级还算可以。
要进入该后台,需要创建超级管理员,该管理员和我们先前创建的User用户不是一个概念,要注意区别对待。
同样在Pycharm的终端中,执行下面的命令:
1
   | python manage.py createsuperuser
   | 
 

三,使用Admin后台
创建好超级管理员后,就可以启动我们的开发服务器了,然后在浏览器中访问http://127.0.0.1:8000/admin/地址,可以看到如下的登录界面:   


注意,图中下方的认证和授权是admin应用自身的账户管理,上面的app栏目才是我们创建的login应用所对应的User模型。
点击app栏目中的用户链接,进入用户列表界面,发现是空的,因为我们当前没有任何用户。点击右上方的增加用户按钮,我们创建几个测试用户试试:

 但是,实际上这里还有点小问题,那就是密码相关。密码不能保存为明文,这个问题我们后面再解决;其次,不可以这么随意的修改和设置密码,为了展示的方便性,我们先这样 
添加了3个用户

 admin的使用和配置博大精深,但在本实战项目里,我们暂时把它当做一个数据库管理后台使用 
url路由和视图
 前面我们已经创建好数据模型了,并且在admin后台中添加了一些测试用户。下面我们就要设计好站点的url路由、对应的处理视图函数以及使用的前端模板了。 
路由设计
我们初步设想需要下面的四个URL:
| URL | 
视图 | 
模板 | 
说明 | 
| /index/ | 
login.views.index | 
index.html | 
主页 | 
| /login/ | 
login.views.login | 
login.html | 
登录 | 
| /register/ | 
login.views.register | 
register.html | 
注册 | 
| /logout/ | 
login.views.logout | 
无需专门的页面 | 
退出 | 
重要说明:由于本项目目的是打造一个针对管理系统、应用程序等需求下的可重用的登录/注册app,而不是门户网站、免费博客等无需登录即可访问的网站,所以在url路由、跳转策略和文件结构的设计上都是尽量自成体系。具体访问的策略如下
- 未登录人员,不论是访问index还是login和logout,全部跳转到login界面
 
- 已登录人员,访问login会自动跳转到index页面
 
- 已登录人员,不允许直接访问register页面,需先logout
 
- 登出后,自动跳转到login界面
 
考虑到登录注册系统属于站点的一级功能,为了直观和更易于接受,这里没有采用二级路由的方式,而是在根路由下直接编写路由条目,同样也没有使用反向解析名(name参数)。所以,在重用本app的时候,一定要按照app使用说明,加入相应的url路由。
 

四,编写 视图函数

现在我们的工作目录

访问主页

1.login 页面编写

2. 引入Bootstrap 4
Bootstrap4就是最好的CSS框架之一!
想要在HTML页面中使用Bootstrap4,最方便的方法就是使用国内外的免费CDN(如果app的使用环境不可以使用外部网络,也可以使用内部的CDN,或者静态文件)。
这里推荐BootCDN:https://www.bootcdn.cn/,速度比较快,有大量的不同版本的CDN。

jQuery


五,静态文件的引入
 继续在/login/static/login目录下创建一个css和一个image目录,css中添加我们为登录视图写的css文件,这里是login.css,image目录中,拷贝进来你想要的背景图片,这里是bg.jpg。最终目录结构如下: 

 主要是引入了login.css文件,注意最开头的{% load static %},表示我们要加载静态文件。 

登录视图
1 2 3 4 5 6 7
   | 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')
   | 
 
说明:
每个视图函数都至少接收一个参数,并且是第一位置参数,该参数封装了当前请求的所有数据;
 
通常将第一参数命名为request,当然也可以是别的;
 
request.method中封装了数据请求的方法,如果是“POST”(全大写),将执行if语句的内容,如果不是,直接返回最后的render()结果,也就是正常的登录页面;
 
request.POST封装了所有POST请求中的数据,这是一个字典类型,可以通过get方法获取具体的值。
 
类似get('username')中的键‘username’是HTML模板中表单的input元素里‘name’属性定义的值。所以在编写form表单的时候一定不能忘记添加name属性。
 
利用print函数在开发环境中验证数据;
 
利用redirect方法,将页面重定向到index页。
启动服务器,然后在http://127.0.0.1:8000/login/的表单中随便填入用户名和密码,然后点击提交。然而,页面却出现了错误提示,如下图所示: 
 

 解决这个问题的办法其实在Django的Debug错误页面已经给出了,我们需要在前端页面的form表单内添加一个{% csrf_token %}标签: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | <form class="form-login" action="/login/" method="post">                  {% csrf_token %}                  <h3 class="text-center">欢迎登录</h3>                  <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>                  <div>                  <a href="/register/" class="text-success " ><ins>新用户注册</ins></a>                  <button type="submit" class="btn btn-primary float-right">登录</button>                  </div>                </form>
   | 
 
 数据验证
1 2 3 4 5 6 7 8 9
   | def login(request):     if request.method == "POST":         username = request.POST.get('username')         password = request.POST.get('password')         if username.strip() and password:                                                      return redirect('/index/')
   | 
 
- get方法是Python字典类型的内置方法,它能够保证在没有指定键的情况下,返回一个None,从而确保当数据请求中没有username或password键时不会抛出异常;
 
- 通过
if username and password:确保用户名和密码都不为空; 
- 通过strip方法,将用户名前后无效的空格剪除;
 
- 更多的数据验证需要根据实际情况增加,原则是以最低的信任度对待发送过来的数据。
 
账户密码 验证
 注意其中添加了一句from . import models,导入我们先前编写好的model模型。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | 
  from . import models
 
  def login(request):     if request.method == "POST":         username = request.POST.get('username')         password = request.POST.get('password')         if username.strip() and password:                                                      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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | message = '请检查填写的内容!'         if username.strip() and password:                                                    try:                 user = models.User.objects.get(name=username)             except :                 message = '用户不存在!'                 return render(request, 'login/login.html', {'message': message})
              if user.password == password:                 print(username, password)                 return redirect('/index/')             else:                 message = '密码不正确!'                 return render(request, 'login/login.html', {'message': message})         else:             return render(request, 'login/login.html', {'message': message})
   | 
 
 说明: 这里增加了message变量,用于保存提示信息。当有错误信息的时候,将错误信息打包成一个字典,然后作为第三个参数提供给render方法。这个数据字典在渲染模板的时候会传递到模板里供你调用。 
 为了在前端页面显示信息,还需要对login.html进行修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | <form class="form-login" action="/login/" method="post">                   {% if message %}                     <div class="alert alert-warning">{{ message }}</div>                   {% endif %}                   {% csrf_token %}                   <h3 class="text-center">欢迎登录</h3>                   <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>                   <div>                   <a href="/register/" class="text-success " ><ins>新用户注册</ins></a>                   <button type="submit" class="btn btn-primary float-right">登录</button>                   </div>                 </form>
   | 
 
 Django的模板语言{% if xxx %}{% endif %}非常类似Python的if语句,也可以添加{% else %}分句。例子中,通过判断message变量是否为空,也就是是否有错误提示信息,如果有,就显示出来!这里使用了Bootstrap的警示信息类alert,你也可以自定义CSS或者JS。 

Django表单
- 创建表单模型
 
在app/创建forms.py 文件进行 表单模型创建
1 2 3 4 5 6
   | 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)     
   | 
 
说明:
- 顶部要先导入forms模块
 
- 所有的表单类都要继承forms.Form类
 
- 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中``内的一个input元素。这一点和Django模型系统的设计非常相似。
 
- label参数用于设置``标签
 
max_length限制字段输入的最大长度。它同时起到两个作用,一是在浏览器页面限制用户输入不可超过字符数,二是在后端服务器验证用户输入的长度也不可超过。 
widget=forms.PasswordInput用于指定该字段在form表单里表现为``,也就是密码输入框。 
- 修改视图函数
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | def login(request):     if request.method == "POST":         login_form = forms.UserForm(request.POST)         message = '请检查填写的内容!'         if login_form.is_valid():                                                    username = login_form.cleaned_data.get('username')             password = login_form.cleaned_data.get('password')             try:                 user = models.User.objects.get(name=username)             except :                 message = '用户不存在!'                 return render(request, 'login/login.html', locals())
              if user.password == password:                 print(username, password)                 return redirect('/index/')             else:                 message = '密码不正确!'                 return render(request, 'login/login.html', locals())         else:             return render(request, 'login/login.html', locals())     login_form = forms.UserForm()     return render(request, 'login/login.html', locals())     
 
   | 
 
- 修改login页面
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
   | {% load static %} <!doctype html> <html lang="en">   <head>          <meta charset="utf-8">     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">               <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">     <link href="{% static 'login/css/login.css' %}" rel="stylesheet"/>     <title>登录</title>   </head>   <body>     <div class="container">             <div class="col">                 <form class="form-login" action="/login/" method="post">                   {% if message %}                     <div class="alert alert-warning">{{ message }}</div>                   {% endif %}                   {% csrf_token %}                   <h3 class="text-center">欢迎登录</h3>
                    {{ login_form }}
                    <div>                       <a href="/register/" class="text-success " ><ins>新用户注册</ins></a>                       <button type="submit" class="btn btn-primary float-right">登录</button>                   </div>                 </form>             </div>     </div> 
                {#    以下三者的引用顺序是固定的#}     <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>     <script src="https://cdn.bootcss.com/popper.js/1.15.0/umd/popper.js"></script>     <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
    </body> </html>
  | 
 
说明:
     一个{{ login_form }}就直接完成了表单内容的生成工作!login_form这个名称来自你在视图函数中生成的form实例的变量名! 
实际上除了通过{{ login_form }}简单地将表单渲染到HTML页面中了,还有下面几种方式:
{{ login_form.as_table }} 将表单渲染成一个表格元素,每个输入框作为一个标签 
{{ login_form.as_p }} 将表单的每个输入框包裹在一个标签内 
- ```` 将表单渲染成一个列表元素,每个输入框作为一个``标签