说明文中内容是参考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.sqlite32. 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 = '用户' 
京公网安备 11010502036488号