本文共 21691 字,大约阅读时间需要 72 分钟。
urlpattern = [ path('/register/', views.register), path('/login/', views.login), path('/logout/', views.logout), ]
class User: id, name, password, email, create_time, update_time last_time(最后一次登录的时间), gender, province
新建project->Django项目,选择虚拟环境:是个隔离的环境,与python无关
创建app
#Terminal命令行执行python manage.py startapp login
# 编辑文件 loginRegister/settings.py # LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-hans' # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Shanghai'
#Terminal命令行执行python manage.py makemigrationspython manage.py migrate # 将迁移脚本的内容写入数据库并创建数据库表 python manage.py createsuperuser # 创建后台登录的超级用户和密码
#Terminal命令行执行python manage.py runserver
方法2:配置Django Server
访问网址: http://127.0.0.1:8000/
访问网址: http://127.0.0.1:8000/admin/学习Git基本操作, 参考链接: https://www.liaoxuefeng.com/wiki/896043488029600
安装Git,并添加环境变量#Terminal命令行执行git init # 安装插件.ignore, 并生成python上传git项目需要忽略内容的文件.gitignore git add * # 添加修改到暂存区 git commit -m "搭建项目开发环境" # 将暂存区的代码提交到本地git仓库 $ git log # 查看历史提交记录
安装.ignore插件
作为一个用户登录和注册项目,需要保存的都是各种用户的相关信息。很显然,我们至少需要一张用户表User,在用户表里需要保存下面的信息:
用户名(name): 必填,最长不超过128个字符且唯一(unique)
密码(password): 必填,最长不超过256个字符
邮箱地址(email): 使用Django内置的邮箱类型且唯一
性别(gender): 性别, 使用choice,只能选择男或者女或者未知,默认为未知;
创建时间(create_time): 用户创建时间
注意点: auto_now_add=True时为添加时的时间,更新对象时不会有变动。修改时间(modify_time):用户最后一次修改时间
注意点: auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。最后一次登录时间(last_login_time): 最后一次登录时间
注意点:null=True的话,数据库中该字段是NULL,即允许空值 注意点:blank=False(默认)的话,字段没被赋值则会抛错;和数据验证(表单验证等)有 关# 编辑login/models.pyfrom django.db import models# Create your models here.class SiteUser(models.Model): """用户的数据库模型,注册/登录需要""" gender_choice=( (0,"未知"), (1,"男"), (2,"女"), ) name=models.CharField(max_length=128,unique=True,verbose_name="用户名")#unique=True用户唯一 password=models.CharField(max_length=256,verbose_name="密码") email=models.EmailField(unique=True,verbose_name="电子邮箱") gender=models.IntegerField(choices=gender_choice,default=0,verbose_name="性别") # auto_now_add=True时为添加时的时间,更新对象时不会有变动。 # auto_now=True无论是你添加还是修改对象,时间为你添加或者修改的时间。 create_time=models.DateTimeField(auto_now_add=True,verbose_name="创建时间") modify_time=models.DateTimeField(auto_now=True,verbose_name="最后一次修改时间") # null针对数据库层面的, blank针对表单的 last_login_time=models.DateTimeField(null=True,blank=True,verbose_name="最后一次登录时间") def __str__(self): return self.name class Meta: verbose_name="网站用户管理" verbose_name_plural=verbose_name
Django支持MySQL, Sqlite, oracle等数据库, 此处选择默认的sqlite,不做修改。
#Terminal命令行执行python manage.py makemigrationspython manage.py migrate # 将迁移脚本的内容写入数据库并创建数据库表
打开数据库文件db.sqlite3, 查看是否有数据库表login_siteuser,如果有,则操作成功。
from django.contrib import adminfrom login.models import SitrUser# Register your models here.# 后台管理设置信息class SiteUserAdmin(admin.ModelAdmin): list_display = ['name','gender','email'] list_display_links = ['name'] list_filter = ['gender','create_time'] list_per_page = 10admin.site.register(SitrUser,SiteUserAdmin)
浏览器访问,检测是否成功访问网址: http://127.0.0.1:8000/admin/
路由设计:
URL 视图views 模板 功能
/index/ login.views.index index.html 首页 /login/ login.views.login login.html 登录页面 /register/ login.views.register register.html 注册界面 /logout/ login.views.logout 无需返回页面 登出界面#主URL,loginRegister/urls.py:from django.contrib import adminfrom django.urls import path,includeurlpatterns = [ path('admin/', admin.site.urls), path('', include('login.urls'))]#子URL,login/urls.py(新建的文件)from django.urls import path, includefrom login import viewsurlpatterns = [ path('index/', views.index, name='index'), path('login/', views.login, name='login'), path('register/', views.register, name='register'), path('logout/', views.logout, name='logout'), ]
# login/views.pyfrom 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 # redirect: 重定向(跳转) return redirect('/login/')
首页 这是首页的模拟界面
用户登录 用户登录
注册界面 用户注册
测试是否成功
浏览器访问,检测是否成功?(第一步完美搞定) 访问网址: http://127.0.0.1:9999/index/ 访问网址: http://127.0.0.1:9999/login/ 访问网址: http://127.0.0.1:9999/register/templates/login/index.html
首页 这是首页的模拟界面
templates/login/login.html
用户登录 用户登录
登录失败! 用户密码错误!
templates/login/register.html
注册界面 用户注册
#编辑templates/login/login.html用户登录 用户登录
# 修改1. 有message信息则显示, 没有就不显示。 { % if message %}登录失败! { { message }}{ % endif %} # 修改2: 提交登录信息时, 以post方法提交给/login/对应的是视图函数处理。
#编辑login/views.pyfrom django.shortcuts import render,redirect# Create your views here.def index(request): pass return render(request, 'login/index.html')def login(request): if request.method == 'POST': username = request.POST.get('username').strip() password = request.POST.get('password').strip() # print(username, password) if username and password: user = SiteUser.objects.filter(name=username, password=password).first() if user: return redirect('/index/') else: message = "用户名或者密码错误" return render(request, 'login/login.html', { 'message':message}) else: message = "非法的数据信息" return render(request, 'login/login.html', { 'message': message}) return render(request, 'login/login.html')def register(request): pass return render(request, 'login/register.html')def logout(request): pass # redirect: 重定向(跳转) return redirect('/login/')
浏览器访问,检测是否成功?
访问网址: http://127.0.0.1:9999/login/ 填写正确的用户名和密码/错误的用户名和密码测试是否为期待的效果。# 编辑login/views.pydef login(request): if request.method == 'POST': username = request.POST.get('username').strip() password = request.POST.get('password').strip() print(username, password) if username and password: user = SiteUser.objects.filter(name=username, password=password).first() if user: # ------------核心修改的内容开始 request.session['is_login'] = True request.session['user_id'] = user.id request.session['username'] = user.name # --------------核心修改的内容结束 else: message = "用户名或者密码错误" return render(request, 'login/login.html', { 'message':message}) else: message = "非法的数据信息" return render(request, 'login/login.html', { 'message': message}) return render(request, 'login/login.html')
- 登出时,清空session信息
# 编辑login/views.pydef logout(request): # 如果状态不是登录状态,则无法登出。 if request.session.get('is_login'): request.session.flush() # 清空session信息 return redirect('/login/')
- 在首页添加登出的超链接并测试
你好, { { request.session.username }}, 这是首页的模拟界面
登出
为了防止机器人频繁登录网站或者破坏分子恶意登录,很多用户登录和注册系统都提供了图形验证码功能。在Django中实现图片验证码功能非常简单,有现成的第三方库可以使用,我们不必自己开发(不必重复造轮子)。这个库叫做django-simple-captcha。
官方文档:https://django-simple-captcha.readthedocs.io/en/latest/usage.html
#Terminal命令行执行pip install django-simple-captcha
#Terminal命令行执行python manage.py migrate
我们前面都是手工在HTML文件中编写表单form元素,然后在views.py的视图函数中接收表单中的用户数据,再编写验证代码进行验证,最后使用ORM进行数据库的增删改查。这样费时费力,整个过程比较复杂,而且有可能写得不太恰当,数据验证也比较麻烦。设想一下,如果我们的表单拥有几十上百个数据字段,有不同的数据特点,如果也使用手工的方式,其效率和正确性都将无法得到保障。有鉴于此,Django在内部集成了一个表单功能,以面向对象的方式,直接使用Python代码生成HTML表单代码,专门帮助我们快速处理表单相关的内容。
Django的表单给我们提供了下面三个主要功能: – 准备和重构数据用于页面渲染; – 为数据创建HTML表单元素; – 接收和处理用户从表单发送过来的数据。 – 编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表 中的一个 元素。官方文档:https://docs.djangoproject.com/zh-hans/3.1/topics/forms/
# /login/forms.py(新建的文件) from captcha.fields import CaptchaField from django import forms class LoginForm(forms.Form): username = forms.CharField(label='用户名', required=True, min_length=4, max_length=128) password = forms.CharField(label="密码", required=True, min_length=4, max_length=10) captcha = CaptchaField(label="验证码")
login(request): # 请求方法为POST提交 if request.method == 'POST': # 修改1: 实例化表单对象 login_form=LoginForm(request.POST) # 修改2: 验证表单数据的合法性 if login_form.is_valid(): # 修改3:获取表单填写的数据,数据清洗 username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') user = SiteUser.objects.filter(name=username, password=password).first() if user: request.session['is_login'] = True request.session['user_id'] = user.id request.session['username'] = user.name else: message = "用户名或者密码错误" # 修改4: locals()以字典方式返回当前所有的变量 , eg:{'message':'xxxx', 'login_form':'xxx'} return render(request, 'login/login.html',locals()) else: message = "填写的登录信息不合法" return render(request, 'login/login.html', locals()) # 请求方法是GET请求 login_form = LoginForm() return render(request, 'login/login.html',locals())
# 编辑templates/login/login.html用户登录 用户登录
# 修改1: 不同的报错,提示不同的信息 { % if login_form.captcha.errors %}登录失败! 验证码不正确{ % elif message %}登录失败! { { message }}{ % endif %}
# 编辑loginRegister/settings.py# mail configure(添加信息如下)EMAIL_HOST = 'smtp.163.com' # 'smtp.qq.com'EMAIL_PORT = 25EMAIL_HOST_USER = 'gfy17737466417@163.com' # 你的邮箱地址EMAIL_HOST_PASSWORD = 'PJIKJQNZJJWDOLTA' # 不是邮箱的登录密码,而是授权码(如何获 取授权码)EMAIL_USE_SSL = False # 不开启ssl
#Terminal输入命令python manage.py shellIn [1]: from django.core.mail import send_mailIn [2]: from loginRegister.settings import EMAIL_HOST_USERIn [3]: send_mail("测试邮件","content",EMAIL_HOST_USER,['gfy17737466417@163.com'])
#编辑login/forms.py,添加的部分class RegisterForm(forms.Form): username = forms.CharField(label="用户名", required=True, max_length=128) password1 = forms.CharField(label="密码", max_length=256, required=True) password2 = forms.CharField(label="确认密码", max_length=256, required=True) email = forms.EmailField(label="邮箱地址") captcha = CaptchaField(label='验证码')
– 如果用户已经登录,则不能注册跳转到首页。
– 如果是GET请求,返回用户注册的html页面。 – 如果是POST请求, 先验证提交的数据是否通过,清洗数据。 接下来判断用户名和邮箱是-- 否已经被 – 注册, 将注册的信息存储到数据库,跳转到登录界面。 – 额外功能: 为了数据的安全性注册时,密码存储到数据库不是明文存储,而是先加密再存储。# login/views.py更改部分def register(request): # 如果用户已经登录,则不能注册跳转到首页。 if request.session.get('is_login', None): return redirect('/index/') # 如果是POST请求 if request.method == 'POST': print(request.POST) register_form = RegisterForm(request.POST) message = "请检查填写的内容!" # 先验证提交的数据是否通过 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') print(locals()) # 接下来判断用户名和邮箱是否已经被注册 same_name_user = SiteUser.objects.filter(name=username) print(same_name_user) if same_name_user: message = '用户名已经存在' return render(request, 'login/register.html', locals()) same_email_user = SiteUser.objects.filter(email=email) if same_email_user: message = '该邮箱已经被注册了!' return render(request, 'login/register.html', locals()) # 将注册的信息存储到数据库,跳转到登录界面 new_user = SiteUser(name=username, password=password1, email=email) new_user.save() return redirect('/login/') # 如果是GET请求,返回用户注册的html页面。 register_form = RegisterForm() return render(request, 'login/register.html', locals())
# templates/login/register.html用户注册
{ % if register_form.captcha.errors %}注册失败! 验证码不正确{ % elif message %}注册失败! { { message }}{ % endif %}
对于如何加密密码,有很多不同的途径,其安全程度也高低不等。这里我们使用Python内置的hashlib库,使用哈希值的方式加密密码,可能安全等级不够高,但足够简单,方便使用。
#新建 login/utils.py 文件,编写一个hash函数:import hashlibdef hash_code(s, salt='mysite'):# 加点盐 h = hashlib.sha256() s += salt h.update(s.encode()) # update方法只接收bytes类型 return h.hexdigest()
#在 login/views.py 中修改login和register视图def register(request): new_user = SiteUser(name=username, password=hash_code(password1), email=email) def login(request): user = SiteUser.objects.filter(name=username, password=hash_code(password)).first()
很自然地,我们会想到如果能用邮件确认的方式对新注册用户进行审查,既安全又正式,也是目前很多站点的做法。
既然要区分通过和未通过邮件确认的用户,那么必须给用户添加一个是否进行过邮件确认的属性。另外,我们要创建一张新表,用于保存用户的确认码以及注册提交的时间
# /login/models.py class SiteUser(models.Model): # class SiteUser最后添加 has_confirmed = models.BooleanField(default=False, verbose_name="是否邮箱验证")# 最后添加class ConfirmString(models.Model): code = models.CharField(max_length=256, verbose_name="确认码") user = models.OneToOneField('SiteUser', on_delete=models.CASCADE) create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") def __str__(self): return self.user.name + ":" + self.code class Meta: ordering = ["-create_time"] verbose_name = "确认码" verbose_name_plural = "确认码"
数据库模型更改,一定要生成迁移脚本和写入数据库。
python manage.py makemigrations python manage.py migrate
修改一下admin.py文件,方便我们在后台修改和观察数据
#login/admin.pyfrom login.models import SiteUser,ConfirmStringadmin.site.register(ConfirmString)#添加
#login/view.pydef register(request): # ................ code = make_confirm_string(new_user) send_email(email, code) message = '请前往邮箱进行确认!' # ..................
#在login/utils.py 文件添加:import hashlibimport datetimefrom django.core.mail import send_mailfrom login.models import ConfirmStringfrom loginRegister import settingsdef hash_code(s, salt='mysite'):# 加点盐 h = hashlib.sha256() s += salt h.update(s.encode()) # update方法只接收bytes类型 return h.hexdigest()def make_confirm_string(user): now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") code = hash_code(user.name, now) ConfirmString.objects.create(code=code, user=user,) return codedef send_email(email, code): print('send mail.........') subject = '注册确认邮件' text_content = '''感谢注册,这里是登录注册系统网站!\ 如果你看到这条消息,说明你的邮箱服务器不提供HTML链接功能,请联系管理员!''' html_content = '''感谢注册点击验证,\ 这里是登录注册系统网站!
请点击站点链接完成注册确认!
此链接有效期为{}天!
'''.format('127.0.0.1:9999', code, settings.CONFIRM_DAYS) send_mail(subject, text_content, settings.EMAIL_HOST_USER, [email, ], html_message=html_content)
最后的有效期天数为设置在settings中的 CONFIRM_DAYS 。下面是邮件相关的settings配置:
#loginRegister/settings.py# 注册有效期天数 CONFIRM_DAYS = 3
在login子应用的 urls.py 中添加一条url:
#login/urls.pypath('confirm/', views.user_confirm,name='confirm'),
其次,在 login/views.py 中添加一个 user_confirm 视图:
– 获取确认码信息 – 数据库中是否有该确认码,如果没有, 返回说是无效的请求 – 数据库中是否有该确认码,如果有, 判断是否过期? 如果过期,删除用户信息,否则更新用户信息。#login/views.py最后添加def user_confirm(request): code = request.GET.get('code', None) message = '' try: confirm = ConfirmString.objects.get(code=code) except: message = '无效的确认请求!' return render(request, 'login/confirm.html', locals()) create_time = confirm.create_time now = datetime.now() print(now, create_time, create_time + timedelta(settings.CONFIRM_DAYS)) if now > create_time + timedelta(settings.CONFIRM_DAYS): confirm.user.delete() message = '您的邮件已经过期!请重新注册!' else: confirm.user.is_confirmed = True confirm.user.save() confirm.delete() message = '感谢确认,请使用账户登录!' return render(request, 'login/confirm.html', locals())
需要一个 confirm.html 页面,我们将它创建在 /login/templates/login/ 下面:
Title { { message }}
页面中通过JS代码,设置2秒后自动跳转到登录页面,可根据自己的需要去除或者美化
既然未进行邮件确认的用户不能登录,那么我们就必须修改登录规则,如下所示:
if not user.has_confirmed: message = '该用户还未经过邮件确认!' return render(request, 'login/login.html', locals())
去邮箱验证,然后登陆
转载地址:http://kkrlz.baihongyu.com/