django登录、登出和使用限制
用户登录、登出、登录限制
1、在app01/models.py里面定义User模型,通过AbstractBaseUser扩展用户模型
from django.db import models from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin from django.contrib.auth import get_user_model class UserManager(BaseUserManager): def _create_user(self , telephone, username, password, **kwargs): if not telephone: raise ValueError("必须要传递手机号码!") if not password: raise ValueError("必须要传递密码") user = self.model( telephone = telephone, username= username , **kwargs) user.set_password( password ) user.save() return user def create_user(self, telephone, username, password, **kwargs): kwargs['is_superuser'] = False return self._create_user( telephone = telephone, username=username, password = password, **kwargs ) def create_superuser(self, telephone, username, password, **kwargs): kwargs['is_superuser'] = True return self._create_user( telephone = telephone, username=username, password = password, **kwargs ) class User(AbstractBaseUser, PermissionsMixin): telephone = models.CharField(max_length=11, unique=True) email = models.CharField(max_length=100, unique=True) username = models.CharField(max_length=100) is_active = models.BooleanField(default=True) USERNAME_FIELD = "telephone" #USERNAME_FIELD作用,是执行authenticate验证, username参数传入后,实际校验的是telephone字段 REQUIRED_FIELDS = [] objects = UserManager() def get_full_name(self): return self.username def get_short_name(self): return self.username class Article(models.Model): title = models.CharField(max_length=100) content = models.TextField() # author = models.ForeignKey( User, on_delete= models.CASCADE ) #get_user_model()会自动获取settings.py里面 AUTH_USER_MODEL,这样不管你定义的那个User,都可以自动获取,更安全 author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
2、在settings.py设置用户模型用哪个
AUTH_USER_MODEL = "app01.User"
3、在 app01/forms.py里面定义表达,用于验证提交的数据
错误说明: from django import forms from django.contrib.auth import get_user_model class LoginForm(forms.ModelForm): remember = forms.IntegerField( required= False) class Meta: model = get_user_model() fields = ['telephone', 'password'] #将telephone写在fields当中,因为telephone在User模型有Unique唯一约束,所以每次提交form表单,from会对有唯一性约束的模型字段自动进行校验 #如果检测到数据库已经有对应的数据,就代表提交重复,会报下面错误: November 08, 2019 - 10:28:46 Django version 2.0.13, using settings 'untitled1108.settings' Starting development server at http://127.0.0.1:8080/ Quit the server with CTRL-BREAK. [08/Nov/2019 10:28:48] "GET /test/ HTTP/1.1" 200 30 [08/Nov/2019 10:28:57] "GET /login/ HTTP/1.1" 200 989 提交的数据为: <QueryDict: {'csrfmiddlewaretoken': ['uX31VMCh50kLLTeIrFbluEbCXcahchHnVzWHoCpG60A6ap5LeACKkaPmAT487RNn'], 'telephone': ['15555655555'], 'password': ['555555']}> <ul class="errorlist"><li>telephone<ul class="errorlist"><li>User with this Telephone already exists.</li></ul></li></ul> [08/Nov/2019 10:29:02] "POST /login/ HTTP/1.1" 302 0 [08/Nov/2019 10:29:02] "GET /login/ HTTP/1.1" 200 989
所以,正确的写法如下:
from django import forms from django.contrib.auth import get_user_model class LoginForm(forms.ModelForm): remember = forms.IntegerField( required= False) telephone = forms.CharField(max_length=11) class Meta: model = get_user_model() # fields = ['telephone', 'password'] fields = [ 'password',]
4、配置模板,用于登录
4.1在settings.py里面配置模板路径
默认的路径 DIRS是空的
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
app01/ templates配置如下, 而工程下的templates一般用于放公用模板base.html,共其他应用模板继承;或者,放一些公用的errors.html;
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ os.path.join(BASE_DIR, 'templates'), os.path.join(BASE_DIR, 'app01/templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
5、创建 app01/templates/login.html如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="POST"> <input type="hidden" name="csrfmiddlewaretoken" value="{{csrf_token}}"> <table> <tr> <td> 用户号码:</td> <td> <input type="text" name="telephone"> </td> </tr> <tr> <td> 密码:</td> <td> <input type="password" name="password"> </td> </tr> <tr> <td> <label for=""> 记住我 <input type="checkbox" name="remember" value="1"> </label> </td> </tr> <tr> <td></td> <td> <input type="submit" value="登录"> </td> </tr> </table> </form> </body> </html>
6、在视图里调用User模型,进行登录、登出、登录限制
from django.shortcuts import render, HttpResponse, reverse,redirect from django.db import connection from app01.models import User from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from app01.forms import LoginForm def test(request): #创建用户 # User.objects.create_user( telephone="15555655555", password="555555", username="zhiliao5" ) #用认证 user = authenticate(request, username="15555655555", password="555555") if user: print(user.username) print("验证成功!") else: print("验证失败!") return HttpResponse("继承AbstractUser扩展用户") def my_login(request): if request.method == "GET": return render(request, "login.html") else: print("提交的数据为:"); print(request.POST) form = LoginForm(request.POST) if form.is_valid(): telephone = form.cleaned_data.get("telephone") password = form.cleaned_data.get("password") remember = form.cleaned_data.get("remember") user = authenticate(request, username =telephone, password=password) if user and user.is_active: login(request, user) if remember: request.session.set_expiry(None) else: request.session.set_expiry(0) return HttpResponse("登录成功!") else: return HttpResponse("手机号码或者密码错误!") else: print(form.errors) return redirect( reverse("login") ) def my_logout(request): logout(request) return HttpResponse("成功退出") @login_required(login_url="/login/") def profile(request): return HttpResponse("这是个人中心,只有登录了以后才能查看到!")
7、在urls.py里面定义路由
from django.contrib import admin from django.urls import path from app01 import views as app01_views urlpatterns = [ path('admin/', admin.site.urls), path("test/", app01_views.test), path("login/", app01_views.my_login, name = "login"), path("logout/", app01_views.my_logout, name = "logout"), path("profile/", app01_views.profile, name="profile"), ]
8、登录,无 “记住我” 功能, 登录成功后,关闭浏览器,session就过期
9、登录,带 “记住我” 的功能, 登录成功后,session过期时间为两个星期
10、登出后, session的创建时间和过期时间相等,等于session无效
11、登录限制,目前直接访问http://127.0.0.1:8080/profile, 若是没有登录的状态,会跳转到登录的url
登录成功后没跳转到 /profile/界面,因为代码里面只是return HttpResponse, 因为没有告诉django跳转的路径
需要跳转的优化代码如下:
from django.shortcuts import render, HttpResponse, reverse,redirect from django.db import connection from app01.models import User from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from app01.forms import LoginForm def test(request): #创建用户 # User.objects.create_user( telephone="15555655555", password="555555", username="zhiliao5" ) #用认证 user = authenticate(request, username="15555655555", password="555555") if user: print(user.username) print("验证成功!") else: print("验证失败!") return HttpResponse("继承AbstractUser扩展用户") def my_login(request): if request.method == "GET": return render(request, "login.html") else: print("提交的数据为:"); print(request.POST) form = LoginForm(request.POST) if form.is_valid(): telephone = form.cleaned_data.get("telephone") password = form.cleaned_data.get("password") remember = form.cleaned_data.get("remember") user = authenticate(request, username =telephone, password=password) if user and user.is_active: login(request, user) if remember: request.session.set_expiry(None) else: request.session.set_expiry(0) #判断是否有next跳转的URL next_url = request.GET.get("next") if next_url: return redirect( next_url) return HttpResponse("登录成功!") else: return HttpResponse("手机号码或者密码错误!") else: print(form.errors) return redirect( reverse("login") ) def my_logout(request): logout(request) return HttpResponse("成功退出") @login_required(login_url="/login/") def profile(request): return HttpResponse("这是个人中心,只有登录了以后才能查看到!")
跳转以后的效果如下: