django之Formh与ModelForm

1.Form和ModelForm组件

组件可以用来干嘛

  • 表单验证(对用户提交的数据进行校验)
  • 自动生成HTML标签【能够将自定义的组件和相对应的类作关联】
  • 数据初始化(新建按钮、编辑按钮)、保持原来的数据。

保持原来的数据是什么意思呢?
比如我们使用用户名和密码去登录一个网站,当密码错误之后,上一次填写的用户名和密码立马被清空,这个时候,一定会让我们很恼火,我密码错了,好家伙,直接把我用户名数据干没了,要是我,不注册了哈哈!

1.1 Form组件

以python开发为例:
Django框架:含内置+第三方表单验证组件
Falsk框架:无内置,只能用第三方表单验证组件。eg:wtforms

  • 表单验证组件化 保持原来的数据 【优点在于把所有的校验都放到form组件中】eg:

    # 自定义form组件的类(表单类)【重点要会类怎么写,参数各式什么意思,就能实现表单的 
    验证】
    from django.shortcuts import render,HttpResponse
    from django import forms
    
    class MyForm(Form):
         mobile1 = forms.CharField(reg="\d{11}", required=True,widget=forms.TextInput)
         sms = forms.CharField(required=True)
        
         """ 用户注册 """
         if request.method == "GET":    # 用户向网站发送请求 True -> 让用户看到页面
             # 实例化对象并初始化数据
             form = MyForm(initial={"mobile1":"18766666666","sms":"999"})  
             # 将表单实例form传递给xx.html模版,并渲染该模版
             return render(request, "xx.html",{"form":form})  
      
             # request.POST -> 包含用户提交的所有数据
             #将用户提交的数据给到Myform组件,该组件根据定义字段去用户提交数据中提取数据,最后根据内部定义的校验机制进行校验	 
             form = MyForm(request.POST)        
             if form.is_valid():    		# 开始校验
                print(form.cleared_data)   # 获取到校验成功的数据
             else:
                print(form.errors)         # 获取到校验失败的数据
    
             # 保持原来的数据
             return render(request, "xxxxx.html",{"form":form})
    
    
  • 生成HTML标签+初始化表单值

    # 前端和后端的两种做法
    
    # 前端的做法
    <form method="post">
        <input type="text" name="mobile1" />
        <input type="text" name="gaojie" />
        <input type="submit" value="提交" />
    </form>
    
    ************************************************************
    # 后端的做法
    <form method="post">
    	# 自动生成标签  【初始化值在业务逻辑中实例化form组件的类中的 instance参数】
        {{form.mobile1}}  # 通过widget插件生成标签
        {{form.gaojie}}
        <input type="submit" value="提交" />
    </form>
    
    

注意:用户点击按钮提交数据一定要加{% csrf_token %}

1.1.2 form组件生成默认值

  • 给单个字段生成默认值【定义字段时初始化】
    class MyForm(Form):
       username = forms.CharField(required=True,initial="默认值")
       email = forms.CharField(required=True,initial="默认值")
    
  • 批量生成默认值【实例化form对象时初始化】
    form = UserForm(initial={“username”:"张欣怡","email":"843615824@qq.com"})
    

1.1.3 验证规则

  • 内置required=True
  • 自定义验证规则【有两种】
    1.正则表达式
    from django.core.validators import RegexValidator
    
    class UserForm(forms.Form):
       user = forms.CharField(label="用户名", max_length=32)
       pwd = forms.CharField(label="密码", max_length=32)
       mobile = forms.IntegerField(
           label="手机号",
           required=True, 
           validators=[RegexValidator(r'^\d{11}$', "手机号格式错误"), ],),  # 正则校验
       	widget=forms.TextInput,
    
    2.钩子方法【Hook方法】
    from django.core.validators import RegexValidator
    from django.core.exceptions import  ValidationError
    
    class UserForm(forms.Form):
        user = forms.CharField(label="用户名", max_length=32)
        pwd = forms.CharField(label="密码", max_length=32)
        mobile = forms.IntegerField(
            label="手机号",
            required=True, 
            validators=[RegexValidator(r'^\d{11}$', "手机号格式错误"), ],),
            widget=forms.TextInput,
        
        # 当内置校验规则和正则校验完成之后执行钩子方法
        def clean_mobile(self):
            # 通过前两种校验规则的数据会放到cleaned_data中,比如我们拿出来去数据库进行校验;
            value = self.cleaned_data["mobile"] 
            """比如将用户的手机号和数据库中的手机号进行校验"""    
            # 不存在,触发异常
            raise ValidationError("异常信息")
    

