实战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.cs![17](/17.png)![17](/17.png)s
,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 }}
将表单的每个输入框包裹在一个标签内
- ```` 将表单渲染成一个列表元素,每个输入框作为一个``标签