zoukankan      html  css  js  c++  java
  • django-from

    构建一个表单

    这是一个非常简单的表单。实际应用中,一个表单可能包含几十上百个字段,其中大部分需要预填充,而且我们预料到用户将来回编辑-提交几次才能完成操作。

    我们可能需要在表单提交之前,在浏览器端作一些验证。我们可能想使用非常复杂的字段,以允许用户做类似从日历中挑选日期这样的事情,等等。

    这个时候,让Django 来为我们完成大部分工作是很容易的。

    在Django 中构建一个表单

    Form 类

    我们已经计划好了我们的 HTML 表单应该呈现的样子。在Django 中,我们的起始点是这里:
    form模块
    from django.forms import Form                 #继承Form类
    from django.forms import fields               #验证字段
    from django.forms import widgets              #通过widgets来更改form表单中的样式
    
    from django.forms import Form                 #继承Form类
    from django.forms import fields               #验证字段
    from django.forms import widgets              #通过widgets来更改form表单中的样式
    class TeacherForm(Form)
        username = fields.CharField(
            required=True,                         #True字段不能为空,false字段可以为空
            error_messages={'required':'用户名不能为空'},     #报错信息可以指定添加报错信息,不指定的话就是默认的报错信息
            widget=widgets.TextInput(attrs={'placeholder':'用户名','class':'form-control'}) #可以给表单添加样式然后在前端页面渲染出来
        ) # 不能为空
        password = fields.CharField(required=True,
                                    error_messages={'required':'密码不能为空'},
                                    widget=widgets.TextInput(attrs={'placeholder':'密码','class':'form-control'})) # 不能为空
        email = fields.EmailField(required=True,
                                  error_messages={'required':'邮箱不能为空','invalid':'邮箱格式错误'},              # 不能为空,且邮箱格式
                                  widget=widgets.EmailInput(attrs={'placeholder':'邮箱','class':'form-control'}))   
        
        
    
    

    视图

    发送给Django 网站的表单数据通过一个视图处理,一般和发布这个表单的是同一个视图。这允许我们重用一些相同的逻辑。

    当处理表单时,我们需要在视图中实例化它:


    def add_teacher(request):
        if request.method == 'GET':
            form = TeacherForm()
            return render(request,'add_teacher.html',{'form':form})
        else:
            """
            1. 用户请求数据验证
            2. 自动生成错误信息
            3. 打包用户提交正确信息
            4. 错误:保留上次输入内容
            5. 定制页面上显示的HTML标签
            Django Form组件
            1. 创建规则(类,字段)
                class Foo:
                    username = xxx
                    password = xxx
                    email = xxx
            2. 数据和规则进行匹配
            """
            form = TeacherForm(data=request.POST) # 数据和规则放置一起
            if form.is_valid():                       # 开始校验,并获取校验结果
                # print('执行成功',form.cleaned_data)          # 所有匹配成功,字典
                # {'username': 'asd', 'password': 'sdf', 'email': 'sadf@live.com','ut_id':1}
                form.cleaned_data['ut_id'] = 1
                models.UserInfo.objects.create(**form.cleaned_data)
                return redirect('/teachers/')
    
            return render(request, 'add_teacher.html',{'form':form})

    如果访问视图的是一个GET 请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一个访问该URL 时预期发生的情况。

    如果表单的提交使用POST 请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:form = NameForm(request.POST)。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。

    我们调用表单的is_valid()方法;如果它不为True,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。

    如果is_valid()True,我们将能够在cleaned_data 属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。

    模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
    </head>
    <body>
        <h1>添加老师</h1>
    <form method="POST" novalidate>
        {% csrf_token %}
        <p>{{ form.username }} {{ form.errors.username.0 }} </p>    #直接通过后端上下文对应调用名字,通过form.errors.username.0来调用错误信息
        <p>{{ form.email }} {{ form.errors.email.0 }} </p>
        <p>{{ form.password }} {{ form.errors.password.0 }} </p>
    
        <input type="submit" value="提交" />
    
    
    </form>
    </body>
    </html>

    根据{{ form }},所有的表单字段和它们的属性将通过Django 的模板语言拆分成HTML 标记 。

    注:Django 原生支持一个简单易用的跨站请求伪造的防护。当提交一个启用CSRF 防护的POST 表单时,你必须使用上面例子中的csrf_token 模板标签。

    现在我们有了一个可以工作的网页表单,它通过Django Form 描述、通过视图处理并渲染成一个HTML <form>

    Django Form 类详解

    form字段参数

    	 #密码验证
         - max_length
    	 - min_length
    	 - validators = [RegexValidator('xxx')]      #正则表达式 通过正则表达式来进行验证
    
    示例:
            password = fields.CharField(
            required=True,
            min_length=3,
            max_length=18,
            error_messages={
                'required': '密码不能为空',
                'min_length': '密码长度不能小于3',
                'max_length': '密码长度不能大于18',
                'invalid': '密码格式错误',           #如果加上xxx的话invalid也要改xxx要相同的名字
            },
            validators=[RegexValidator('d+','只能是数字','xxxx') ]     #****注意当两个相同的判断都生效时,上面的优先级更高
        )

    验证登陆
    from django.shortcuts import render,redirect,HttpResponse
    from django.conf import settings
    # Create your views here.
    from django.forms import Form
    from django.forms import fields
    from django.forms import widgets
    from app01 import models
    from django.core.validators import RegexValidator
    from django.core.exceptions import ValidationError
    class LoginForm(Form):
        username = fields.CharField(
            required=True,
            min_length=3,
            max_length=18,
            error_messages={
                'required': '用户不能为空',
                'min_length': '用户长度不能小于3',
                'max_length': '用户长度不能大于18',
            }
        )
        password = fields.CharField(
            required=True,
            min_length=3,
            max_length=18,
            error_messages={
                'required': '密码不能为空',
                'min_length': '密码长度不能小于3',
                'max_length': '密码长度不能大于18',
                'invalid': '密码格式错误',                    #如果加上xxx的话invalid也要改xxx要相同的名字
            },
            validators=[RegexValidator('d+','只能是数字') ]       #****注意当两个相同的判断都生效时,上面的优先级更高
        )
    '''  之前的方法
        # def clean_username(self):
        #     # ...
        #     user = self.cleaned_data['username']
        #     is_exsit = models.UserInfo.objects.filter(username=user).count()
        #     if not is_exsit:
        #         raise ValidationError('用户名不存在')
        #     return user
        #
        # def clean_password(self):
        #     user = self.cleaned_data['username']
        #     return user
    '''
    def login(request):            #现在用form进行验证
        if request.method == "GET":
            form = LoginForm()
            return render(request, 'login.html', {'form': form})
        elif request.method == "POST":
            form = LoginForm(data=request.POST)
           # <tr><th><label for="id_password">Password:</label></th><td><input type="text" name="password" value="123" maxlength="18" minlength="3" requir     这里form拿到的是前端页面上的所有input框中的代码
    ed id="id_password" /></td></tr>
    
            if form.is_valid():           #先进行form验证
                # 验证成功
                user = models.UserInfo.objects.filter(**form.cleaned_data).first()   #这里接收的话需要加上**form表单验证成功在进行数据库验证
                if not user:
                    # 如果验证错误用户名或密码错误
                    # form.add_error('password','用户名或密码错误')
                    form.add_error('password', ValidationError('用户名或密码错误'))   #则主动抛出错误
                    return render(request, 'login.html', {'form': form})
                else:
                    request.session[settings.SJF] = {'id': user.id, 'username': user.username}
                    return redirect('/index/')
            else:
                # 验证失败
                return render(request, 'login.html', {'form': form})
        else:
            return HttpResponse('滚')
    






    Django Form 扩展

    钩子函数

    form运行的过程
    当用户进行验证时,会有两个值username,password,拿到两个值后,会进行一个循环验证是否有这两个,
    
    
    
    
        @property                                        #2        #当视图函数调用时,@property不需要()就可以调用
        def errors(self):
            "Returns an ErrorDict for the data provided for the form"
            if self._errors is None:
                self.full_clean()                      #3        #通过full_clean()拿到全部的值之后调用
            return self._errors
    
        def is_valid(self):                             #1      #可以看到iv_valid验证,
            """
            Returns True if the form has no errors. Otherwise, False. If errors are
            being ignored, returns False.
            """
            return self.is_bound and not self.errors
    
    
    
        def full_clean(self):                          #4          
            """
            Cleans all of self.data and populates self._errors and
            self.cleaned_data.
            """
            self._errors = ErrorDict()
            if not self.is_bound:  # Stop further processing.
                return
            self.cleaned_data = {}
            # If the form is permitted to be empty, and none of the form data has
            # changed from the initial data, short circuit any validation.
            if self.empty_permitted and not self.has_changed():
                return
    
            self._clean_fields()
            self._clean_form()
            self._post_clean()
    
        def _clean_fields(self):                          #执行里面的方法
            for name, field in self.fields.items():           #进行一个循环,把用户传过来的值进行循环验证
                # value_from_datadict() gets the data from the data dictionaries.
                # Each widget type knows how to retrieve its own data, because some
                # widgets split data over several HTML fields.
                if field.disabled:
                    value = self.get_initial_for_field(field, name)
                else:
                    value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
                try:
                    if isinstance(field, FileField):
                        initial = self.get_initial_for_field(field, name)
                        value = field.clean(value, initial)
                    else:
                        value = field.clean(value)
                    self.cleaned_data[name] = value
                    if hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()
                        self.cleaned_data[name] = value
                except ValidationError as e:
                    self.add_error(name, e)
    
    from django.shortcuts import render,redirect,HttpResponse
    from django.conf import settings
    # Create your views here.
    from django.forms import Form
    from django.forms import fields
    from django.forms import widgets
    from app01 import models
    from django.core.validators import RegexValidator
    from django.core.exceptions import ValidationError
    class LoginForm(Form):
        username = fields.CharField(                   #每一个进来的值,先进行正则,在进行钩子函数
            required=True,
            min_length=3,
            max_length=18,
            error_messages={
                'required': '用户不能为空',
                'min_length': '用户长度不能小于3',
                'max_length': '用户长度不能大于18',
            }
        )
        password = fields.CharField(                #在进来一个值,先进行正则,在执行钩子函数
            required=True,
            min_length=3,
            max_length=18,
            error_messages={
                'required': '密码不能为空',
                'min_length': '密码长度不能小于3',
                'max_length': '密码长度不能大于18',
                'invalid': '密码格式错误',
            },
            validators=[RegexValidator('d+','只能是数字') ]
        )
        
         #自定义函数
        def clean_username(self):
            # ...
            user = self.cleaned_data['username']                 #这样拿到用户的值
            is_exsit = models.UserInfo.objects.filter(username=user).count()     #通过models进行用户的验证
            if not is_exsit:                                          #如果没有值
                raise ValidationError('用户名不存在')                  #则抛出错误,用户名不存在
            return user                                             #在返回回去,如果没返回的话会一直拿到的是空值
    
        def clean_password(self):                        #注意,这里只能拿自己的字段不能拿别人的字段,用户拿过来的值是字典,字典是无序的,不知道是先执行的username,还是password,
            user = self.cleaned_data['username']
            return user
    
    def login(request):
        if request.method == "GET":
            form = LoginForm()
            return render(request, 'login.html', {'form': form})
        elif request.method == "POST":
            form = LoginForm(data=request.POST)
            print(form)
            if form.is_valid():
                # 验证成功
                user = models.UserInfo.objects.filter(**form.cleaned_data).first()
                if not user:
                    # 用户名或密码错误
                    # form.add_error('password','用户名或密码错误')
                    # form.add_error('username',ValidationError('用户名错误'))
                    form.add_error('password', ValidationError('用户名密码错误'))
                    return render(request, 'login.html', {'form': form})
                else:
                    request.session[settings.SJF] = {'id': user.id, 'username': user.username}
                    return redirect('/index/')
            else:
                # 验证失败
                return render(request, 'login.html', {'form': form})
        else:
            return HttpResponse('滚')
    


    中间件

    中间件是什么
    中间件是一个类



    当我们登陆验证需要使用装饰器来装饰session来进行登陆验证是否带着session来没有则返回login页面,这样的话太麻烦了,在视图中少数的函数还好,当成百上千的函数出现时还需要这种方式吗
    装饰器函数登陆验证示例:
    def auth(func):
        def inner(request,*args,**kwargs):
            user_info = request.session.get(settings.SJF)
            if not user_info:
                return redirect('/login/')
            return func(request,*args,**kwargs)
        return inner
    @auth                                                 #当进入index页面验证是否带有session
    def index(request):
        username = request.session[settings.SJF]['username']
        return render(request,'index.html',{'username':username})
    
    @auth
    def teachers(request):
        # models.UserInfo.objects.filter(ut__title='讲师')
        teacher_list = models.UserInfo.objects.filter(ut_id=1)
    
        return render(request,'teachers.html',{'teacher_list':teacher_list})
    
    那怎么解决呢,这就需要中间件了,先来一张中间件的流程图

    加入我们有一个中间件,中间件里面有函数m1 m2 m3 当浏览器从request,m1 m2 m3 进去,当返回时,会从response,m3 m2 m1出来 
    当浏览器访问时,进入login页面登陆会先经过中间件这样我们可以在中间件加入判断,如果运行成功中间件默认无返回值是None之后继续执行后续的中间件和视图函数,如果有返回值则直接从自己的prcess_request进去则从自己的prcess_response返回给浏览器,
    示例:
    from django.conf import settings
    from django.shortcuts import redirect
    class MiddlewareMixin(object):
        def __init__(self, get_response=None):
            self.get_response = get_response
            super(MiddlewareMixin, self).__init__()
    
        def __call__(self, request):             #call方法让子类继承
            response = None
            if hasattr(self, 'process_request'):
                response = self.process_request(request)
            if not response:
                response = self.get_response(request)
            if hasattr(self, 'process_response'):
                response = self.process_response(request, response)
            return response
    
    
    class M1(MiddlewareMixin):      #里面继承类
    
        def process_request(self,request):
            # 无返回值:继续执行后续中间件和视图函数
            # 有返回值:执行自己的process_response和上面的response
            # request.xxxx= 888
            # request.path_info # /login/
            if request.path_info == "/login/":            #取路径,需要login页面一直是放开的,所以不对login做验证
                return None
    
            user_info = request.session.get(settings.SJF)         #取session,有session值的话是true,
            if not user_info:                                      #没有session的话则直接返回login
                return redirect('/login/')
        def process_response(self,request,response):
            print('m1.process_response')
            return response
    class M2(MiddlewareMixin):
    
        def process_request(self,request):
            print('m2.process_request')
        def process_response(self,request,response):
            print('m2.process_response')
            return response
    中间件的使用路径
     

    django中间件做过什么

                - 用户登录 
    			- 日志记录
    			- csrf
    			- session
    			- 权限管理***



    数据源无法实时更新

    		1. headmaster_id
    		2. 数据源无法实施更新,重写构造方法
           方式一(推荐):
    				class ClassForm(Form):
    					caption = fields.CharField(error_messages={'required':'班级名称不能为空'})
    					# headmaster = fields.ChoiceField(choices=[(1,'娜娜',)])
    					headmaster_id = fields.ChoiceField(choices=[])
    
    					def __init__(self,*args,**kwargs):
    						super().__init__(*args,**kwargs)
    						self.fields['headmaster_id'].choices =models.UserInfo.objects.filter(ut_id=2).values_list('id','username')
    
                方式二:
    			
    				from django.forms.models import ModelChoiceField
    				class ClassForm(Form):
    					caption = fields.CharField(error_messages={'required':'班级名称不能为空'})
    					# headmaster = fields.ChoiceField(choices=[(1,'娜娜',)])
    					headmaster_id = ModelChoiceField(queryset=models.UserInfo.objects.filter(ut_id=2))


























  • 相关阅读:
    HDU 2460 Network 傻逼Tarjan
    HTTP状态码
    Spring Tool Suite(STS)安装
    Manajro17配置
    VsCode搭建Java开发环境
    《从零开始搭建游戏服务器》项目管理工具Maven
    Debian9安装MariaDB
    Intellij从无到有创建项目
    PostgreSQL 二进制安装
    Intellij IDEA创建项目
  • 原文地址:https://www.cnblogs.com/zcfx/p/7895334.html
Copyright © 2011-2022 走看看