开发环境:
Microsoft Windows 10 Enterprise LTSC [Version 10.0.19044.1586], locale zh-CN
Python 3.8.10
PyCharm 2021.2 (Professional Edition)
Visual Studio Code, 64-bit edition (version 1.67.2)
修改网站的导航栏 打开 templates/nav.html
文件, 编辑其内容如下:
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 <nav class ="navbar navbar-expand-sm bg-light mb-4" > <div class ="container d-flex w-100" > <h2 > 我的网站</h2 > <div class ="flex-grow-1" > </div > <ul class ="navbar-nav" > <li class ="nav-item" > <a class ="nav-link" href ="/" > 首页</a > </li > <li class ="nav-item" > <a class ="nav-link" href ="/department/" > 组织构架管理</a > </li > <li class ="nav-item" > <div class ="nav-link" href ="#" > {% if request.user.is_authenticated %} 当前用户: {{ request.user.username }} {% endif %} </div > </li > <li class ="nav-item" > <a class ="nav-link" href ="/logout/" > 注销 </a > </li > <li class ="nav-item" > <a class ="nav-link" href ="/login/" > 登录</a > </li > <li class ="nav-item" > <a class ="nav-link" href ="/signup/" > 注册</a > </li > </ul > </div > </nav >
扩展用户模型 Djano
已经内置有用户管理的模型, 但是其原有字段可能不适合于当前的应用, 所以对用户模型进行扩展。
添加应用 users
打开 PowerShell窗口
, 激活虚拟环境, 创建名为 uers
的应用
1 2 venv\Scripts\activate python manage.py startapp users
打开 users/models.py
文件, 添加
以下代码:
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 43 44 45 46 47 from django.db import modelsfrom django.contrib.auth.models import AbstractUserfrom department.models import Departmentclass UserProfile (AbstractUser ): """ 用户: makemigration提示错误:sers.UserProfile.user_permissions: (fields.E304), 需要在settings中指定自定义认证模型:AUTH_USER_MODEL = 'users.UserProfile' """ name = models.CharField( max_length=20 , default='' , verbose_name='姓名' ) gender = models.CharField( max_length=10 , choices=(('male' , '男' ), ('female' , '女' )), default='male' , verbose_name='性别' ) mobile = models.CharField( max_length=11 , default='' , verbose_name='电话' ) department = models.ForeignKey( Department, blank=True , null=True , verbose_name='所属部门' , on_delete=models.CASCADE) position = models.CharField( max_length=50 , blank=True , null=True , verbose_name='职位' ) enrollment_date = models.DateField( blank=True , null=True , verbose_name='入职日期' ) class Meta : verbose_name = '用户信息' verbose_name_plural = verbose_name ordering = ['id' ] def __str__ (self ): return self .name
AbstractUser
: Django
内置的一个完整用户模型,包含字段,作为一个抽象类,以便您可以继承它并添加您自己的配置文件字段和方法。
AbstractBaseUser
: 仅包含身份验证功能,但不包含实际字段:当您继承子类时,您必须提供它们
修改项目settings.py 打开 myproject/settings.py
, 添加自定义的用户模型
1 2 3 4 5 6 7 8 9 10 11 12 13 INSTALLED_APPS = [ 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'department' , 'users' , ] AUTH_USER_MODEL = 'users.UserProfile'
如下图所示
迁移模型 在 manage.py
所在文件夹打开 PowerShell窗口
, 激活虚拟环境, 执行以下两个命令:
1 2 3 python manage.py makemigrations python manage.py migrate
迁移的时候有可能会出现类似 ValueError: Related model 'departments.structure' cannot be resolved
的错误。
解决办法:1. 删除 各个应用
的 migrations
文件夹除 __init__.py以外的文件
, 重新执行以上两个迁移的命令;
如果还不行,则删除数据库, 然后重建同名数据库, 重新执行以上两个迁移的命令;
重建数据后, 要重新创建超级用户
(如果有需要) 及添加数据
删除 迁移相应的文件
删除数据库文件
命令执行过程如下:
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 (venv) PS D:\PycharmProjects\djangoProject> python manage.py makemigrations No changes detected (venv) PS D:\PycharmProjects\djangoProject> python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, department, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0001_initial... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying department.0001_initial... OK Applying sessions.0001_initial... OK
基于类的视图 添加用户相关的视图文件
在 templates
文件夹中新建名为 users
(与新添加的应用同名) 的文件夹
在 templates/users
文件夹新建用于 用户注册
和 用户登录
的网页文件:signup.html
和 login.html
待用
添加用户 注册、登录 的表单类 在 users
文件夹新建名为 forms.py
的 Python file
, 编辑其内容如下:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 from django import formsfrom django.core import validatorsfrom django.contrib.auth import get_user_modelfrom .models import UserProfileUser = get_user_model() class LoginForm (forms.Form): username = forms.CharField( required=True , widget=forms.TextInput(attrs={'class' : 'form-control' }), error_messages={"required" : "请填写用户名" }) password = forms.CharField( required=True , widget=forms.PasswordInput(attrs={'class' : 'form-control' }), error_messages={"required" : u"请填写密码" }) class UserUpdateForm (forms.ModelForm): class Meta : model = User fields = ['name' , 'gender' , 'username' , 'mobile' , 'email' , 'enrollment_date' , 'department' , 'position' , 'is_active' ] class UserCreateForm (forms.ModelForm): """ 创建用户表单,进行字段验证 """ username = forms.CharField( required=True , min_length=3 , widget=forms.TextInput(attrs={'class' : 'form-control' }), error_messages={"required" : "请填写用户名" , "min_length" : "密码长度最少6位数" , }) email = forms.CharField( required=True , validators=[validators.EmailValidator()], widget=forms.TextInput(attrs={'class' : 'form-control' }), error_messages={"required" : "请填写邮箱" , }) password = forms.CharField( required=True , min_length=6 , max_length=20 , widget=forms.PasswordInput(attrs={'class' : 'form-control' }), error_messages={"required" : u"密码不能为空" , "min_length" : "密码长度最少6位数" , }) class Meta : model = User fields = ['password' , 'username' , ]
编辑视图 打开 users/views.py
文件, 编辑其内容如下:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 from django.shortcuts import renderfrom django.http import HttpResponseRedirectfrom django.views.generic.base import Viewfrom django.contrib.auth import authenticate, login, logoutfrom .forms import LoginForm, UserCreateFormfrom .models import UserProfileclass SignupView (View ): """ 注册新用户 """ def get (self, request ): signup_form = UserCreateForm() return render(request, 'users/signup.html' , locals ()) def post (self, request ): signup_form = UserCreateForm(request.POST) if signup_form.is_valid(): cd = signup_form.cleaned_data new_user = UserProfile.objects.create_user(cd['username' ], cd['email' ], cd['password' ]) new_user.name = new_user.username new_user.save() login(request, new_user) return HttpResponseRedirect('/' ) else : error_msg = '注册失败' return render(request, 'users/signup.html' , locals ()) class LoginView (View ): """ 用户登录 """ def get (self, request ): login_form = LoginForm() return render(request, 'users/login.html' , locals ()) def post (self, request ): redirect_to = request.GET.get('next' , '/' ) login_form = LoginForm(request.POST) if login_form.is_valid(): cd = login_form.cleaned_data _username = cd['username' ] _password = cd['password' ] user = authenticate(username=_username, password=_password) if user is not None : if user.is_active: login(request, user) return HttpResponseRedirect(redirect_to) else : error_msg = '用户未激活' return render(request, 'users/login.html' , locals ()) else : error_msg = "用户名或密码错误!" return render(request, 'users/login.html' , locals ()) else : error_msg = "用户名和密码不能够为空!" return render(request, 'users/login.html' , locals ()) class LogoutView (View ): """ 退出登录 """ def get (self, request ): logout(request) return HttpResponseRedirect('/' )
添加路由 用户 注册
、登录
、注销
这几个操作不想添加有前缀的url, 所以直接将相关的路由添加到项目的 urls.py
文件中。打开 djangoProject/urls.py
文件, 编辑其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from django.contrib import adminfrom django.urls import path, includeimport department.viewsfrom users.views import LoginView, LogoutView, SignupView urlpatterns = [ path('admin/' , admin.site.urls), path('department/' , include('department.urls' )), path('' , department.views.home), path('login/' , LoginView.as_view(), name='login' ), path('logout/' , LogoutView.as_view(), name='logout' ), path('signup/' , SignupView.as_view(), name='signup' ), ]
注册和登录的模板 编辑用于用户注册
和登录
的网页文件
注册用户的模板 打开 templates/users/signup.html
文件, 编辑其内容如下:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 {% extends "layout.html" %} {% block title %}用户注册{% endblock %} {% block content %} <div class ="d-flex justify-content-center align-items-center vh-100 bg-secondary" > <div class ="card" > <h3 class ="text-center p-3" > 注册新用户</h3 > <form method ="POST" class ="bg-white login_form" > {% csrf_token %} <div class ="text-danger text-center" > {{ error_msg }}</div > <div class ="row align-items-center g-3 m-3" > <div class ="col-3 d-flex justify-content-end" > <label for ="username" class ="col-form-label" > 用户名</label > </div > <div class ="col-9" > {{ signup_form.username }} </div > </div > <div class ="row align-items-center g-3 m-3" > <div class ="col-3 d-flex justify-content-end" > <label for ="email" class ="col-form-label" > 电子邮箱</label > </div > <div class ="col-9" > {{ signup_form.email }} </div > </div > <div class ="row align-items-center g-3 m-3" > <div class ="col-3 d-flex justify-content-end" > <label for ="password" class ="col-form-label" > 密码</label > </div > <div class ="col-9" > {{ signup_form.password }} </div > </div > <div class ="row align-items-center g-3 m-3" > <button type ="submit" class ="btn btn-primary block" > 注册新用户</button > </div > <div class ="text-end g-3 m-3" > <a class ="text-decoration-none mx-3 text-success" href ="/" > 返回首页</a > <a class ="text-decoration-none" href ="/login/" > 使用已有账号登录</a > </div > </form > </div > </div > {% endblock %} {% block style %} <style > .login_form { width : 480px ; } </style > {% endblock %}
注册新用户的界面
用户登录的模板 打开 templates/users/login.html
, 编辑其内容如下:
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 43 44 45 46 47 48 49 {% extends "layout.html" %} {% block title %}用户登录{% endblock %} {% block content %} <div class ="d-flex justify-content-center align-items-center h-100 bg-secondary" > <div class ="card" > <h3 class ="text-center p-3" > 用户登录</h3 > <form method ="POST" class ="bg-white login_form" > {% csrf_token %} <div class ="text-danger text-center" > {{ error_msg }}</div > <div class ="row align-items-center g-3 m-3" > <div class ="col-3 d-flex justify-content-end" > <label for ="username" class ="col-form-label" > 用户名</label > </div > <div class ="col-9" > {{ login_form.username }} </div > </div > <div class ="row align-items-center g-3 m-3" > <div class ="col-3 d-flex justify-content-end" > <label for ="password" class ="col-form-label" > 密码</label > </div > <div class ="col-9" > {{ login_form.password }} </div > </div > <div class ="row align-items-center g-3 m-3" > <button type ="submit" class ="btn btn-primary block" > 登录</button > </div > <div class ="text-end g-3 m-3" > <a class ="text-decoration-none mx-3 text-success" href ="/" > 返回首页</a > <a class ="text-decoration-none" href ="/signup/" > 没有账号</a > </div > </form > </div > </div > {% endblock %} {% block style %} <style > .login_form { width : 480px ; } </style > {% endblock %}
用户登录的界面
启用登陆验证 Django
在做后台系统过程中,我们通常都会为 视图函数
添加 @login_required
装饰器,这个装饰器的主要作用就是在用户访问这个方法时,检查用户是否已经成功登陆,如果没有则重定向
到登陆页面。
打开 department/views.py
文件, 编辑部分内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from django.contrib.auth.decorators import login_required @login_required(login_url='/login/' ) def create (request ): """ 省略部分代码 """ @login_required(login_url='/login/' ) def update (request, dep_id ): """ 省略部分代码 """ @login_required(login_url='/login/' ) def delete (request, dep_id ): """ 省略部分代码 """
在函数上方添加的 @ 开头的对象为装饰器,作用是给函数增加额外的功能
@login_required(login_url=’/login/‘): 必须登录后才能访问的装饰器
login_url=’/login/‘: 当未登录用户访问该函数时自动跳转到的登录url
参考资料:
Django v4.0 中文文档
Django入门与实践教程
Bootstrap5 中文手册
===END===