注意

假设我们定义了内置校验required=True以及正则定义规则以及钩子方法等三种定义规则,那么当用户点击按钮提交数据时,

假设其中的一种校验规则没有通过,那么就不会执行其他的校验规则

比如:用户名设定了required=True【表示必填】规则,但当用户没有填写用户名,填写完其他的表单之后,即使点击了提交按钮,required=True开始校验就没通过,那么接下里的校验也不会执行,更别说if form.is_valid():中的 逻辑代码了,因为if form.is_valid():的执行才是各个字段校验的开始

其他注意点

  • 1.用户要提交数据,必须要有{% csrf_token %}
    eg:
     <form method="post">
         {% csrf_token %}
         {{ form.username }}
         {{ form.pwd }}
         <input type="submit" value="提交">
     </form>
    
  • 2.用户提交后数据,通过request.POST获取
  • 3.required = True
    意味游览器会帮我们校验对应表单,如果没有写对应的数据,游览器就会检测出来,并不会
    向后台发送网络请求
    如果不想游览器对表单中的数据进行必填校验,应使用novalidate
    假设:1.设定了required = True,当对应表单未填
    <form method="post" novalidate>
        {% csrf_token %}
        {{ form.username }}
        {{ form.pwd }}
        <input type="submit" value="提交">
    </form>
    
  • 4.校验成功后的值和未成功的值如何取呢?
    def login(request):
     if request.method == 'GET':
         form = LoginForm()
         return render(request, "login.html", {"form": form})
     
     # 将用户提交给你的数据交给你定义的组件,组件根据自己字段去提取数据,最后调用内部的required=True机制对他生成校验
     form = LoginForm(data=request.POST)
     if form.is_valid():
         print(form.cleaned_data) # 获取校验成功后的值
         return HttpResponse("成功")
     else:
         print(form.errors)      # 获取报错信息
         return HttpResponse("失败")
    
  • 5.内置的错误提示修改为中文
    settings.LANGUAGE_CODE = 'zh-hans'  # 中文
    settings.LANGUAGE_CODE = 'en-us'    # 英文
    
  • 6.生成的html有默认的id名,为id_字段名,修改的方法如下
     widget=forms.TextInput(attrs={"id":"phone_id"})
    

1.1.4 错误信息

错误数据怎么来的
form.errors的理解

if form.is_valid(): # 根据校验规则对每个字段进行校验
    pass
如果有错误信息就会把它放到自己字段的errors中,最后校验完之后,把所有的errors再汇总到form.errors中

简单示例:
urls

from django.urls import path
from api import views
urlpatterns = [
    path("login/",views.login)
]

views

from django.shortcuts import render, HttpResponse
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms


class UserForm(forms.Form):
    user = forms.CharField(label="用户名", max_length=32, required=True, widget=forms.TextInput)
    pwd = forms.CharField(label="密码", max_length=32, widget=forms.PasswordInput)
    mobile = forms.CharField(
        label="手机号",
        required=True,
        validators=[RegexValidator(r'^\d{11}$', "手机号格式错误")],
        widget=forms.TextInput
    )

    def clean_mobile(self):
        # 通过前两种校验规则的数据会放到cleaned_data中,比如我们拿出来去数据库进行校验;
        value = self.cleaned_data["mobile"] 
        """比如将用户的手机号和数据库中的手机号进行校验"""    
        # 不存在,触发异常
        raise ValidationError("异常信息")


