专题18:Django之Form,ModelForm

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

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

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

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

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

1、Form

在views.py中:

# MyForm中继承的Form是Django自带的
class MyForm(forms):   
    # widget=forms.Input表示可以在HTML页面中可以显示成input框
    user = forms.CharField(widget=forms.Input)  
    pwd = forms.CharField(widget=forms.Input)
    email = forms.CharField(widget=forms.Input)


def user_add():
    if method.request == "GET":
        form = MyForm()  # 实例化一个MyForm对象
        return render(request, "user_add.html", {"form": form})
    
    

前端user_add.html中:

<form method="post" action="/user/add/">
    {{ form.user }}
    {{ form.pwd }}
    {{ form.email }}
</form>

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

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

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

<form method="post" action="/user/add/">
    {% for field in form %}
        {{ field }}
    {% endfor %}
</form>

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

1)使用ModelForm增加用户

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

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

class UserInfo(models.Model):
    """员工表"""
    name = models.CharField(verbose_name="姓名", max_length=16)
    password = models.CharField(verbose_name="密码", max_length=64)
    age = models.IntegerField(verbose_name="年龄")
    account = models.DecimalField(verbose_name="工资", max_digits=10, decimal_places=2, default=0)

    create_time = models.DateField(verbose_name="入职时间")

    gender_choices = (
        (1, "男"),
        (2, "女")
    )
    # gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)

    gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)

    depart = models.ForeignKey(verbose_name="部门", to="Department", to_field="id",
                               null=True, blank=True, on_delete=models.SET_NULL)

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

views.py中:

from django import forms

# 注意:如果要用ModelForm,则前面必须写这个类
class MyModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        # 这里fields这个列表就把models.py中的UserInfo表中的字段关联到了ModelForm这里
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]


def user_add():
    if method.request == "GET":
        form = MyModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_add.html", {"form": form})

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

from django import forms

class MyModelForm(forms.ModelForm):
    xxx = forms.CharField("...")
    class Meta:
        model = UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart", "xxx"]

def user_add():
    if method.request == "GET":
        form = MyModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_add.html", {"form": form})

实操:

views.py中:

from django import forms

class UserModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]

def user_model_form_add():
    if method.request == "GET":
        form = UserModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_model_form_add.html", {"form": form})

user_model_form_add.html中:

<form method="post" action="/user/model/form/add/">
    {% csrf_token %}
    {% for field in form %}
        {{ field.label }} :   {{ field }}
    {% endfor %}
</form>

 运行后页面显示如下:

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

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

from django.db import models

class Department(models.Model):
    """部门表"""
    # title是部门的名字,eg.title=招标部
    title = models.CharField(verbose_name="标题", max_length=32)


class UserInfo(models.Model):
    """员工表"""
    name = models.CharField(verbose_name="姓名", max_length=16)
    password = models.CharField(verbose_name="密码", max_length=64)
    age = models.IntegerField(verbose_name="年龄")
    account = models.DecimalField(verbose_name="工资", max_digits=10, decimal_places=2, default=0)

    create_time = models.DateField(verbose_name="入职时间")

    gender_choices = (
        (1, "男"),
        (2, "女")
    )

    gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)

    depart = models.ForeignKey(verbose_name="部门", to="Department", to_field="id",
                               null=True, blank=True, on_delete=models.SET_NULL)

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

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

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

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

 前端user_model_form_add.html中:

{% extends 'layout.html' %}
{% load static %}

{% block css %}
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
{% endblock %}

{% block content %}
    <div>
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">新建用户</h3>
            </div>
            <div class="panel-body">
                <form method="post" action="/user/model/form/add/" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
{#                        框外文字: 框内文字#}
                        <div>{{ field.label }}: {{ field }}</div>
                        <span style="color: red">{{ field.errors.0 }}</span>

                    {% endfor %}
                    <button type="submit" class="btn btn-primary">保 存</button>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

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

from django import forms

class UserModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
        widgets = {
            "name": TextInput(attrs={"class": "form-control"}),
            "password": PasswordInput(attrs={"class": "form-control"})
        }

def user_model_form_add():
    if method.request == "GET":
        form = UserModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_model_form_add.html", {"form": form})

页面如下:

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

from django import forms

class UserModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加class="form-control"样式
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "place-holder": field.label}

