• 专题18:Django之Form,ModelForm


    原始思路实现添加用户功能的缺点:

    1)用户提交的数据没有校验

    2)如果用户输入的数据有错误,没有错误提示

    3)前端页面上的每一个字段都需要我们重新写一次

    4)关联的数据需要手动取获取并循环展示在页面

    1、Form

    在views.py中:

    1. # MyForm中继承的Form是Django自带的
    2. class MyForm(forms):
    3. # widget=forms.Input表示可以在HTML页面中可以显示成input框
    4. user = forms.CharField(widget=forms.Input)
    5. pwd = forms.CharField(widget=forms.Input)
    6. email = forms.CharField(widget=forms.Input)
    7. def user_add():
    8. if method.request == "GET":
    9. form = MyForm() # 实例化一个MyForm对象
    10. return render(request, "user_add.html", {"form": form})

    前端user_add.html中:

    1. <form method="post" action="/user/add/">
    2. {{ form.user }}
    3. {{ form.pwd }}
    4. {{ form.email }}
    5. form>

     上面代码相当于如下代码,两者比较,明显上面使用了form的代码更加简洁

    1. <form method="post" action="/user/add/">
    2. <input type="text" class="form-control" placeholder="请输入用户名称" name="user">
    3. <input type="text" class="form-control" placeholder="请输入密码" name="pwd">
    4. <input type="text" class="form-control" placeholder="请输入邮箱" name="email">
    5. form>

    还有一种比上面更简洁的写法,直接用for循环遍历form

    1. <form method="post" action="/user/add/">
    2. {% for field in form %}
    3. {{ field }}
    4. {% endfor %}
    5. form>

    2、ModelForm(对某些数据库的表做增删改查,推荐用ModelForm)

    1)使用ModelForm增加用户

    上面的views.py的方法其实也有很麻烦的地方,就是如果我要用字段的话,那我就要在MyForm这个类中把所要用的字段都手动写一遍,如果字段很多的话,就比较麻烦,有没有更加简单的办法呢?

    答案是有的,注意看MyForm里面的字段是不是和models.py中类的字段很相似?看下图:

    1. class UserInfo(models.Model):
    2. """员工表"""
    3. name = models.CharField(verbose_name="姓名", max_length=16)
    4. password = models.CharField(verbose_name="密码", max_length=64)
    5. age = models.IntegerField(verbose_name="年龄")
    6. account = models.DecimalField(verbose_name="工资", max_digits=10, decimal_places=2, default=0)
    7. create_time = models.DateField(verbose_name="入职时间")
    8. gender_choices = (
    9. (1, "男"),
    10. (2, "女")
    11. )
    12. # gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
    13. gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
    14. depart = models.ForeignKey(verbose_name="部门", to="Department", to_field="id",
    15. null=True, blank=True, on_delete=models.SET_NULL)

    其实,它们之间的内部之间是有关联的,我们如果使用ModelForm的话,可以这样写:

    views.py中:

    1. from django import forms
    2. # 注意:如果要用ModelForm,则前面必须写这个类
    3. class MyModelForm(forms.ModelForm):
    4. class Meta:
    5. model = models.UserInfo
    6. # 这里fields这个列表就把models.py中的UserInfo表中的字段关联到了ModelForm这里
    7. fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    8. def user_add():
    9. if method.request == "GET":
    10. form = MyModelForm() # 实例化一个MyModelForm对象
    11. return render(request, "user_add.html", {"form": form})

    同时,ModelForm中的fields列表里的字段不仅可以是数据库的字段,也可以是我们自定义的字段,而新字段相当于新加字段到表中。如下所示:

    1. from django import forms
    2. class MyModelForm(forms.ModelForm):
    3. xxx = forms.CharField("...")
    4. class Meta:
    5. model = UserInfo
    6. fields = ["name", "password", "age", "account", "create_time", "gender", "depart", "xxx"]
    7. def user_add():
    8. if method.request == "GET":
    9. form = MyModelForm() # 实例化一个MyModelForm对象
    10. return render(request, "user_add.html", {"form": form})

    实操:

    views.py中:

    1. from django import forms
    2. class UserModelForm(forms.ModelForm):
    3. class Meta:
    4. model = models.UserInfo
    5. fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    6. def user_model_form_add():
    7. if method.request == "GET":
    8. form = UserModelForm() # 实例化一个MyModelForm对象
    9. return render(request, "user_model_form_add.html", {"form": form})

    user_model_form_add.html中:

    1. <form method="post" action="/user/model/form/add/">
    2. {% csrf_token %}
    3. {% for field in form %}
    4. {{ field.label }} : {{ field }}
    5. {% endfor %}
    6. form>

     运行后页面显示如下:

    那么问题来了,为什么部门这块显示的不是部门名称,而是一个个Department类的对象呢? 

    首先,我们来分析一下models.py中的代码:

    1. from django.db import models
    2. class Department(models.Model):
    3. """部门表"""
    4. # title是部门的名字,eg.title=招标部
    5. title = models.CharField(verbose_name="标题", max_length=32)
    6. class UserInfo(models.Model):
    7. """员工表"""
    8. name = models.CharField(verbose_name="姓名", max_length=16)
    9. password = models.CharField(verbose_name="密码", max_length=64)
    10. age = models.IntegerField(verbose_name="年龄")
    11. account = models.DecimalField(verbose_name="工资", max_digits=10, decimal_places=2, default=0)
    12. create_time = models.DateField(verbose_name="入职时间")
    13. gender_choices = (
    14. (1, "男"),
    15. (2, "女")
    16. )
    17. gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
    18. depart = models.ForeignKey(verbose_name="部门", to="Department", to_field="id",
    19. null=True, blank=True, on_delete=models.SET_NULL)

    从上面代码中可以看出,models.py中的depart变量其实是从Department表中取到的一连串对象,前端页面显示的正是这一个个Department类的实例化对象。可是我们要让前端页面显示具体的部门名称,所以这里要引入一个知识点:

    当我们定义一个类,并且实例化一个该类的对象时,我们可以在类中定义一个__str()__,这样当我们打印这个对象时,输出的就是__str()__中return的值,比如:而这个页面的问题,也可以用这种方法,也就是在Department表中定义一个__str()__方法,return的就是self.title。代码如下:

    1. class Department(models.Model):
    2. """部门表"""
    3. # title是部门的名字,eg.title=招标部
    4. title = models.CharField(verbose_name="标题", max_length=32)
    5. def __str__(self):
    6. return self.title

    上面的问题解决了,接下来,我们需要给这些输入框增加样式。具体怎么做呢?

     前端user_model_form_add.html中:

    1. {% extends 'layout.html' %}
    2. {% load static %}
    3. {% block css %}
    4. <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
    5. {% endblock %}
    6. {% block content %}
    7. <div>
    8. <div class="container">
    9. <div class="panel panel-default">
    10. <div class="panel-heading">
    11. <h3 class="panel-title">新建用户h3>
    12. div>
    13. <div class="panel-body">
    14. <form method="post" action="/user/model/form/add/" novalidate>
    15. {% csrf_token %}
    16. {% for field in form %}
    17. {# 框外文字: 框内文字#}
    18. <div>{{ field.label }}: {{ field }}div>
    19. <span style="color: red">{{ field.errors.0 }}span>
    20. {% endfor %}
    21. <button type="submit" class="btn btn-primary">保 存button>
    22. form>
    23. div>
    24. div>
    25. div>
    26. div>
    27. {% endblock %}

    上面的代码中,样式唯一缺的就是form-control,我们要想办法把这个样式加上。具体时在views.py中通过widgets来加:

    1. from django import forms
    2. class UserModelForm(forms.ModelForm):
    3. class Meta:
    4. model = models.UserInfo
    5. fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    6. widgets = {
    7. "name": TextInput(attrs={"class": "form-control"}),
    8. "password": PasswordInput(attrs={"class": "form-control"})
    9. }
    10. def user_model_form_add():
    11. if method.request == "GET":
    12. form = UserModelForm() # 实例化一个MyModelForm对象
    13. return render(request, "user_model_form_add.html", {"form": form})

    页面如下:

    这种方法虽然可以,但是当字段比较多的时候,像这样一个一个的写就会比较麻烦。在我们真正做开发的时候,肯定不会像这样一个一个的写,我们通常这样写:

    1. from django import forms
    2. class UserModelForm(forms.ModelForm):
    3. class Meta:
    4. model = models.UserInfo
    5. fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    6. def __init__(self, *args, **kwargs):
    7. super().__init__(*args, **kwargs)
    8. # 循环找到所有插件,添加class="form-control"样式
    9. for name, field in self.fields.items():
    10. field.widget.attrs = {"class": "form-control", "place-holder": field.label}
    11. def user_model_form_add():
    12. if method.request == "GET":
    13. form = UserModelForm() # 实例化一个MyModelForm对象
    14. return render(request, "user_model_form_add.html", {"form": form})

    如果我想让某个字段(比如age)不加样式,可以循环中判断:if name == "age": continue

    那么如何对用户输入的数据进行校验呢?

    1. from django import forms
    2. class UserModelForm(forms.ModelForm):
    3. class Meta:
    4. model = models.UserInfo
    5. fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    6. def __init__(self, *args, **kwargs):
    7. super().__init__(*args, **kwargs)
    8. # 循环找到所有插件,添加class="form-control"样式
    9. for name, field in self.fields.items():
    10. field.widget.attrs = {"class": "form-control", "place-holder": field.label}
    11. def user_model_form_add():
    12. if method.request == "GET":
    13. form = UserModelForm() # 实例化一个MyModelForm对象
    14. return render(request, "user_model_form_add.html", {"form": form})
    15. elif method.request == "POST":
    16. form = UserModelForm(data=request.POST)
    17. # 如果校验成功,可以获取到form.cleaned_data,返回一个字典
    18. # 如果数据合法,我们将这个新增的用户数据保存到数据库,这里注意:我们不再需要用ORM语句了(虽然用也可以),因为ModelForm知道我们获取用户数据可能会将其保存进数据库,所以我们直接调用form.save()就可以了,数据会直接保存到UserInfo这个表中,因为在上面的ModelForm中我们定义了model = models.UserInfo
    19. if form.is_valid():
    20. # print(form.cleaned_data)
    21. form.save()
    22. return redirect("/user/list/")
    23. # 如果校验失败,则可以获取到它所有的错误信息
    24. else:
    25. # print(form.errors)
    26. return render(request, 'user_model_form_add.html', {"form": form})

    前端页面通过field.errors.0来显示错误信息,field.errors是一个列表,我们只要显示这个列表的第0个元素就行。

    1. {% extends 'layout.html' %}
    2. {% load static %}
    3. {% block css %}
    4. <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
    5. {% endblock %}
    6. {% block content %}
    7. <div>
    8. <div class="container">
    9. <div class="panel panel-default">
    10. <div class="panel-heading">
    11. <h3 class="panel-title">新建用户h3>
    12. div>
    13. <div class="panel-body">
    14. <form method="post" action="/user/model/form/add/" novalidate>
    15. {% csrf_token %}
    16. {% for field in form %}
    17. {# 框外文字: 框内文字#}
    18. <div>{{ field.label }}: {{ field }}div>
    19. <span style="color: red">{{ field.errors.0 }}span>
    20. {% endfor %}
    21. <button type="submit" class="btn btn-primary">保 存button>
    22. form>
    23. div>
    24. div>
    25. div>
    26. div>
    27. {% endblock %}

     以上的代码只能校验输入内容是否为空,那比如,我想规定姓名的长度必须大于3,那么我们需要在ModelForm的类中额外规定一下:name = forms.CharField(min_length=3, label="姓名")

    1. from django import forms
    2. class UserModelForm(forms.ModelForm):
    3. name = forms.CharField(min_length=3, label="姓名")
    4. class Meta:
    5. model = models.UserInfo
    6. fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    7. def __init__(self, *args, **kwargs):
    8. super().__init__(*args, **kwargs)
    9. # 循环找到所有插件,添加class="form-control"样式
    10. for name, field in self.fields.items():
    11. field.widget.attrs = {"class": "form-control", "place-holder": field.label}
    12. def user_model_form_add():
    13. if method.request == "GET":
    14. form = UserModelForm() # 实例化一个MyModelForm对象
    15. return render(request, "user_model_form_add.html", {"form": form})
    16. elif method.request == "POST":
    17. form = UserModelForm(data=request.POST)
    18. # 如果校验成功,可以获取到form.cleaned_data,返回一个字典
    19. # 如果数据合法,我们将这个新增的用户数据保存到数据库,这里注意:我们不再需要用ORM语句了(虽然用也可以),因为ModelForm知道我们获取用户数据可能会将其保存进数据库,所以我们直接调用form.save()就可以了,数据会直接保存到UserInfo这个表中,因为在上面的ModelForm中我们定义了model = models.UserInfo
    20. if form.is_valid():
    21. # print(form.cleaned_data)
    22. form.save()
    23. return redirect("/user/list/")
    24. # 如果校验失败,则可以获取到它所有的错误信息
    25. else:
    26. # print(form.errors)
    27. return render(request, 'user_model_form_add.html', {"form": form})

    2)使用ModelForm编辑用户信息

    1. {% extends 'layout.html' %}
    2. {% block content %}
    3. <div>
    4. <div class="container">
    5. <div class="panel panel-default">
    6. <div class="panel-heading">
    7. <h3 class="panel-title">编辑用户h3>
    8. div>
    9. <div class="panel-body">
    10. <form method="post" novalidate>
    11. {% csrf_token %}
    12. {% for field in form %}
    13. <div>{{ field.label }}: {{ field }}div>
    14. <span style="color: red">{{ field.errors.0 }}span>
    15. {% endfor %}
    16. <button type="submit" class="btn btn-primary">保 存button>
    17. form>
    18. div>
    19. div>
    20. div>
    21. div>
    22. {% endblock %}
    1. def user_edit(request, nid):
    2. """编辑用户"""
    3. # 根据ID获取到需要编辑的那一行的数据(对象)
    4. row_object = models.UserInfo.objects.filter(id=nid).first()
    5. if request.method == 'GET':
    6. # 在ModelForm中写instance=row_object,则Django会默认将该对象中的每一个值写在输入框中
    7. form = UserModelForm(instance=row_object)
    8. return render(request, 'user_edit.html', {"form": form})
    9. # 把用户post的数据更新到row_object这一行,并赋值给form
    10. elif request.method == "POST":
    11. form = UserModelForm(data=request.POST, instance=row_object)
    12. if form.is_valid():
    13. form.save()
    14. return redirect('/user/list/')
    15. else:
    16. return render(request, 'user_edit.html', {"form": form})

  • 相关阅读:
    使用react-grid-layout和echarts-for-react实现一个支持拖拽的自定义响应式dashboard页面
    NVIDIA DPU — BlueField DPU Hardware
    平安人寿“内鬼”泄露近4万条公民信息
    反向散射耦合RFID系统的原理及特点,带你更深入的了解
    飞书事件订阅的应用
    动态内存管理
    TCP/IP协议
    MySQL数据库————存储过程和函数
    技术干货 | GreatDB新一代读写分离架构,如何炼就近乎0损耗的性能?
    使用逆滤波算法deconvwnr恢复图像回复图像时,产生了很多横竖条纹。解决办法
  • 原文地址:https://blog.csdn.net/Vincent_Zhang233/article/details/127926745