def login(request):
    # 1.用户请求进来
    if request.method == "GET":
        # 实例化form组件对象
        form = UserForm()
        # 给用户返回相应的页面,并将form对象相应的传递过去
        return render(request, "login.html", {"form": form})

    form = UserForm(request.POST)
    if form.is_valid():
        print(form.cleaned_data)
        return HttpResponse("登录成功")
    else:
        print(form.errors)
    	return render(request, "login.html", {"form": form})  # 此时校验失败,但是form组件中已经有了提交的数据

templates/login.html

<div>
    <h1>用户登录</h1>
    <form method="post" novalidate>
        {% csrf_token %}
        # form.字段  -> 标签控件【根据widget插件生成】,本质上是对象
        # form.字段.errors -> 该字段校验的失败的错误信息【列表】
       
        {{ form.user.label }}{{ form.user }}{{ form.user.errors.0 }} # 拿取user字段校验错误列表中的第一个信息
        {{ form.pwd.label }}{{ form.pwd }}{{ form.pwd.errors.0 }}    # 拿取pwd字段校验错误列表中的第一个信息
        {{form.mobile.label}}{{form.mobile}}{{form.mobile.errors.0}}#拿取mobile字段校验错误列表中的第一个信息
        <input type="submit" value="提交">
    </form>
</div>

form.erors输出到底是什么呢

  • 表面是标签
    模版渲染时,在html中
    1.当每个字段进行校验的时候,如果报错,会将错误信息构成字典,放到每个字段的errors中  
    eg:user字段报错 -> {“user”:["xx错误"]}   
    eg: pwd字段报错 -> {"pwd":["xxx错误"]}
    
    2.最后每个字段校验完成后会把所有的错误在放到errors字段中
    errors = {“user”:["xx错误"],"pwd":["xxx错误"]}
    
    解释说明键对应的值为什么列表
    因为validators中里面可以定义多个校验规则,可能会有多个错误信息,所以是列表
    
  • 真正是个对象嘿嘿,怎么看呢?
    print(type(form.errors))  # <class 'django.forms.utils.ErrorDict'>
    那我们就可以 from django.forms.utils import ErrorDict
    

这个在循环生成所有的字段就能体现出来了哦!

1.2 展示所有的字段

1.2.1 手动生成

class UserForm(forms.Form):
    user = forms.CharField(label="用户",required=True,widget=forms.TextInput)
    pwd = forms.CharField(label="密码", max_length=32, widget=forms.PasswordInput)
    phone = forms.CharField(
        label="手机号",
        required=True,
        validators=[RegexValidator(r'^\d{11}$', "手机号格式错误"), ],
        widget=forms.TextInput(attrs={"id": "phone_id"}),
    )

{{ form.user.label }}{{ form.user }}{{ form.user.errors.0 }}
{{ form.pwd.label }}{{ form.pwd }}{{ form.pwd.errors.0 }}   
{{form.mobile.label}}{{form.mobile}}{{form.mobile.errors.0}}

1.2.2 循环生成

涉及到对象和可迭代对象。

class UserForm(forms.Form):
    user = forms.CharField(label="用户",required=True,widget=forms.TextInput)
    pwd = forms.CharField(label="密码", max_length=32, widget=forms.PasswordInput)
    phone = forms.CharField(
        label="手机号",
        required=True,
        validators=[RegexValidator(r'^\d{11}$', "手机号格式错误"), ],
        widget=forms.TextInput(attrs={"id": "phone_id"}),
    )

    
item->每个字段 item.errors -> 每个字段中对应的列表【错误信息】 item.errors.0 -> 每个字段中对应列表中的第一个错误信息 

item.label -> 动态生成字段标签  item -> 标签控件   item.errors.0 -> 每个字段对应的第一条错误信息

{% for item in form %}
            {{ item.label }}{{ item }}{{ item.errors.0 }}  
{% endfor %}

手动&自动显示字段的难点

相同点

form.字段 = item

form.字段.label = item.label

form.字段.errors = item.errors

再来简单看看不同
手动生成字段