def user_model_form_add():
    if method.request == "GET":
        form = UserModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_model_form_add.html", {"form": form})

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

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

from django import forms

class UserModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加class="form-control"样式
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "place-holder": field.label}

def user_model_form_add():
    if method.request == "GET":
        form = UserModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_model_form_add.html", {"form": form})
    elif method.request == "POST":
        form = UserModelForm(data=request.POST)
        # 如果校验成功,可以获取到form.cleaned_data,返回一个字典
        # 如果数据合法,我们将这个新增的用户数据保存到数据库,这里注意:我们不再需要用ORM语句了(虽然用也可以),因为ModelForm知道我们获取用户数据可能会将其保存进数据库,所以我们直接调用form.save()就可以了,数据会直接保存到UserInfo这个表中,因为在上面的ModelForm中我们定义了model = models.UserInfo
        if form.is_valid():
            # print(form.cleaned_data)
            form.save()
            return redirect("/user/list/")
        # 如果校验失败,则可以获取到它所有的错误信息
        else:
            # print(form.errors)
            return render(request, 'user_model_form_add.html', {"form": form})
            

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

{% extends 'layout.html' %}
{% load static %}

{% block css %}
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
{% endblock %}

{% block content %}
    <div>
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">新建用户</h3>
            </div>
            <div class="panel-body">
                <form method="post" action="/user/model/form/add/" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
{#                        框外文字: 框内文字#}
                        <div>{{ field.label }}: {{ field }}</div>
                        <span style="color: red">{{ field.errors.0 }}</span>

                    {% endfor %}
                    <button type="submit" class="btn btn-primary">保 存</button>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

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

from django import forms

class UserModelForm(forms.ModelForm):

    name = forms.CharField(min_length=3, label="姓名")
    class Meta:
        model = models.UserInfo
        fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 循环找到所有插件,添加class="form-control"样式
        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "place-holder": field.label}

def user_model_form_add():
    if method.request == "GET":
        form = UserModelForm()  # 实例化一个MyModelForm对象
        return render(request, "user_model_form_add.html", {"form": form})
    elif method.request == "POST":
        form = UserModelForm(data=request.POST)
        # 如果校验成功,可以获取到form.cleaned_data,返回一个字典
        # 如果数据合法,我们将这个新增的用户数据保存到数据库,这里注意:我们不再需要用ORM语句了(虽然用也可以),因为ModelForm知道我们获取用户数据可能会将其保存进数据库,所以我们直接调用form.save()就可以了,数据会直接保存到UserInfo这个表中,因为在上面的ModelForm中我们定义了model = models.UserInfo
        if form.is_valid():
            # print(form.cleaned_data)
            form.save()
            return redirect("/user/list/")
        # 如果校验失败,则可以获取到它所有的错误信息
        else:
            # print(form.errors)
            return render(request, 'user_model_form_add.html', {"form": form})
            

2)使用ModelForm编辑用户信息

{% extends 'layout.html' %}

{% block content %}
    <div>
    <div class="container">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">编辑用户</h3>
            </div>
            <div class="panel-body">
                <form method="post" novalidate>
                    {% csrf_token %}
                    {% for field in form %}
                        <div>{{ field.label }}: {{ field }}</div>
                        <span style="color: red">{{ field.errors.0 }}</span>

                    {% endfor %}
                    <button type="submit" class="btn btn-primary">保 存</button>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}
def user_edit(request, nid):
    """编辑用户"""
    # 根据ID获取到需要编辑的那一行的数据(对象)
    row_object = models.UserInfo.objects.filter(id=nid).first()
    if request.method == 'GET':
        # 在ModelForm中写instance=row_object,则Django会默认将该对象中的每一个值写在输入框中
        form = UserModelForm(instance=row_object)
        return render(request, 'user_edit.html', {"form": form})

    # 把用户post的数据更新到row_object这一行,并赋值给form
    elif request.method == "POST":
        form = UserModelForm(data=request.POST, instance=row_object)
        if form.is_valid():
            form.save()
            return redirect('/user/list/')
        else:
            return render(request, 'user_edit.html', {"form": form})

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值