• Django组件-forms组件


     form组件

    form组件出现的原因

    1. 当我们用普通的form表单提交时会刷新页面,如果这个我们表单中的某项填错了,刷新后我们正确的选项也没有了.
    2. 传统的form表单需要我们自己亲自校验每一项.工作量太大

    Django 中form组件的2大功能:
           1 验证(在前端页面显示我们定义好的错误信息)
           2 保留用户上次输入的信息

    form组件生成form表单的几种方法:

    不要忘记,表单的输出 包含submit 标签,和表单的<form> 按钮。 你必须自己提供它们。

    方法1

    对于<input>/<label> 对,还有几个输出选项:

    • {{ form.as_table }} 以表格的形式将它们渲染在<tr> 标签中
    • {{ form.as_p }} 将它们渲染在<p> 标签中
    • {{ form.as_ul }} 将它们渲染在<li> 标签中

    注意,你必须自己提供<ul> 或<table> 元素。

    views.py

    from django.shortcuts import render
    from django import forms
    # Create your views here.
    class RegisterFrom(forms.Form):  #定义一个类
        user=forms.CharField()   #和我们input表单一一对应
        pwd=forms.CharField()
    
    def register(request):
        if request.method=="POST":
            pass
        form_obj=RegisterFrom() # 1 实例化对象  
        return render(request,"register.html",{"form_obj":form_obj})
    #分析题目:
    #1这步运行后到底干了什么 ,实例化了一个变量 self.fields={"user":"user规则","pwd":"pwd规则对象"}

    html:

    <form action="">
        {{ form_obj.as_p }}  #p的意思就是p标签的意思
        <p><input type="submit" value="注册"></p>
    </form>
    
    </body>
    </html>

    方法二:手动渲染任意标签

    可以自己写任意标签,但是方法1,只能用人家规定好的p标签

     在上面的例子中,Django会根据{{ form.as_p }}自动生成表单,我们也可以自己来具体编写表单的HTML;每个字段都是表单的一个属性

    除了HTML不同外其余的都相同

    html

    <form action="">
        <P><label for="">用户名</label>
        {{ form_obj.user }}
        </P>
     <P><label for="">密码 &nbsp;&nbsp;</label>
        {{ form_obj.pwd }}
        </P>
        <p><input type="submit" value="注册"></p>
    </form>

    方法三:循环表单字段

     如果你为你的表单使用相同的HTML,你可以使用{% for %} 循环迭代每个字段来减少重复的代码

    <div class="container">
        <div>
            <h3>数据展示页面</h3>
            <hr>
            <a href="{{ add_url }}"><button class="btn btn_success ">添加按钮</button></a>
            <table class="table table-striped">
                <thead>
                <tr>
    {#            运用了form组件的第三种方法#}
                {% for header in header_list %}
                <th>{{ header }}</th> {# 仅仅是获得了一个字符串名字 #}
                {% endfor %}
                </tr>
                </thead>
                <tbody>
                {% for data in new_display %}{# data为每条信息对象 #}
                    <tr>
                    {% for item in data %}
                        <td>{{ item }}</td> {# 每条对象信息的每个属性值 #}
    
                    {% endfor %}
                    </tr>
    
                {% endfor %}
    
                </tbody>
            </table>
        </div>
    </div>

    处理表单返回的数据

    Form.is_valid() :Form对象的首要任务就是验证数据。 对于绑定的Form实例,可以调用is_valid()方法来执行验证,该方法会返回一个表示数据是否合法的布尔值。

    验证成功

    不管表单提交的是什么数据,一旦通过is.valid() 的验证,返回True,则,验证后的表单数据将位于form.cleaned_data 字典中。 这些数据已经为你转换好为Python 的类型。

    cleaned_data:,验证后的表单数据将位于form.cleaned_data 字典中

    注意:此时你也可以从request.post获得验证成功的数据传到数据库(如果需要的话),但是我们更期待你从from.class_data字典中来获得数据.

    注意:如果你的数据没有 通过验证,cleaned_data 字典中只包含合法的字段

    def register(request):
        if request.method=="POST":
            form=UserForm(request.POST)
            if formsis_valid():
                user=form.cleaned_data["username"]
                pwd=form.cleaned_data["password"]
    obj=User.objects.create(name=username,password=pwd)
                return HttpResponse("注册成功")

    验证失败

     form.errors:验证失败后的表单数据将位于form.errors的字典

    在这个字典中,键为字段的名称,值为表示错误信息的Unicode 字符串组成的列表。 错误信息保存在列表中是因为字段可能有多个错误信息。

     访问errors 属性可以获得错误信息的一个字典:

    >>> f.errors
    {'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}

    Form.add_error(field, error)

    该方法允许从Form.clean()方法中或从表单外部向特定字段添加错误;例如从一个角度。

    field 参数为字段的名称。 如果值为None,error 将作为Form.non_field_errors() 返回的一个非字段错误。

    error 参数可以是一个简单的字符串,或者最好是一个ValidationError 实例。 Raising ValidationError 中可以看到定义表单错误时的最佳实践。

    注意,Form.add_error() 会自动删除cleaned_data 中的相关字段。

    print(form_obj.errors)

    <ul class="errorlist"><li>user<ul class="errorlist">
    <li>Ensure this value has at least 6 characters (it has 2).
    <ul class="errorlist">
        <li>user
            <ul class="errorlist">
                <li>Ensure this value has at least 6 characters (it has 2).</li>
            </ul>
        </li>
    </ul>

    print(type(form_obj.errors))

    <class 'django.forms.utils.ErrorDict'> 是一个特殊的字典类型
    那我们就用字典的方法取值: print(type(form_obj.errors["user"]))
    <class 'django.forms.utils.ErrorList'>  #表名这是一个列表类型,可以用列表方法取值
    print(type(form_obj.errors["user"][0]))

    class  str  表名这是一个字符串类型
    print((form_obj.errors["user"][0]))
    Ensure this value has at least 6 characters (it has 2).
    print(type(form_obj.fields)
    <class 'collections.OrderedDict'>

    源码解析:

    def _clean_fields(self):
            for name, field in self.fields.items():   #name就是你在class中定义的对象(如user), filed 就是你每个对象对象的字段规则, self.fileds就是 实例化对象对应的那个字典,item有序排列
                # 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)
    </li></ul></li></ul>

    整理一下,我们得到: user的错误信息

     局部钩子

    字段校验有时候很不灵活,我们只能校验field的字段中有的规则,没有的规则,我们就不能在字段中校验,比如说,我们要校验用户名不能全为数字,这时候你在字段中就校验不出来了.这就是出现了局部钩子的概念

    局部钩子:用来校验某一个字段,自己额外添加的规则, 定义函数的名称必须为 def clean_字段(self),为什么写的原因在下边源码中已经解释了

    try:
      if hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()  #self表示当前对象
                        self.cleaned_data[name] = value #这个校验的字段成功后放在了cleaned_data的字典中,你可以从这个字典中获得对应的数据
    except ValidationError as e:
        self.add_error(name, e) #如果校验不通过,就把它添加到errors的字典中去

    例子:

    def clean_user(self):
            """
            局部钩子
            验证用户名是否注册过
            :return:
            """
            username=self.cleaned_data.get("user")#从已经符合字典中取出相应的字段,也就是说先判断is_valid(),然后再校验局部钩子
    
            data1=models.UserInfo.objects.filter(username=username)
            if not data1:
                return username
            else:
                raise ValidationError("该用户已经注册")

    全局钩子

    当校验规则涉及到了多个字段时候,这时候就要用全局钩子了.

    def clean(self):
            """
            全局钩子,验证两次密码是否一致
            :return:
            """
            pwd=self.cleaned_data.get("password")
            repeat_pwd=self.cleaned_data.get("repeat_password")
            if pwd and repeat_pwd:    #判断是否有值,因为只要前边有错误,也会走全局钩子
                if pwd == repeat_pwd:
                    return self.cleaned_data
                else:
                    raise ValidationError("密码不一致")
            else:
                return self.cleaned_data

    源码解析,

    全局钩子走这个源码: self._clean_form()

    def _clean_form(self):
            try:
                cleaned_data = self.clean()
            except ValidationError as e:
                self.add_error(None, e)
            else:
                if cleaned_data is not None:
                    self.cleaned_data = cleaned_data
    def clean(self):
            """
            Hook for doing any extra form-wide cleaning after Field.clean() has been
            called on every field. Any ValidationError raised by this method will
            not be associated with a particular field; it will have a special-case
            association with the field named '__all__'.
            """
            return self.cleaned_data #这里面什么都没干,只是返回了cleaned_data中的数据,我们就可以扩展这个钩子
    Form.non_field_errors()

    这个方法返回Form.errors 中不是与特定字段相关联的错误。 它包含在Form.clean() 中引发的ValidationError 和使用Form.add_error(None, "...") 添加的错误。

    注意在前端你要console.log(form.field.errors),你会发现在errors这个字典中会出现"__all__",这个字段.这个就是存放全局钩子错误信息的字段

    __all__:  在错误信息中全局钩子对应的字段是__all__

    modelForm

    modelForm说白了就是结合了model和form的特性

    models.py

    class Book(models.Model):
        title=models.CharField(max_length=32)
        price=models.IntegerField(default=12)
        publishs=models.ForeignKey(to="Publish")
        authors=models.ManyToManyField(to="Author")
        def __str__(self):
            return self.title
    class Publish(models.Model):
        name=models.CharField(max_length=32)
        email = models.CharField(max_length=32,default=111)
        def __str__(self):
            return self.name
    class Author(models.Model):
        name=models.CharField(max_length=32)
        def __str__(self):
            return self.name
    
    form组件的例子
    form组件的例子

    views.py 拿bookmodel来举例子:

    from django import forms
    from django.shortcuts import render,HttpResponse,redirect
    from .models import *
    class BookForm(forms.Form):
        title=forms.CharField(max_length=8)
        price=forms.IntegerField()
        publish_list = Publish.objects.all().values_list("pk", "name")
        author_list = Author.objects.all().values_list("pk", "name")
        t=((1,"北京"),(2,"天津"))
        state=forms.ChoiceField(choices=publish_list)
        state1=forms.MultipleChoiceField(choices=author_list)

    你会发现在model中定义了字段,但是又在form中定义了一遍,是不是感觉到冗余,ModelForm就是基于这个问题出现的.

    像这样:

    from .models import *
    from django.forms import ModelForm #导入ModelForm这个类
    class BookModelForm(ModelForm):
        class Meta:
            model=Book
            # fields=["title","price"] #只展示"title"和"price"字段
            fields="__all__"     #展示所有的字段
            labels={         
                "title": "书名",
                "price":"价格",
                "publishs":"出版社",
                "authors":"作者"
            }#自定义字段在前端显示的名称
            error_messages={
                "title":{"required":"不能为空"}
            }
    form_obj=BookModelForm()#生成一个ModelForm类的对象

    字段类型

    生成的Form类中将具有和指定的模型字段对应的表单字段,顺序为fields 属性中指定的顺序。

    每个模型字段有一个对应的默认表单字段。具体字段详情见中文文档

    • QuerySet 表示成ChoiceField,它是一个django.forms.ModelChoiceField,其选项是模型的ForeignKey
    • QuerySet 表示成MultipleChoiceField,它是一个django.forms.ModelMultipleChoiceField,其选项是模型的ManyToManyField
    • 如果模型字段设置了blank=True,那么表单字段的required字段会设置为False值。 否则,required=True
    • 表单字段的verbose_name 设置为模型字段的label,并将第一个字母大写。
    • 表单字段的help_text 设置为模型字段的help_text
    • 如果模型字段设置了choices,那么表单字段的widget将会设置为Select,选择项从模型字段的choices而来。 选项通常会包含空选项,并且会默认选择。如果字段是必选的,它会强制用户选择一个选项。 如果模型字段的default 且具有一个显示的default 值,将不会包含空选项(初始将选择blank=False值)。

    选择要展示的字段:

    强烈建立选择展示所有的字段

    fields=["title","price"] #只展示"title"和"price"字段

    (1)设置'__all__' 属性为特殊的值fields 以表示需要使用模型的所有字段.

    (2)设置Meta 内联的ModelForm 类的exclude 属性为一个要从表单中排除的字段的列表

    lass PartialAuthorForm(ModelForm):
        class Meta:
            model = Author
            exclude = ['title']

    save()

    每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。

     ModelForm的子类可以接受现有的模型实例作为关键字参数instance;如果提供此功能,则save()将更新该实例

     如果没有提供,save() 将创建模型的一个新实例:

    这就是和form本质的区别

    def add(request):
        if request.method=="GET":
            forms_obj=BookModelForm()
            return render(request,"add.html",locals())
        else:
            forms_obj=BookModelForm(request.POST)#创建一个对象
            if forms_obj.is_valid():
                forms_obj.save() #在modelform中直接一句话就等于取出数据添加到数据库了操作了.
           #如果不用modelForm用form的话,要取出传进来的参数,创建对象然后通过create添加到数据库
    
                return redirect("/index/")
            else:
                return render(request, "add.html", locals())
    
    def edit(request,id):
        edit_book = Book.objects.filter(pk=id).first()
        if request.method=="GET":
            forms_obj=BookModelForm(instance=edit_book)
            return render(request,"edit.html",locals())
        else:
            forms_obj= BookModelForm(data=request.POST,instance=edit_book)#更新对象
            if forms_obj:
                forms_obj.save()
                return redirect("/index/")

    form和modelForm的区别

    1. 减少了代码重复
    2. 提交和更新数据更简洁

    Django的用户认证  auth模块

    如果你想用这个auth模块: 大前提要用Django生成的auth_user 表,不能自己再创建user表了

     auth模块是什么?

    auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组和权限管理。

    auth可以和admin模块配合使用, 快速建立网站的管理系统。

    在INSTALLED_APPS中添加'django.contrib.auth'使用该APP, auth模块默认启用.

    django.contrib.auth.middleware.AuthenticationMiddleware  这个是auth模块的中间件

    认证登录

    1 #导入auth模块:
    2 from django.contrib import auth

    auth模块提供了老多的方法在这里我们只介绍三种方法:

    authenticate() 验证用户

    提供了用户认证,即验证用户名以及密码是否正确,一般需要username  password两个关键字参数

    如果认证信息有效,会返回一个  User  对象。authenticate()会在User 对象上设置一个属性标识那种认证后端认证了该用户,且该信息在后面的登录过程中是需要的。当我们试图登陆一个从数据库中直接取出来不经过authenticate()的User对象会报错返回None!!


    user = authenticate(username='someone',password='somepassword') #把从前端传过来的用户名和密码在这里进行验证

     login(request,user) 给用户绑定session

     该函数接受一个request的对象以及认证了的user对象

    此函数利用Django的session框架给某个已认证的用户加上session ID等信息.

    from django.contrib.auth import authenticate,login #导入auth模块下的authenticate类和login类
    
    def my_view(request):
        username=request.POST.get("username")
        password=request.POST.get("password")
        user=authenticate(username=username,password=password)
        if user is not None:
            login(request,user) #把request,和user传入login函数中进行session的写入操作,相当于request.session["name"]=username
            return redirect("/index/")
        else:
            return redirect("login")

    logout(request)注销用户 

    from django.contrib.auth import logout
    def logout_view(request):
        logout(request)#其实这里相当于我们写的request.session.flush()
        return redirect("/login/")

    该函数接受一个HttpRequest对象,无返回值。当调用该函数时,当前请求的session信息会全部清除。该用户即使没有登录,使用该函数也不会报错.

    user对象的is_authenticated()

    要求:

    1  用户登陆后才能访问某些页面,

    2  如果用户没有登录就访问该页面的话直接跳到登录页面

    3  用户在跳转的登陆界面中完成登陆后,自动访问跳转到之前访问的地址

    方法1:

    def my_view(request):
        if not request.user.is_authenticated():
            return redirect("%s?next=%s"%(settings.LOGIN_URL,request.path))##这句话不理解

    方法2:login_required函数

    django已经为我们设计好了一个用于此种情况的装饰器:login_requierd()

    1
    2
    3
    4
    5
    from django.contrib.auth.decorators import login_required
         
    @login_required
    def my_view(request):
      ...

    若用户没有登录,则会跳转到django默认的 登录URL '/accounts/login/ ' (这个值可以在settings文件中通过LOGIN_URL进行修改)。并传递  当前访问url的绝对路径 (登陆成功后,会重定向到该路径)。 

    user对象

    当你用了auth模块后,request的会生成一个user属性,里边封装了你登录了的对象,这样你可以在request.user.用户的信息,如果你没有登录print(request.user)会打印出匿名用户对象的英文单词,

    重点: 你可以在所有的视图函数和HTML页面的中用request.user来获取登录人的相关信息

    User 对象属性:username, password(必填项)password用哈希算法保存到数据库

    is_staff : 用户是否拥有网站的管理权限.

    is_active : 是否允许用户登录, 设置为``False``,可以不用删除用户来禁止 用户登录

    user对象的方法

    is_authenticated()

    如果是真正的 User 对象,返回值恒为 True 。 用于检查用户是否已经通过了认证。
    通过认证并不意味着用户拥有任何权限,甚至也不检查该用户是否处于激活状态,这只是表明用户成功的通过了认证。 这个方法很重要, 在后台用request.user.is_authenticated()判断用户是否已经登录,如果true则可以向前台展示request.user.name.

    def index(request):
        user=request.user
        if not user.is_authenticated():
            return redirect("/login/")
        return render(request,"index.html",local())

      注册用户create_user()

    使用 create_user 注册用户:

    from django.contrib.auth.models import User #这个就是Django自己创建的auth_user表,你要对他进行操作就需要用它创建的这个类User
    user=User.objects.create_user(username="xiaohia",password="123456") #注意这里不能用create()函数这样也能注册新用户但是密码不会加密,必须用人家提供的create_user()方法
    user.save()#别忘记了保存

    验证密码check_password(passwd)

    用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True

    def my_view(request):
        user=request.user
        if request.method=="POST":
            old_password=request.POST.get("old_password")
            if user.check_password(old_password):
                return HttpResponse("ok")

    这儿你可能会对request.user不理解 在这里我们来讲一下

    补充request.user

    关于request的更多内容请见https://www.cnblogs.com/renfanzi/p/5838243.html, 这里关于request的内容整理的很好

    一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。

      如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。

    例如:

    1
    2
    3
    4
    if request.user.is_authenticated():
        # Do something for logged-in users.
    else:
        # Do something for anonymous users.

        user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。

    匿名用户

      class models.AnonymousUser

      django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    id 永远为None。
    username 永远为空字符串。
    get_username() 永远返回空字符串。
    is_staff 和 is_superuser 永远为False。
    is_active 永远为 False。
    groups 和 user_permissions 永远为空。
    is_anonymous() 返回True 而不是False。
    is_authenticated() 返回False 而不是True。
    set_password()、check_password()、save() 和delete() 引发 NotImplementedError。

       New in Django 1.8:

      新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User

    修改密码set_password()

    from django.contrib.auth.models import User
    user=User.objects.get(username="xiaohia")
    user.set_password(password="123456")
    user.save() #别忘记了保存

    注册用户

    复制代码
    def sign_up(request):
    
    
        state = None
    
        if request.method == 'POST':
    
            password = request.POST.get('password', '')
            repeat_password = request.POST.get('repeat_password', '')
            email=request.POST.get('email', '')
            if password == '' or repeat_password == '':
                state = 'empty'
            elif password != repeat_password:
                state = 'repeat_error'
            else:
                username = request.POST.get('username', '')
                if User.objects.filter(username=username):
                    state = 'user_exist'
                else:
                    new_user = User.objects.create_user(username=username, password=password,email=email)
                    new_user.save()
                    new_my_user = MyUser(user=new_user, telephone=request.POST.get('telephone', '')) ####这步是什么意思
                    new_my_user.save()
                    return redirect('/book/')
        content = {
            'state': state,
            'user': None,
        }
        return render(request, 'book/sign_up.html', content)
    复制代码

    修改密码:

    @login_required
    def set_password(request):
        user = request.user
        state = None
        if request.method == 'POST':
            old_password = request.POST.get('old_password', '')
            new_password = request.POST.get('new_password', '')
            repeat_password = request.POST.get('repeat_password', '')
            if user.check_password(old_password):
                if not new_password:
                    state = 'empty'
                elif new_password != repeat_password:
                    state = 'repeat_error'
                else:
                    user.set_password(new_password)
                    user.save()
                    return redirect("/log_in/")
            else:
                state = 'password_error'
        content = {
            'user': user,
            'state': state,
        }
        return render(request, 'book/set_password.html', content)
    
    修改密码
    View Code

    中介模型

    用中介模型的原因

     

    我们知道我们可以用manytomany创建出第三张关系表, 我们以前都做过这么几张表,学生表和课程表,学生表和课程表是多对多的关系, 但是我有这么一个需求,我想在第三张关系表中记录每个学生每门课程的分数,这时候应该怎么办,我们都知道不能在关系表中添加字段,这时候就需要第四章表了,第4张表就是中介模型,

    建表代码:

    class Student(models.Model):
        name=models.CharField(max_length=32)
        courses=models.ManyToManyField(to="Course",through="Student2Course")  #在manytomany中增加一个through字段 关联到第4张表
    
    class Course(models.Model):
        name = models.CharField(max_length=32)
    
    class Student2Course(models.Model):
        student=models.ForeignKey(to="Student") #用这步的原因的是外键关联到那两张表会自动建立id字段
        course=models.ForeignKey(to="Course")
        score=models.IntegerField()

    表结构:

    发现了没? 以前用manytomany自动建立的第三张表student_courses 没有了,出现了一个新的表 student2course,让我们来看看这张表

    是不是以前的关系表都可以继承过来了,还增加了新字段.

    注意事项

     以前manytomany操作第3张表的方法,很多都不能用了,add、 create,remove,但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系, 但是我们可以用操作普通表的方式来操作第4张表.

  • 相关阅读:
    Divide by Zero 2017 and Codeforces Round #399 (Div. 1 + Div. 2, combined) C
    Divide by Zero 2017 and Codeforces Round #399 (Div. 1 + Div. 2, combined) B. Code For 1
    引入CSS文件的方式,以及link与@import的区别
    JavaScript
    css
    html
    CentOS 7编译安装Python3.7.X
    python3配置爬虫开发环境
    Threading模块
    队列Queue的get方法
  • 原文地址:https://www.cnblogs.com/wxj1129549016/p/9897925.html
Copyright © 2020-2023  润新知