{{ form.user.label }}{{ form.user }}{{ form.user.errors.0 }}
{{ form.pwd.label }}{{ form.pwd }}{{ form.pwd.errors.0 }}   
{{form.mobile.label}}{{form.mobile}}{{form.mobile.errors.0}}

自动生成字段

{% for item in form %}
            {{ item.label }}{{ item }}{{ item.errors.0 }}  
{% endfor %}

1.3 样式相关

光目前动态生成的字段多少有点丑,那我们就可以添加一些前端框架的属性让我们的页面变的更加好看!
样式嘛,本质上都是在给widget插件中添加attrs属性。eg:

widget=forms.TextInput(attrs={"class":"form-control"}) # <input type="text" class="form-control"/>
  • 手动操作【给特定字段添加attrs属性】

    class UserForm(forms.Form):
     user = forms.CharField(
         label="用户名", 
         max_length=32, 
         required=True, 
         widget=forms.TextInput(attrs={"class": "form-control"})
     )
     pwd = forms.CharField(
         label="密码", 
         max_length=32, 
         widget=forms.PasswordInput(attrs={"class": "form-control"})
     )
     mobile = forms.CharField(
         label="手机号",
         required=True,
         validators=[RegexValidator(r'^\d{11}$', "手机号格式错误")],
         widget=forms.TextInput(attrs={"class":"form-control"})
     )
     ...
     ....
     .....
    
    {% for field in form %}
     <p>{{ field.label }} {{ field }} {{ field.errors.0 }}  </p>
    {% endfor %}
    
  • 自动操作(找到每个字段中的widget插件,再找到插件中的attrs属性,进行
    更新{"class":"form-control"}
    注意

    field.widget.attrs = ({"class":"form-control"}) # 这个是重新赋值【会把原来的attrs属性给覆盖掉】

    field.widget.attrs.update({"class":"form-control"}) # 这个才是更新,相同键不同值就更新值,没有就直接添加

    class BaseForm(RenderableFormMixin):
    def __init__():
        # 构造字典{"v1":对象,"v2":对象}
        # {"v1":forms.CharField(...,widget=forms.TextInput),"v2":.....对象}
        self.fields = copy.deepcopy() 
     
    class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass):
       pass
    
    class UserForm(forms.Form):
       user = forms.CharField(
           label="用户名", 
           max_length=32, 
           required=True, 
           widget=forms.TextInput
       )
       pwd = forms.CharField(
           label="密码", 
           max_length=32, 
           widget=forms.PasswordInput
       )
       mobile = forms.CharField(
           label="手机号",
           required=True,
           validators=[RegexValidator(r'^\d{11}$', "手机号格式错误")],
           widget=forms.TextInput
       )
       
       def __init__(self,*args,**kwargs):
           # 通过执行BaseForm类中的__init__方法实现
           super().__init__(self,*args,**kwargs) # 此时父类中就有self.fields = {}
           
           # 稍微作了一些条件的限制 ,当然我只是写了示例,可以自定义的!
           for name,field in self.fields.items():
               if name == "user":    # 如果是user字段就不给加form-control
                   continue 
               if field.widget.attrs:  # 有attrs属性就给做修改
                   field.widget.attrs.update({"class":"form-control"}) # 做修改
               else:       			# 没有就赋值
               	field.widget.attrs = {"class":"form-control"}  # 覆盖原来的
    
    form = RegisterForm()                   # __init__  实例化
    form = RegisterForm(data=request.POST)  # __init__  实例化
    

上面就是对展示所有字段的两种方式的介绍
但是,就下面这段代码这么写,每次,都要深浅拷贝就很烦,所以我搞了一个通用的父类,拿过来就能使用

 def __init__(self,*args,**kwargs):
        # 通过执行BaseForm类中的__init__方法实现
        super().__init__(self,*args,**kwargs) # 此时父类中就有self.fields = {}
        
        # 稍微作了一些条件的限制 ,当然我只是写了示例,可以自定义的!
        for name,field in self.fields.items():
            if name == "user":    # 如果是user字段就不给加form-control
                continue 
            if field.widget.attrs:  # 有attrs属性就给做修改
                field.widget.attrs.update({"class":"form-control"}) # 做修改
            else:       			# 没有就赋值
            	field.widget.attrs = {"class":"form-control"}  # 覆盖原来的

