在跟随视频学习的时候,发现视频后面的代码质量越来越差[x-bilibili id="BV1rT4y1v7uQ?p=79" autoplay="off" /]
现在我就来重新梳理一下视频中老师思路的代码和伟大的gpt4大人所给出的建议。


视频中对密码加盐的思路太蠢了,为什么要造这么多没有用的垃圾轮子?以下是使用了django内置库的代码加盐的结果。

from django.shortcuts import render, redirect
from app01 import models
from django import forms
from django.core.paginator import Paginator
from django.contrib.auth.hashers import make_password, check_password

class AdminForm(forms.ModelForm):
    # 需要对用户输入的密码进行二次确认
    password2 = forms.CharField(label='确认密码', widget=forms.PasswordInput)

    class Meta:
        model = models.Admin
        fields = '__all__'
        widgets = {
            'password': forms.PasswordInput(),
            'password2': forms.PasswordInput(),
        }

    def clean(self):
        cleaned_data = super().clean()
        password = cleaned_data.get('password')
        password2 = cleaned_data.get('password2')
        if password and password2 and password != password2:
            self.add_error('password2' , '两次输入的密码不一致')
        return cleaned_data

    def save(self, commit=True):
        admin = super().save(commit=False)
        admin.password = make_password(self.cleaned_data['password'])
        if commit:
            admin.save()
        return admin


def admin_add(request):
    if request.method == 'GET':
        form = AdminForm()
        return render(request, 'admin_add.html', {'form': form})

    form = AdminForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect('/admin/list/')
        # return render(request, 'admin_add.html', {'form': form, 'error': '添加成功'})验证密码是否一致
    else:
        return render(request, 'admin_add.html', {'form': form})

其中有几点是我认为对于初学者需要明确的地方:
1.在class AdminForm中写的那些函数是会自动执行定义在类中的全部流程。这包括字段的初始化、数据的验证以及数据的清洗,不用再去考虑该如何调用他。
2.代码中的clean函数:

  • cleaned_data = super().clean() 这一行代码调用了父类(ModelForm)clean方法。父类的clean方法会对每个字段进行基本的验证,并返回一个cleaned_data字典,其中包含了所有经过验证的字段数据。
  • 视频中老师的代码是这样的:
if password != password2:
    raise forms.ValidationError('两次输入的密码不一致')

这样做的结果是,使用raise forms.ValidationError会引发一个全局表单错误。这意味着错误信息不会被特定地关联到任何一个字段,而是会显示在表单的通用错误区域。

我的代码中对于密码校验的部分是这样处理的:

if password and password2 and password != password2:
    self.add_error('password2', "两次输入的密码不一致")

如果你想把错误信息直接关联到特定的字段(比如password2),你可以使用self.add_error('password2', '两次输入的密码不一致')。这种方式更加用户友好,因为它直接指出了错误发生的位置(即报错弹窗在确认密码处)。

  • 当表单被提交并且调用了is_valid()方法之后(if form.is_valid():),cleaned_data将包含用户提交的并通过验证的数据。然后使用form.save()即可自动处理将`cleaned_data中的数据映射到对应的模型字段上,并将其保存到数据库。

3.密码加密
首先需要导入Django提供的密码加密函数make_password

from django.contrib.auth.hashers import make_password

为了在Django中添加密码加密的功能,通常情况下我们会使用Django自带的密码处理系统。Django提供了强大的密码管理工具,如make_password函数,用于安全地加密密码。在你的AdminForm表单中,你可以在保存表单数据到数据库之前加密密码。

这里是你可以遵循的步骤来实现密码加密:

  1. 导入必要的模块
    首先,你需要导入Django提供的密码加密函数

    from django.contrib.auth.hashers import make_password
  2. 修改保存逻辑
    接下来,修改admin_add视图或者AdminFormsave方法来加密密码。这里有两种常见的方法来实现:

方法一:在视图中加密密码
在视图函数admin_add中,可以在保存Admin对象之前手动设置加密后的密码:

from django.contrib.auth.hashers import make_password

def admin_add(request):
    if request.method == 'POST':
        form = AdminForm(request.POST)
        if form.is_valid():
            admin = form.save(commit=False)
            # 加密密码并设置
            admin.password = make_password(form.cleaned_data['password'])
            admin.save()
            return redirect('/admin/list/')
        else:
            return render(request, 'admin_add.html', {'form': form})
    else:
        form = AdminForm()
        return render(request, 'admin_add.html', {'form': form})

在这个例子中,当表单验证通过时,我们使用make_password函数对用户输入的明文密码进行加密,并将加密后的密码存储到Admin对象的password字段。

方法二:在ModelFormsave方法中加密密码(目前个人推荐)
你也可以重写AdminFormsave方法,在保存数据到数据库之前加密密码:

class AdminForm(forms.ModelForm):
    # ... 其他代码

    def save(self, commit=True):
        admin = super().save(commit=False)
        admin.password = make_password(self.cleaned_data['password'])
        if commit:
            admin.save()
        return admin

在这个例子中,我们重写了AdminFormsave方法。在调用父类的save方法获取Admin对象后,我们对密码进行加密处理,然后根据commit参数决定是否立即保存对象。
提示:如何选择方法一还是方法二?下面是优劣对比

方法一:在视图中加密密码
优点:

视图控制:在视图中处理密码加密允许你在特定的视图逻辑中控制密码处理的流程。这对于不同视图有不同处理逻辑的应用来说非常有用。
灵活性:如果你需要在保存之前执行其他逻辑或条件判断,这种方法更灵活。
缺点:

重复代码:如果多个视图涉及密码处理,你可能需要在每个视图中重复相同的密码加密逻辑。
分散管理:将密码加密逻辑放在视图中可能使得业务逻辑分散,难以管理。

方法二:在ModelForm的save方法中加密密码
优点:

集中管理:将密码处理逻辑放在ModelForm中可以集中管理,使代码更整洁,维护更方便。
代码复用:如果有多个地方需要处理相同模型的保存操作,这种方法避免了重复代码。
缺点:

灵活性较低:如果在不同的视图中需要对密码处理有不同的逻辑,这种方法可能不够灵活。
隐藏逻辑:将逻辑隐藏在表单的保存方法中,可能会让代码的阅读和理解更加困难。
总结来说,如果你的应用中多个视图都涉及到相同的密码处理逻辑,那么选择方法二可能更好。如果你需要更多的灵活性或者在特定视图中有特殊的逻辑,那么方法一可能更适合。

4.拓展:django.contrib.auth.hashers中还有一个check_password函数,check_password 函数的基本用法是将用户输入的明文密码和数据库中存储的加密密码进行比较。如果两者匹配,函数返回True,否则返回False。