说明文中内容是参考here的系列教程,里面比较详细地说明了整个登录功能实现的流程。这里主要是自己整理一下,便于之后参考。
1. 项目目录
项目名为student_system
-student_system -student_system # 创建项目时自动生成的与项目名同名的目录 -__init__.py -settings.py -urls.py -wsgi.py -library # 通过命令行创建的应用 -migrations # 数据库相关文件,不用管。 -templates # 手动创建的目录,用于保存html等文件 -admin.py -apps.py -forms.py -models.py -tests.py -urls.py -views.py -forms.py -templates -login -index.html -login.html -logout.html -register.html -static -login -css -login.css -register.css -images -manage.py -db.sqlite3
2. Django配置
2.1. Django连接MySQL数据库
- 首先,在MySQL中创建名称为student_system的数据库
- 在项目下面的Settings.py文件中,找到DATABASES这一项,修改为下面的代码
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'student_system', #你的数据库名称,是第一步中创建的名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': 'localhost', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
- 接着,在当前项目的Python环境中安装pymysql, pip install pymysql
- 在项目名同名的目录下面的init.py文件中写入下面的语句。
import pymysql pymysql.install_as_MySQLdb()
Django默认导入的mysql驱动程序是MySQLdb,但是MySQLDB对Py3支持不全,所以这里使用pymysql。pymysql.install_as_MySQLdb()是将pymysql转化为MySQL。
此时,数据库就配置完成了
3. 实现登录功能
首先,创建一个应用library。
python manage.py startapp library
然后在settings.py文件中注册应用。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'library' # 应用名 ]
3.1. 设计模型
library/models.py
from django.db import models # Create your models here. class User(models.Model): gender = ( (1, '男'), (2, '女') ) name = models.CharField(max_length=128, unique=True) password = models.CharField(max_length=256) email = models.EmailField(unique=True) sex = models.IntegerField(choices=gender, default=1) c_time = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name class Meta: ordering = ['-c_time'] # 创建时间的逆序,也就是创建的越晚,排在上面 verbose_name = verbose_name_plural = '用户'
3.2. 创建数据库表
在项目目录的终端执行下面两行代码
python manage.py makemigrations python manage.py migrate
3.3. 搭建Admin后台
首先,在admin中注册。 library/admin.py
from django.contrib import admin from .models import User admin.site.register(User)
通过命令行创建超级管理员,输入以下命令,然后按照操作创建即可。
python manage.py createsuperuser
接着启动服务器 python manage.py runserver,然后在浏览器中输入 http://127.0.0.1:8000/admin/ ,使用刚创建的用户名和密码登录进去就可以了。
在后台增加数据的时候可以发现,因为email字段采用的是邮箱验证,因此不合标准的邮箱会提示。
3.4. 路由设计
为了路由结构的清晰性,在library目录下面增加一个urls.py文件,配置如下:
from django.urls import path from . import views app_name = 'library' urlpatterns = [ path('index/', views.index, name="index"), path('login/', views.index, name='login'), path('register/', views.register, name="register"), path('logout/', views.logout, name="logout") ]
接着,在项目目录下面的urls.py文件中配置应用的urls.py
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('lib/', include('library.urls')) ]
3.5. 视图设计
首先,在liarary/views.py下面
from django.shortcuts import render, 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('/login/')
接着,在library应用下面创建templates文件夹,接着在templates下面创建login文件夹,接着在login下面创建上面提及的3个html文件。
截止到这里,一个整体的框架就基本完成了,接着就是实现登录、注册、登出的逻辑。
3.6. 开始登录的页面设计
login.html界面,这里没有使用使用原生态的HTML页面,因为所有前端的验证和安全机制都是不可信的,因此采用Django的Form构建表单。
3.6.1. form表单
首先,在library下面创建一个forms.py文件,然后在里面使用Python代码创建需要填写的元素:
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)
然后,在templates文件目录下面的login.html界面中写上如下代码
<form action="{% url 'library:login' %}" method="post"> {% csrf_token %} <h3>欢迎登录</h3> <div> {{ login_form.username.label_tag }} {{ login_form.username }} </div> <div> {{ login_form.password.label_tag }} {{ login_form.password }} </div> <div> <a href="{% url 'library:register' %}"><ins>新用户注册</ins></a> <button type="submit">登录</button> </div> </form>
3.6.2. 增加静态文件
首先,在应用library下面创建一个与templates同级的static目录,在这个目录下面创建login目录。接着在login目录下面创建一个css和images目录。创建完成之后目录结构为
-library -static -login -css -images -templates -login -index.html -login.html ......
在login下面的css目录下创建login.css, 并写入下面的代码:
body { height: 100%; background: aliceblue; }
结合,需要在login.html中引入这个css文件,文件代码如下:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="{% static 'login/css/login.css' %}"> <title>登录</title> </head> <body>
3.6.3. 登录路由实现
这里使用url解析的方式来赋值需要跳转的地址。在library下面的urls.py中的内容是
app_name = 'library' urlpatterns = [ path('', views.index, name="index"), path('index/', views.index, name="index"), path('login/', views.login, name='login'), path('register/', views.register, name="register"), path('logout/', views.logout, name="logout") ]
3.6.4. 登录界面逻辑实现
在下面的逻辑中增加了提示信息,这也是对应的需要在前端界面中增加的内容。
def login(request): if request.method == 'POST': login_form = UserForm(request.POST) if login_form.is_valid(): username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') try: user = User.objects.get(name=username) except: message = "用户不存在" return render(request, 'login/login.html', locals()) if user.password == password: return redirect(reverse('library:index')) else: message = "用户密码错误" return render(request, 'login/login.html', locals()) login_form = UserForm() return render(request, 'login/login.html', locals())
在前端显示中增加提示信息
<form action="{% url 'library:login' %}" method="post"> {% if message %} <div>{{ message }}</div> {% endif %} {% csrf_token %} <h3>欢迎登录</h3> ... </form>
至此,整个登录界面已经完成了。
3.6.5. 前端增加类名等信息
为了能够给指定的内容进行渲染,需要使用类名信息,可以在表单中设置widget属性设置类名。还可以设置一些聚焦等属性。
from django import forms class UserForm(forms.Form): username = forms.CharField(label='用户名', max_length=128, widget=forms.TextInput(attrs={'class':'form-control','placeholder':"Username", 'autofocus':''})) password = forms.CharField(label='密码', max_length=256, widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder':'Password'}))
然后在静态文件中就可以设置form-control类的样式。
4. session回话
参考here
4.1. 实现不允许重复登录
def login(request): if request.session.get('is_login', None): # 这里 print("已经登录...") return redirect(reverse('library:index')) if request.method == 'POST': login_form = UserForm(request.POST) if login_form.is_valid(): username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') try: user = User.objects.get(name=username) except: message = "用户不存在" return render(request, 'login/login.html', locals()) if user.password == password: request.session['is_login'] = True # 这里 request.session['user_id'] = user.id # 这里 request.session['user_name'] = user.name # 这里 return redirect(reverse('library:index')) else: message = "用户密码错误" return render(request, 'login/login.html', locals()) login_form = UserForm() return render(request, 'login/login.html', locals())
4.2. 退出系统
request.session.flush() # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。
def logout(request): if not request.session.get('is_login', None): # 如果没有登录的话 return redirect(reverse('library:login')) request.session.flush() return redirect(reverse('library:login'))
index.html中增加入口.
注意标签中使用url的这种写法。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% if request.session.user_name %} <h1>welcome, {{ request.session.user_name }}</h1> {% endif %} <p> <a href="{% url 'library:logout' %}">登出</a> </p> </body> </html>
4.3. 添加首页内容
首页进行判断,如果没有登录的时候,就跳转到登录页面。
def index(request): if not request.session.get('is_login', None): return redirect('library:login') return render(request, 'login/index.html')
5. 注册功能
5.1. 注册表单
使用Django的Form创建表单。
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) # 下拉框。
5.2. HTML中嵌入注册表单
register.html页面内容如下
{% load static %} </html> <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <title>注册</title> </head> <body> <form action="{% url 'library:register' %}" method="post"> {% if message %} <div>{{ message }}</div> {% endif %} {% csrf_token %} <h3 >欢迎注册</h3> <div> {{ register_form.username.label_tag }} {{ register_form.username}} </div> <div> {{ register_form.password1.label_tag }} {{ register_form.password1 }} </div> <div> {{ register_form.password2.label_tag }} {{ register_form.password2 }} </div> <div> {{ register_form.email.label_tag }} {{ register_form.email }} </div> <div> {{ register_form.sex.label_tag }} {{ register_form.sex }} </div> <div> {{ register_form.captcha.label_tag }} {{ register_form.captcha }} </div> <div> <a href="{% url 'library:login' %}"><ins>直接登录</ins></a> <button type="submit">注册</button> </div> </form> </body> </html>
5.3. 视图中增加注册的逻辑
需要注意,首先验证两次输入的密码的一致性;接着判断用户名邮箱是否重复。需要注意,is_valid()方法的验证,虽然前端的输入框能够一定程度上验证准确率,但是在后端还要使用is_valid验证数据输入是否符合。
def register(request): if request.session.get('is_login', None): return redirect(reverse('library:index')) if request.method == 'POST': register_form = RegisterForm(request.POST) if register_form.is_valid(): # 当输入的数据格式都合法的时候 username = register_form.cleaned_data.get('username') password1 = register_form.cleaned_data.get('password1') password2 = register_form.cleaned_data.get('password2') email = register_form.cleaned_data.get('email') sex = register_form.cleaned_data.get('sex') # print(username, password1, password2, email, sex) if password1 != password2: message = "两次输入的密码不一致" return render(request, 'login/register.html', locals()) same_name_user = User.objects.filter(name=username) if same_name_user: message = "用户名已存在" return render(request, 'login/register.html', locals()) same_email_user = User.objects.filter(email=email) if same_email_user: message = "邮箱已经注册" return render(request, 'login/register.html', locals()) new_user = User() new_user.name = username new_user.password = password1 new_user.email = email new_user.sex = sex new_user.save() return redirect(reverse('library:login')) else: # 当输入的数据存在不合法的时候。 # 比如输入ddd@ddd这个作为邮箱,此时前端的控件不会报错, # 后台在验证的时候会发现这个数据不符合,因此会运行这里的代码 return render(request, 'login/register.html', locals()) register_form = RegisterForm() return render(request, 'login/register.html', locals())
6. 密码加密
使用python的hashlib模块,在views.py文件中增加下列代码.
def hash_code(s, salt='library'): h = hashlib.sha256() s += salt h.update(s.encode()) return h.hexdigest() # 在注册方面里面,需要保存的是hash之后的密码 new_user = User() new_user.name = username new_user.password = hash_code(password1) # 在登录的时候,需要将密码hash之后和数据库对比 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(reverse('library:index'))
登录、注册功能到这里暂时告一段落
7. 该功能所有代码
7.1. views.py
from django.shortcuts import render, redirect # Create your views here. from django.urls import reverse from .models import User, Book from .forms import UserForm, RegisterForm import hashlib def hash_code(s, salt='library'): h = hashlib.sha256() s += salt h.update(s.encode()) return h.hexdigest() def index(request): if not request.session.get('is_login', None): return redirect('library:login') return render(request, 'login/index.html') def login(request): if request.session.get('is_login', None): return redirect(reverse('library:index')) if request.method == 'POST': login_form = UserForm(request.POST) if login_form.is_valid(): username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') try: user = User.objects.get(name=username) except: message = "用户不存在" return render(request, 'login/login.html', locals()) 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(reverse('library:index')) else: message = "用户密码错误" return render(request, 'login/login.html', locals()) login_form = UserForm() return render(request, 'login/login.html', locals()) def register(request): if request.session.get('is_login', None): return redirect(reverse('library:index')) if request.method == 'POST': register_form = RegisterForm(request.POST) if register_form.is_valid(): # 当输入的数据格式都合法的时候 username = register_form.cleaned_data.get('username') password1 = register_form.cleaned_data.get('password1') password2 = register_form.cleaned_data.get('password2') email = register_form.cleaned_data.get('email') sex = register_form.cleaned_data.get('sex') # print(username, password1, password2, email, sex) if password1 != password2: message = "两次输入的密码不一致" return render(request, 'login/register.html', locals()) same_name_user = User.objects.filter(name=username) if same_name_user: message = "用户名已存在" return render(request, 'login/register.html', locals()) same_email_user = User.objects.filter(email=email) if same_email_user: message = "邮箱已经注册" return render(request, 'login/register.html', locals()) new_user = User() new_user.name = username new_user.password = hash_code(password1) new_user.email = email new_user.sex = sex new_user.save() return redirect(reverse('library:login')) else: # 当输入的数据存在不合法的时候。 # 比如输入ddd@ddd这个作为邮箱,此时前端的控件不会报错, # 后台在验证的时候会发现这个数据不符合,因此会运行这里的代码 return render(request, 'login/register.html', locals()) register_form = RegisterForm() return render(request, 'login/register.html', locals()) def logout(request): if not request.session.get('is_login', None): # 如果没有登录的话 return redirect(reverse('library:login')) request.session.flush() return redirect(reverse('library:login'))
7.2. urls.py
from django.urls import path from . import views app_name = 'library' urlpatterns = [ path('', views.index, name="index"), path('index/', views.index, name="index"), path('login/', views.login, name='login'), path('register/', views.register, name="register"), path('logout/', views.logout, name="logout") ]
7.3. 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=1) c_time = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name class Meta: ordering = ['-c_time'] # 创建时间的逆序,也就是创建的越晚,排在上面 verbose_name = verbose_name_plural = '用户'