1.4 通用父类

  • 推荐
    class BootStrapForm(object):
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
          for name, field in self.fields.items():
              field.widget.attrs = {"class": "form-control"}
    
    
    class LoginForm(BootStrapForm, forms.Form):
      user = forms.CharField(label="用户名", widget=forms.TextInput)
      pwd = forms.CharField(label="密码", widget=forms.PasswordInput)
    
    
    def login(request):
      form = LoginForm()    # 实例化组件对象
      return render(request, "login.html", {"form": form})
    
    它的思路
    1.当实例化组件的时候,自动先执行BootStrapForm的__init__方法,
    2.然后里面再super【按照mro的继承关系继续向上寻找上一级】 
    3.将所有的键和字段对象打包成字典赋值给self.fields,即
    self.fields = {
        "user":forms.CharField(.......widget=forms.TextInput),
        "pwd":forms.CharField(.......widget=forms.PasswordInput),
        "mobile":forms.CharField(.......widget=forms.TextInput)
    }
    
  • 不推荐(多了一个继承关系)
    class BootStrapForm(forms.Form):
       def __init__(self, *args, **kwargs):
           # 不是找父类
           # 根据类的mro(继承关系),去找上个类
           # super().__init__(*args, **kwargs)
           for name, field in self.fields.items():
               field.widget.attrs = {"class": "form-control"}
    
    class LoginForm(BootStrapForm):
       user = forms.CharField(label="用户名", widget=forms.TextInput)
       pwd = forms.CharField(label="密码", widget=forms.TextInput)
    
    def login(request):
       form = LoginForm()
       return render(request, "login.html", {"form": form})
    

这块呢,又涉及到类的内部继承关系,来了解一下这个到底是个什么东西!

1.5 类的继承关系

类的内部继承关系,是基于c3算法来玩的。

class A:
    super().__init__()  # 就会调用<class '__main__.object'>的__init__方法

class B:
    super().__init__()  # 就会调用<class '__main__.A'>的__init__方法

class C(B,A):
    super().__init__()  # 就会调用<class '__main__.B'>的__init__方法

# [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
print(C.mro())

1.6 登录页面

知识点:基于Form组件 + 自定义的通用父类 + Boostarp cdn

Boostarp cdn 的网址

https://www.bootcdn.cn/twitter-bootstrap/

实现页面如下

实现代码

  • urls

    from django.urls import path
    from api import views
    
    urlpatterns = [
       path("login/",views.login)
    ]
    
  • views

    from django import forms
    from django.shortcuts import render, HttpResponse
    
    
    class BootstarpForm(object):
       def __init__(self, *args, **kwargs):
           super().__init__(*args, **kwargs)
           for name, field in self.fields.items():
               field.widget.attrs.update({"class": "form-control"})
    
    
    class LoginForm(BootstarpForm, forms.Form):
       user = forms.CharField(
           label="用户名",
           required=True,
           max_length=32,
           widget=forms.TextInput)
       pwd = forms.CharField(
           label="密码",
           required=True,
           max_length=32,
           widget=forms.TextInput
       )
    
    
    def login(request):
       if request.method == 'GET':
           form = LoginForm()
           return render(request, "login.html", {"form": form})
    
       form = LoginForm(request.POST)
       if form.is_valid():
           return HttpResponse("登录成功")
       else:
           print(form.errors)
           return render(request, "login.html", {"form": form})
    
  • login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.css" rel="stylesheet">
    </head>
    <body>
    <div style="width: 300px;margin: 100px auto">
        <h1>用户登录</h1>
        <form method="post" novalidate>
            {% csrf_token %}
            {% for item in form %}
                {{ item.label }}{{ item }}{{ item.errors.0 }}
            {% endfor %}
        <input  type="submit" value="提交">
        </form>
    </div>
    </body>
    </html>
    

1.2ModelForm组件

Form和ModelForm的区别【也是最明显的区别】

