需求:实现django用户注册功能
问题分析:Django提供了一个后台管理系统,注册用户可以通过后台管理系统轻松实现。但我们需要实现的用户注册功能是针对于用户的,总不可能让用户登录后台系统自行添加自己的信息吧。Django中储存用户信息的数据表是auth_user,所以我们只需要将用户提供的表单信息保存进auth_user就实现了用户的自行注册。
首次尝试:
1、新增表单类,在form.py文件下填写一下代码:
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username","password","first_name","last_name","email"]
``User是auth_user表的模型
2、在view.py文件下编写表单处理逻辑,由于Django的用户密码是进行加密后再写入数据库的,在保存进数据库之前,必须对密码进行加密处理。调佣方法is_valid()后,FormModel对象会生成cleaned_data,起初以为调用save()方法是根据生成的cleaned_data进行保存,所以在save前修改cleaned_data达到目的
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password
from .forms import UserForm
def signup(request):
form = UserForm()
if request.method == "POST":
form = UserForm(request.POST)
if form.is_valid():
form.cleaned_data["password"] = make_password(form.cleaned_data.get("password"))
form.save()
return redirect('restaurant-list')
else:
return HttpResponse("字段无效")
context = {
"form": form,
}
return render(request, 'signup.html', context)
make_password()可以对字符串进行加密,返回加密后的字符串,
如果需要对加密方式进行替换,可在setting.py中进行配置,默认使用第一种加密方式:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
三、提交表单验证结果
前两条信息是通过admin后台添加的用户,可以发现是成功进行了加密的,但是通过用户端添加注册的用户密码并没有进行加密。所以可以判断我们的思路是错误的,在save前对cleaned_data进行修改并不能影响结果。因此开始单步调试。
四、调试
在此处发现cleaned_data的赋值方法,于是就想着在此处修改cleaned_data,看能否达到我们的目的。在之前的UserForm类(form.py)中加入clean_password方法,代码如下:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username","password","first_name","last_name","email"]
def clean_password(self):
value = make_password(self.data.get("password"))
return value
再次请求表单:
可以发现密码正常进行了加密后存入了数据库,此处又产生了一些一个疑问:为什么在_clean_fields()(即form.is_vaild)方法中修改cleaned_data后写入数据库的就是加密后的密码,而在其之后便不行呢?首先想到的是,在save中数据的依据肯定不是cleaned_data,这点是肯定的,那么只有可能是在form.is_vaild()中将cleaned_data的值赋值给了另一个变量,而save方法就是通过这个变量的值对数据库进行写入,所以form.is_vaild()结束后,我们再对cleaned_data进行修改不会影响到后面的结果。带着疑问和初步的分析,我们继续调试。
在此处可以发现model通过clean改变了属性的值,深入调试发下这里即是form.cleaned_data的值。由于ModelForm实例化时可以传入Instance参数(Model实例对象),进而于Model实例对象关联,所以我们可以猜测,FormModel的save方法是对instance(Model实例对象)的保存。接下来在save前修改form.instance,看是否能得到加密后的密码。代码如下:
def signup(request):
form = UserForm()
if request.method == "POST":
form = UserForm(request.POST)
if form.is_valid():
form.instance.password = make_password(form.instance.password)
#form.cleaned_data["password"] = make_password(form.cleaned_data.get("password"))
form.save()
return redirect('restaurant-list')
else:
return HttpResponse("字段无效")
context = {
"form": form,
}
提交表单查看结果
可以看到,密码进行了加密后写入了数据库,这正是我们想要的结果。
综上,我们有两种解决方法:
第一种:在UserForm中写clean_password方法,对password进行处理。
第二种:在save 前更改form.instance.password,对其进行加密处理。
总结:保存的本质的在于对form.instance的更改,个人比较倾向第一种方法,因为是根据源代码的逻辑写入的方法,与Django模块有比较好的融合度。第二种的话可能会出现一些问题,比如password通过了form.is_vaild的检查,但是后期自己自行进行处理后,得到的加密password可能会不符合passwordField的要求,赋值时可能就会报错。我们还可以在request.POST中直接对表单数据进行处理,但同样会出现的问题,所以这里不推荐。