240      
    Alex   
  
    每个人都有属于自己的一片森林,也许我们从来不曾去过,但它一直在那里,总会在那里。迷失的人迷失了,相逢的人会再相逢!   

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))


























posted @ 2017-11-25 15:06  Alex_c  阅读(295)  评论(0编辑  收藏  举报