Form:需要用手动生成字段
ModelForm:基于model的字段自动生成form中的字段

使用form

  • 创建Form类 + 定义字段
    from django import forms
    
    class LoginForm(forms.Form):
        user = forms.CharField(label="用户名", widget=forms.TextInput)
        pwd = forms.CharField(label="密码", widget=forms.TextInput)
    
  • 视图
    def login(request):
        if request.method == "GET":
            form = LoginForm()
            return render(request, "login.html", {"form": form})
        form = LoginForm(data=request.POST)
        if not form.is_valid():
            # 校验失败
            return render(request, "login.html", {"form": form})
        print(form.cleaned_data)
        # ...
        return HttpRespon("OK")
    
  • 前端
    <form method="post" novalidate>
         {% csrf_token %}
         {% for field in form %}
             <p>{{ field.label }} {{ field }} {{ field.errors.0 }}</p>
         {% endfor %}
         <input type="submit" value="提交">
    </form>
    
    

使用ModelForm

  • 创建Form类 + 定义字段
    from django import forms
    
    class LoginForm(forms.Form):
       user = forms.CharField(label="用户名", widget=forms.TextInput)
       pwd = forms.CharField(label="密码", widget=forms.TextInput)
    
  • 通过orm类,创建相应的表和字段【执行下面的命令】
    python manage.py makemigrations
    python manage.py migrate
    
  • 视图 【创建ModelForm + 业务逻辑】
    class LoginForm(forms.ModelForm):
     # 可以自定义字段
     mobile = forms.CharFiled(label="手机号")
     #如果要校验,就要重写数据中生成的字段
     mobile = forms.CharFiled(label="手机号",validators=[RegexValidator(r'^\d{11}$', "手机号格式错误")]) 
     
     # 直接从数据库中拿取生成字段
     class Meta:
         model = models.UserInfo
         fileds = ["name","age", "mobile"]
         # 定义插件 -> 指定字段使用的 HTML 控件类型
         widgets = {
             "name":forms.TextInput,
             "age":forms.TextInput,
         }
         # 更改字段的标签名称
         labels ={
             "name":"v1",
             "age":"age1",
         }
    
  • 前端
    <form method="post" novalidate>
       {% for field in form %}
           {% csrf_token %}
           <p>{{ field.label }} {{ field }} {{ field.errors.0 }}</p>
           <input type="submit" value="提交">
       {% endfor %}
    </form>
    

注意:

  • 后续进行增删改查是基于数据库Models中的某个表,推荐使用:ModelForm;
  • 如果要进行表单校验是与数据库的表无关直接使用Form。

1.2.1 ModelForm比较好的地方

一 初始化数据

  • form
    class LoginForm(BootStrapForm, forms.Form):
       user = forms.CharField(label="用户名", widget=forms.TextInput)
       pwd = forms.CharField(label="密码", widget=forms.TextInput)
    
    def login(request):
       if request.method == "GET":
           form = LoginForm(initial={"user": "老高", "pwd": "123"})
           return render(request, "login.html", {"form": form})
    
  • ModelForm
    class User(models.Model):
       username = models.CharField(verbose_name="用户名", max_length=32)
       age = models.IntegerField(verbose_name="年龄")
    
    class LoginModelForm(BootStrapForm, forms.ModelForm):
     mobile = forms.CharField(label="手机号", widget=forms.TextInput)
    
         class Meta:
             model = models.UserInfo
             fields = ["username", "age", "mobile"]
             widgets = {
                 "age": forms.TextInput,  # 可以修改age对应的插件
             }
             labels = {
                 "age": "x2",  # 可以修改age在页面上展示的label
             }
     	
         # 钩子方法
         def clean_name(self):
             value = self.cleaned_data['name']
             # raise ValidationError("....")
             return value
    
    def login(request):
       user_object = models.UserInfo.objects.filter(id=1).first()
       form = LoginModelForm(instance=user_object, initial={"mobile": "老高"})
       return render(request, "login.html", {"form": form})
    

