forms组件及django中间件

forms组件数据校验

小需求:获取用户数据并发送给后端校验 后端返回不符合校验规则的提示信息

form组件
	1.自动校验数据
	2.自动生成标签
	3.自动展示信息

from django import forms


class MyForm(forms.Form):
    username = forms.CharField(min_length=3, max_length=8)  # username字段最少三个字符最大八个字符
    age = forms.IntegerField(min_value=0, max_value=200)  # 年龄最小0 最大200
    email = forms.EmailField()  # 必须符合邮箱格式

校验数据的功能(初识)
    form_obj = views.MyForm({'username':'jason','age':18,'email':'123'})
    form_obj.is_valid()  # 1.判断数据是否全部符合要求
    False  # 只要有一个不符合结果都是False
    form_obj.cleaned_data  # 2.获取符合校验条件的数据
    {'username': 'jason', 'age': 18}
    form_obj.errors  # 3.获取不符合校验规则的数据及原因
    {'email': ['Enter a valid email address.']}
1.只校验类中定义好的字段对应的数据 多传的根本不做任何操作
2.默认情况下类中定义好的字段都是必填的

forms组件渲染标签

<p>forms组件渲染标签的方式1(封装程度过高 扩展性差 主要用于本地测试):</p>
    {#    {{ form_obj.as_p }}#}
    {#    {{ form_obj.as_ul }}#}
    {#    {{ form_obj.as_table }}#}
<p>forms组件渲染标签的方式2(封装程度过低 扩展性高 编写麻烦)</p>
    {#    {{ form_obj.username.label }}#}
    {#    {{ form_obj.username }}#}
    {#    {{ form_obj.age.label }}#}
    {#    {{ form_obj.age }}#}
    {#    {{ form_obj.email.label }}#}
    {#    {{ form_obj.email }}#}
<p>forms组件渲染标签的方式3(封装程度较高 扩展性高 编写简单 推荐使用)</p>
    {#    {% for form in form_obj %}#}
    {#        <p>#}
    {#            {{ form.label }}#}
    {#            {{ form }}#}
    {#        </p>#}
    {#    {% endfor %}#}

注意事项
	forms组件之负责渲染获取用户数据的标签 也就意味着form标签与按钮都需要自己写
	前端的校验是弱不禁风的 最终都需要后端来校验 所以我们在使用forms组件的时候可以直接取消前端帮我们的校验<form action="" novalidate>

forms组件展示信息

"""
forms类中填写的校验性参数前端浏览器会识别并添加校验操作
但是前端的校验是可有可无的 不能指望它!!!   后端必须要有

form表单可以取消浏览器自动添加校验功能的操作
	<form action="" method="post" novalidate></form>
"""
#如何渲染错误信息
<form action=""  method="post" novalidate>
    <p>forms组件渲染标签的方式3(封装程度高 扩展性高 编写简单 推荐使用)</p>
{% for form in form_obj %}
        <p>
         {{ form.label }}
         {{ form }}
        <span>{{ form.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit"  value="提交">
</form>
</body>

后端:
from django import forms
class MyForm(forms.Form):
    username = forms.CharField(min_length=3,max_length=8)
    age = forms.IntegerField(min_value=0,max_value=200)
    email = forms.EmailField()

def ab_forms_func(request):
    #1.产生一个空对象
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj=MyForm(request.POST)  # request.POST可以看成是一个字典 直接传给forms类校验 字典中无论有多少键值对都没关系 只在乎类中编写
        if form_obj.is_valid(): # 校验数据是否合法
            print(form_obj.cleaned_data)
        else:
            print(form_obj.errors)
    # 2.将该对象传递给html文件
    return render(request,'formsPage.html',locals())

针对错误信息的提示可以修改成各国语言

方式1:自定义内容
	给字段对象添加errors_messages参数
	username = forms.CharField(min_length=3, max_length=8, label='用户名',
                               error_messages={
                                   'min_length': '用户名最少三个字符',
                                   'max_length': '用户名最多八个字符',
                                   'required': '用户名不能为空'
                               }
                               )
方式2:修改系统语言环境
	from django.conf import global_settings  django内部真正的配置文件

forms组件校验补充

from django import forms
from django.core.validators import RegexValidator
from django.forms import widgets


class MyForm(forms.Form):
    username = forms.CharField(min_length=3, max_length=8, label='用户名', initial='假阳比真阳更可怕',
                               # error_messages={
                               #     'min_length': '用户名最少三个字符',
                               #     'max_length': '用户名最多八个字符',
                               #     'required': '用户名不能为空'
                               # }
                               # widget=forms.widgets.TextInput(attrs={'class': 'form-control', 'username': 'jason'})
                               # type='text'
                               )
    password = forms.CharField(min_length=3, max_length=8, label='密码',
                               # widget=forms.widgets.PasswordInput(attrs={'class': 'form-control c1 c2 c3 c4'})
                               # type='password'
                               )
    confirm_pwd = forms.CharField(min_length=3, max_length=8, label='确认密码')

    email = forms.EmailField(required=False)

    # gender = forms.ChoiceField(
    #     choices=((1, "男"), (2, "女"), (3, "保密")),
    #     label="性别",
    #     initial=3,
    #     widget=forms.widgets.RadioSelect()
    # )
    # hobby = forms.ChoiceField(
    #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    #     label="爱好",
    #     initial=3,
    #     widget=forms.widgets.Select()
    # )
    # hobby1 = forms.MultipleChoiceField(
    #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    #     label="爱好",
    #     initial=[1, 3],
    #     widget=forms.widgets.SelectMultiple()
    # )
    # keep = forms.ChoiceField(
    #     label="是否记住密码",
    #     initial="checked",
    #     widget=forms.widgets.CheckboxInput()
    # )
    # hobby2 = forms.MultipleChoiceField(
    #     choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
    #     label="爱好",
    #     initial=[1, 3],
    #     widget=forms.widgets.CheckboxSelectMultiple()
    # )
    # phone = forms.CharField(
    #                 validators=[
    #                             RegexValidator(r'^[0-9]+$', '请输入数字'),
    #                             RegexValidator(r'^159[0-9]+$', '数字必须以159开头'),
    #                             ],
    # )
    # age = forms.IntegerField(min_value=0, max_value=200, label='年龄',
    #                          # error_messages={
    #                          #     'min_value':'年龄不能小于0岁',
    #                          #     'max_value':'你他喵的200岁以上?',
    #                          #     'required':'年龄不能为空 你妹的'
    #                          # }
    #                          )
    # email = forms.EmailField()
forms组件针对字段数据的校验 提供了三种类型的校验方式(可以一起使用)
	第一种类型:直接填写参数 		  max_length
	第二种类型:使用正则表达式	     validators	
 	第三种类型:钩子函数				编写代码自定义校验规则
	class MyForm(forms.Form):
        username = forms.CharField(min_length=3, max_length=8)
        password = forms.CharField(min_length=3, max_length=8)
        confirm_pwd = forms.CharField(min_length=3, max_length=8)
        # 钩子函数>>>:校验的最后一环 是在字段所有的校验参数之后触发
        # 局部钩子:每次只校验一个字段数据       校验用户名是否已存在
        def clean_username(self):
            username = self.cleaned_data.get('username')
            if username == 'jason':
                self.add_error('username', '用户名jason已存在')
            return username

        # 全局钩子:一次可以校验多个字段数据     校验两次密码是否一致
        def clean(self):
            password = self.cleaned_data.get('password')
            confirm_pwd = self.cleaned_data.get('confirm_pwd')
            if not password == confirm_pwd:
                self.add_error('confirm_pwd', '两次密码不一致')
            return self.cleaned_data

用户名注册和登录

urls.py:
# forms组件
path('ab_forms/',views.ab_forms_func),

models.py:
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password=models.IntegerField()
    email = models.EmailField()

views.py:
from django import forms
from django.forms import widgets
class MyForm(forms.Form):
    username = forms.CharField(min_length=3,max_length=8,label='用户名',initial='真假')
    password = forms.CharField(min_length=3,max_length=8,label='密码')
    confirm_pwd = forms.CharField(min_length=3,max_length=8,label='确认密码')
    email = forms.EmailField(required=False)

    def clean_username(self):
        username = self.cleaned_data.get('username')
        user_obj = models.UserInfo.objects.filter(username=username).first()
        if user_obj:  #如果对象有值,说明已经注册了
            self.add_error('username',f'用户名{username}已存在')
        return username
    def clean(self):
        password=self.cleaned_data.get('password')
        confirm_pwd = self.cleaned_data.get('confirm_pwd')
        if not password == confirm_pwd:
            self.add_error('confirm_pwd','两次密码不一致')
        return self.cleaned_data

def ab_forms_func(request):
    #1.产生一个空对象
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj=MyForm(request.POST)  # request.POST可以看成是一个字典 直接传给forms类校验 字典中无论有多少键值对都没关系 只在乎类中编写
        if form_obj.is_valid(): # 校验数据是否合法
            username=form_obj.cleaned_data.get('username')
            password=form_obj.cleaned_data.get('password')
            email=form_obj.cleaned_data.get('email')
            user=models.UserInfo.objects.filter(username=username).create(username=username,password=password,email=email)
        else:
            print(form_obj.errors)
    # 2.将该对象传递给html文件
    return render(request,'formsPage.html',locals())

formsPage.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
</head>
<body>
<form action="" method="post" novalidate>
     {% for form in form_obj %}
         <div>
       {{ form.label }}
        {{ form }}
        <span>{{ form.errors.0 }}</span>
         </div>

    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>
</html>

forms组件参数补充

min_length			最小字符
max_length			最大字符
min_value			最小值
max_value			最大值
label				字段注释
error_messages		错误提示
validators			正则校验器
initial				默认值
required			是否必填
widget				控制标签的各项属性

validators正则校验器案例

from django.core.validators import RegexValidator
  phone = forms.CharField(
        validators=[
                    RegexValidator(r'^[0-9]+$', '请输入数字'),
                    RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )

widget控制渲染出来的标签各项属性案例、

password = forms.CharField(
      widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)
forms.widgets.控制type的类型(attrs=控制各项属性:class id ...)

choices选择类型的标签内部对应关系案例

   gender = forms.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )
    hobby1 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )
    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
    hobby2 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )
具体参考:https://www.cnblogs.com/Dominic-Ji/p/9240365.html

modelform组件

"""我们学习校验性组件的目的 绝大部分是为了数据录入数据库之前的各项审核 forms组件使用的时候需要对照模型类编写代码 不够方便"""
froms组件的强化版本 更好用更简单更方便!!!
urls.py:
# modelform组件
path('ab_mf_form/',views.ab_mf_func)

models.py:
class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password=models.IntegerField()
    email = models.EmailField()

views.py:
from django import forms
from app01 import models
class MyModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        fields = '__all__'

def ab_mf_func(request):
    modelform_obj = MyModelForm()
    if request.method == 'POST':
        modelform_obj = MyModelForm(request.POST)
        if modelform_obj.is_valid():
            modelform_obj.save() # models.UserInfo.objects.create(字段1,字段2...)
        else:
            print(modelform_obj.errors)
    return render(request,'modelFormPage.html',locals())

modelFormPage.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
</head>
<body>
<form action="" method="post" novalidate>
     {% for modelObj in modelform_obj %}
         <div>
         {{ modelObj.label }}
        {{ modelObj }}
        {{ modelObj.errors.0 }}

         </div>

    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>
</html>

image

django中间件

django默认有七个中间件 并且还支持用户自定义中间件
中间件主要可以用于:网址访问频率的校验 用户权限的校验等全局类型的功能需求
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

如何自定义中间件
	1.创建存储自定义中间件代码的py文件或者目录(如果中间件很多)
	2.参考自带中间件的代码编写类并继承
	3.在类中编写五个可以自定义的方法
		需要掌握的
		process_request
		1.请求来的时候会从上往下依次经过每一个注册的中间件里面的该方法,如果没有则直接跳过
		2.如果该方法自己返回了HttpResponse对象那么不再往后执行而是直接原路返回
		process_response
		1.响应走的时候会从下往上依次经过每一个注册了的中间件里面的方法,如果没有则直接跳过
		2.该方法有两个形参,request和response 形参response指代的是就是后端想要返回给前端浏览器的数据,该方法必须返回该形参 也可以替换
"""如果在执行process_request方法的时候直接返回了HttpResponse对象,那么会原路返回执行process_response,不是执行所有"""
		需要了解的:
		process_view
		路由匹配成功之后执行视图函数/类之前自动触发(顺序同process_request)
		process_exception
		视图函数/类执行报错自动触发(顺序同process_response)
		process_template_response
		视图函数/类返回的HttpResponse对象含有render并且对应一个方法的时候自动触发(顺序同process_response)
	4.一定在配置文件中注册中间件才可以生效

基于django中间件的功能设计

将各个功能制作成配置文件的字符串形式
	如果想拥有该功能就编写对应的字符串
	如果不想有该功能则注释掉对应的字符串

补充知识:如何利用字符串导入模块
# 常用的导入模块的方法是from...import...
如:from app01 import views

我们还可以使用字符串形式导入模块,需要使用到importlib模块
使用importlib模块导入的最小单位为文件,不能导入文件内的名字(类或函数).
module = importlib.import_module(path)
# path:string类型的完整路径

from app01 import views
等价于
module=importlib.import_module('app01.views')

import importlib
s1 = 'bbb.b'  # aaa.bbb.ccc.b
res = importlib.import_module(s1)  # from aaa.bbb.ccc import b
print(res)  # <module 'bbb.b' from 'D:\\pythonProject03\\djangomiddle\\bbb\\b.py'>
''注意字符串的结尾最小单位只能是py文件 不能是py文件里面的变量名'''
思考django中间件是如何处理的(最小单位是类名)

需求分析:
模拟编写一个消息通知的功能(微信、qq、邮箱)
方式1:基于函数封装的版本
没有眼前一亮的感觉 很一般

def qq(content):
    print('来自于的QQ的消息:%s' %content)

def weixin(content):
    print('来自于微信的消息:%s' % content)

def email(content):
    print('来自于邮箱的消息:%s' %content)

def send_all(content):
    qq(content)
    weixin(content)
    email(content)

if __name__ == '__main__':
    send_all('圣诞节快乐')

执行结果:
来自于的QQ的消息:圣诞节快乐
来自于微信的消息:圣诞节快乐
来自于邮箱的消息:圣诞节快乐

方式2:基于django中间件的功能设计
眼前一亮 回味无穷

基于中间件思想编写项目-功能的插拔式设计

需求:发送消息,发送包括邮件、qq、微信
创建notify文件夹,内部创建3个功能py文件和1个__init__.py
email.py

class Email(object):
    def __init__(self):
        pass  # 模拟发送email之前需要做的准备操作

    def send_msg(self,content):
        print('来自于email的消息>>>:%s' %content)

qq.py

class Qq(object):
    def __init__(self):
        pass   #模拟发送qq之前需要做的准备工作

    def send_msg(self, content):
        print('来自qq的消息>>>:%s' %content)

weixin.py

class Weixin(object):
    def __init__(self):
        pass   #模拟发送微信之前需要做的准备操作

    def send_msg(self,content):
        print('来自于微信的消息>>>:%s'%content)

在notify目录下的__init__.py中编写核心代码

from setting import NOTIFY_LIST
import importlib


def send_all(content):
    for full_path in NOTIFY_LIST:  # full_path=notify.email.Email
        module_path, class_str_name = full_path.rsplit('.',maxsplit=1)  # 'notify.email' 'Email'
        # 1.利用字符串导入模块 拿到模块名
        module_name = importlib.import_module(module_path)  # from notify import email
        # 2.利用反射从模块中获取字符串对应的类名
        class_name = getattr(module_name,class_str_name)
        # 3.利用类名加括号产生对象
        obj = class_name()
        # 4.对象调用发送消息的方法
        obj.send_msg(content)

在项目目录下创建start.py和settings.py
setting.py

NOTIFY_LIST = [
    'notify.email.Email',
    'notify.qq.Qq',
    'notify.weixin.Weixin',
]

start.py

import notify

if __name__ == '__main__':
    notify.send_all('元旦假期是用来查漏补缺的')

此时,如果需要取消qq发送,管理员可以去配置文件中将对应的配置注释掉。如果需要添加redis发送功能,需要先创建新的py文件。
redis.py

class Redis(object):
    def __init__(self):
        pass  # 模拟发送Redis之前需要做的准备操作

    def send_msg(self,content):
        print('来自于redis的消息>>>:%s' %content)

然后去配置文件setting.py中对redis发送功能进行注册。
setting.py

NOTIFY_LIST = [
    'notify.email.Email',
    'notify.qq.Qq',
    'notify.weixin.Weixin',
	'notify.redis.Redis',
]

这样做,当添加新功能时,不需要修改项目主要的逻辑代码

基本思路:

1.将功能编写成一个一个的模块(.py文件)
2.在配置文件中注册功能
3.使用rsplit方法切割模块路径以及对应的类
4.使用importlib模块以字符串的形式动态地导入模块
5.使用反射获得对应的类,并加小括号实例化对象
6.通过对象调用方法
"""
添加和删除功能只需要在配置文件中将对应的注册信息增加或注释即可
"""
posted @   悠悠-winter  阅读(32)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示