Django项目(三、用户登录与注册的逻辑)
Django响应页面请求的方式
页面直接响应
当我们直接使用一个html文件去响应某个请求时, 我们不需要在views.py编写视图函数, 而可以直接在urls.py中编写以下逻辑:
#urls.py
...
from django.views.generic import TemplateView
urlpatterns = [
...
url('$',TemplateView.as_view(template_name='index.html'),name='index')
]
在接下来编写登录逻辑的过程中, 会逐步介绍其它的响应方式。
视图函数编写登录逻辑
#views.py
from django.shortcuts import render
from django.contrib.auth import authenticate,login
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from .models import UserProfile
class CustomBackend(ModelBackend):
def authenticate(self,username=None,password=None,**kwargs):
try:
user = UserProfile.objects.get(Q(username=username)|Q(email=username))
if user.check_password(password):
return user
except Exception as e:
return None
def user_login(request):
if request.method == 'POST':
user_name = request.POST.get('username',"")
pass_word = request.POST.get('password',"")
user = authenticate(username=user_name,password=pass_word)
if user is not None:
login(request,user)
return render(request,"login.html")
else:
return render(request,'login.html',{"msg":"用户名或密码错误!"})
elif request.method == 'GET':
return render(request,'login.html',{})
#urls.py
...
from users.views import user_login
urlpatterns = [
...
url('^login/$',user_login,name='login')
]
views.py中用到了dango自带的一个用户验证方法authenticate, 如果验证成功, 该方法将返回一个user对象, 否则返回None。
authenticate方法默认验证是通过username和password进行验证, django允许我们使用自定义验证方式, 如上所示, 我们通过继承类ModelBackend来重写authenticate方法,用Q实现或逻辑,当然最后重写的类我们需要在settings.py中进行配置:
#settings.py
...
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
views.py中还用到了login, 这个方法将通过request记录下用户的地址, 通过session和cookies记录用户的状态。
前端通过{% request.user.is_authenticated %}判断用户是否验证。
基于类的方法编写登录逻辑
基于类编写响应逻辑的方式是Django最建议用户使用的:
#users/views.py
...
from django.views.generic.base import View
class LoginView(View):
def get(self,request):
return render(request, "login.html",{})
def post(self,request):
login_form = LoginForm(request.POST)
if login_form.is_valid():
user_name = request.POST.get('username',"")
pass_word = request.POST.get('password',"")
user = authenticate(username=user_name,password=pass_word)
if user is not None:
if user.is_active:
login(request,user)
return HttpResponseRedirect(reverse("index"))
else:
return render(request, 'login.html', {"msg": "账号未激活!"})
else:
return render(request,'login.html',{"msg":"用户名或密码错误!"})
else:
return render(request,'login.html',{"login_form":login_form})
#urls.py
...
from users.views import LoginView
urlpatterns = [
...
url('^login/$',LoginView.as_view(),name='login')
]
form表单的使用
Django网站的用户有时需要向服务端发送数据,例如POST方法,Django中有一个类form专门用于对用户传来的数据进行处理。
一般html文件中的<form>元素可以帮助用户传递表单数据:
#xxx.html
<form action="/form" method="post">
...
{% csrf_token %}
</form>
action="/form"表示这次POST请求将会与urls.py中的根路径下的form的URL进行绑定。
创建表单
由于表单对象与model结构相似,也是用到了类似于关系对象映射的方式。我们为了避免混淆,建议在mysite目录下,也即与models.py同级路径下创建一个文件forms.py用于编写表单:
#apps/users/forms.py
from django import forms
#登录用表单
#required=True表示这个字段必填, 如果用户未填就提交会报错
#form字段名称一定要和前端页面中<input>中的name一致, 才能实现数据校验
class LoginForm(forms.Form):
username = forms.CharField(required=True)
password = forms.CharField(required=True)
modelform方式创建表单
由于表单对象与model结构相似,我们也可以直接使用modelform继承models的字段结构生成表单:
#apps/users/forms.py
from django import forms
from users.models import Userprofile
...
class xxxForm(forms.ModelForm):
my_filed = forms.CharField()
class Meta:
#对应的model
model = Userprofile
#筛选对应的字段
fields = ['username','nickname']
注意, modelform对象加载数据后也可以使用save(commit=True)方法, 将加载的数据存入models对应数据库。
处理表单的视图函数
打开views.py,在里面编写登录验证视图逻辑。首先我们需要引入Django自带的用于登录验证的两个对象,authenticate与login,同时将刚刚创建的表单引入:
#apps/users/views.py
...
from .forms import LoginForm
class LoginView(View):
def get(self,request):
return render(request,'login.html',{})
def post(self,request):
login_form = LoginForm(request.POST)
#判断校验结果是否有错
if login_form.is_valid():
user_name = request.POST.get('username',"")
pass_word = request.POST.get('password',"")
user = authenticate(username=user_name,password=pass_word)
if user is not None:
login(request,user)
return render(request,"login.html")
else:
return render(request,"login.html",{"msg":"用户名或密码错误!"})
else:
#校验结果存储在login_form中传回给前端
return render(request,'login.html',{"login_form":login_form})
校验结果通过form对象传回给前端:
#template/login.html.py
<form>
#异常input效果
<div class= "form {% if login_form.errors.username %}errorput{% endif %}">
#显示错误日志
<div>{% for key,error in login_form.errors.items %}{{ key }}:{{ error }}{% endfor %}</div>
</form>
验证码在注册逻辑中的应用
首先,我们考虑实现django验证码的功能, 我们下载一个第三方插件:
pip3 install django-simple-captcha==0.5.5
在settings.py中配置:
#settings.py
...
INSTALLED_APPS = [
...
"captcha",
]
配置urls.py:
#urls.py
...
urlpatterns = [
...
url(r'^captcha/',include('captcha.urls')),
]
然后运行makemigrations和migrate, 至此便完成了图片验证码的所有配置。接下来我们来应用这个配置, 以注册的表单为例:
#apps/users/forms.py
from django import forms
from captcha.fields import CaptchaField
...
#注册用表单
#required=True表示这个字段必填, 如果用户未填就提交会报错
#form字段名称一定要和前端页面中<input>中的name一致, 才能实现数据校验
class RegisterForm(forms.Form):
email = forms.EmailField(required=True)
password = forms.CharField(required=True)
captcha = CaptchaField()
邮箱验证码
接下来, 我们以用户注册的邮箱激活验证码为例, 演示邮箱验证码的配置。首先在项目根目录新建文件apps/utils/email_send.py, 用于配置邮箱验证码:
#apps/utils/email_send.py,
from random import Random
from django.core.mail import send_mail
from users.models import EmailVerifyRecord
from blogs.settings import EMAIL_FROM
def random_str(randomlength=8):
str = ''
chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'
length = len(chars) - 1
random = Random()
for i in range(randomlength):
str += chars[random.randint(0,length)]
return str
def send_register_email(email,send_type="register"):
email_record = EmailVerifyRecord()
code = random_str(16)
email_record.code = code
email_record.email = email
email_record.send_type = send_type
email_record.save()
email_title = ""
email_boty = ""
if send_type == 'register':
email_title = "眼君数据网注册激活链接"
email_body = "请点击下面的链接激活你的账号:http://127.0.0.1:8000/active/{0}".format(code)
send_status = send_mail(email_title,email_body,EMAIL_FROM,[email])
if send_status:
pass
注意settings.py中也要配置邮箱的发送者:
#settings.py,
EMAIL_HOST = "smtp.qq.com"
EMAIL_PORT = 465
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = False
EMAIL_FROM = EMAIL_HOST_USER
在urls.py中的相应配置:
...
from users.views import LoginView,RegisterView,ActiveUserView
urlpatterns = [
...
url(r'^active/(?P<active_code>.*)/$',ActiveUserView.as_view(),name='user_active'),
]
在views.py中编写注册逻辑:
#apps/users/views.py
from django.shortcuts import render
from django.contrib.auth import authenticate,login,logout
from django.contrib.auth.hashers import make_password
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from .models import UserProfile,EmailVerifyRecord
from django.views.generic.base import View
from django.http import HttpResponse, HttpResponseRedirect
from .forms import LoginForm,RegisterForm,ForgetForm,ModifyPwdForm
from django.core.urlresolvers import reverse
from utils.email_send import send_register_email
# Create your views here.
...
class ActiveUserView(View):
def get(self,request,active_code):
all_records = EmailVerifyRecord.objects.filter(code=active_code)
if all_records:
for record in all_records:
email = record.email
user = UserProfile.objects.get(email=email)
user.is_active = True
user.save()
else:
return render(request, "active_fail.html")
return render(request, "login.html")
class RegisterView(View):
def get(self,request):
register_form = RegisterForm()
return render(request, "register.html",{'register_form':register_form})
def post(self,request):
register_form = RegisterForm(request.POST)
if register_form.is_valid():
user_name = request.POST.get('email',"")
if UserProfile.objects.filter(email=user_name):
return render(request, 'register.html', {"msg": "用户已存在!"})
pass_word = request.POST.get('password',"")
user_profile = UserProfile(username=user_name,email=user_name,password=make_password(pass_word),is_active=False)
user_profile.save()
send_register_email(user_name,"register")
return render(request,"login.html")
else:
return render(request,'register.html',{'register_form':register_form})