二.新建数据

  • form组件
    def login(request):
         if request.method == "GET":
         	form = LoginForm()
         	return render(request, "login.html", {"form": form})
         form = LoginForm(data=request.POST)
         if not form.is_valid():
             return render(request, "login.html", {"form": form})
         
         # form.cleaned_data
         # 手动读取字典,保存至数据库
         # models.UserInfo.objects.create(name=form.cleaned_data['xx'], pwd=form.cleaned_data['yy'])
         return HttpResponse("成功")
    
  • ModelForm组件
    def login(request):
         if request.method == "GET":
         	form = LoginForm()
         	return render(request, "login.html", {"form": form})
         
         form = LoginForm(data=request.POST)
         if not form.is_valid():
             return render(request, "login.html", {"form": form})
         
         form.save() # 自动将数据新增到数据库
         return HttpResponse("成功")
    

注意两个问题

  • 假设表单在数据库中有三个字段,但是在前端只显示了2个,提交之后,对于【sqllite】,剩下这个字段会为空

    但是对于【mysql】遇到这种问题,默认不能为空,所以form.save()会报错

    # 方法一:
        允许为空 null=True,blank=True
    # 方法二: 
        form.instance.数据库字段名(phone) = 值
        form.save()
    
  • 在自定义ModelForm中除了数据库中的字段,还自定义了字段,但是数据库中没有,即使用户输入,后台也会忽略

    class UserAddModelForm(forms.ModelForm):
        address = forms.CharField(label="地址")
        class Meta:
            model = models.User
            fields = "__all__" # 数据库和上面自定义的字段都生成标签
    

三.更新数据

  • form组件
    def login(request):
       if request.method == "GET":
       	form = LoginForm(initial={"user": "武沛齐", "pwd": "123"})
       	return render(request, "login.html", {"form": form})
       form = LoginForm(data=request.POST)
       if not form.is_valid():
           return render(request, "login.html", {"form": form})
       
       # form.cleaned_data
       # 手动读取字典,保存至数据库
       models.UserInfo.objects.filter(id=1).update(name=form.cleaned_data['xx'],pwd=form.cleaned_data['y'])]
       return HttpResponse("成功")   
    
  • ModelForm组件

    模拟场景:用户根据不同的id访问不同的页面,相当于点击了编辑按钮,然后表单中会显 示对应用户在数据库中的数据,当用户修改完成之后,点击提交按钮,将数据更新在数据库中。

    def login(request):
       if request.method == "GET":
       	# 这两行代码:将用户在数据库中数据在表单进行显示
           user_object = model.UserInfo.object.filter(id=1).first()
           form = UserEditModelForm(instance=user_object)
       	return render(request, "login.html", {"form": form})
      
       user_object = model.UserInfo.object.filter(id=1).first()
        # 更新必须要有对象 instance = 某一行对象   
        # instance=user_object ——>告诉django该表单主要是用来更新已有的用户对象,而不是创建新的对象。
       form = LoginModelForm(data=request.POST, instance=user_object)
       if not form.is_valid():
           return render(request, "login.html", {"form": form})
       
       # save方法更新或创建取决于上面是否有instance参数  有 ——> 更新  没 -> 创建
       form.save() # 更新到数据库
       return HttpResponse("成功")
    
posted @   清风拂山岗(小高同学)  阅读(20)  评论(0)    收藏  举报
编辑推荐:
· 为什么说方法的参数最好不要超过4个?
· C#.Net 筑基-优雅 LINQ 的查询艺术
· 一个自认为理想主义者的程序员,写了5年公众号、博客的初衷
· 大数据高并发核心场景实战,数据持久化之冷热分离
· 运维排查 | SaltStack 远程命令执行中文乱码问题
阅读排行:
· 博客园众包平台:诚征3D影像景深延拓实时处理方案(预算8-15万)
· 为什么说方法的参数最好不要超过4个?
· 发布一个小功能,通过 markdown 图片语法嵌入B站视频
· 《HelloGitHub》第 111 期
· Spring AI Alibaba 1.0 正式发布!核心特性速览+老项目升级指南
点击右上角即可分享
微信分享提示