欢迎来到赛兔子家园

Django中Modelform组件

Modelforms组件主要作用
  • 数据校验
  • 生成html标签
  • 前端页面显示错误信息

原因:使用forms组件时,需要在类中自己写字段,而这些字段与models.py中已定义字段等重复了,因此完全没有必要重新写一遍字段。Modelform组件就是把model和form组合起来。

关于label

页面中展示的label内容,有4种展示方式,并且优先级不同:

  1. 模型类中字段没有添加verbose_name参数,且modelform类也没有指定labels属性,那么前端页面默认显示字段的首字母大写的字段名。这个优先级最低。
  2. 模型类中字段有verbose_name参数,前端默认显示verbose_name参数值。优先级适中。
  3. modelform类中定义了labels属性,那么它的优先级高。
  4. 如果重写了某个字段,然后在字段的配置中的label属性优先级最高。
class Author(models.Model):
    user = models.CharField(max_length=32, verbose_name='用户名')
    
class AuthorModelForm(forms.ModelForm):
    # 例如重写user字段
    user =  forms.CharField(
        label='用户名',   # 这里的label优先级最高
        widget=forms.widgets.PasswordInput(),  # 这里是为了让页面输入是密文
    )
    class Meta:
        model = Author
        fields = "__all__"
        labels = {
            "user": "第二优先级高的label"
        }
forms

empty_lable

通常对于外键字段,都会有个下拉框,没有设置默认值的话,就会默认显示empty_lable值,也就是你看到的:

修改默认empty_lable

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2023/4/12 16:02'
# software: PyCharm

from django import forms

from apps.web import models


class BookModelForm(forms.ModelForm):
    class Meta:
        model = models.Book
        fields = "__all__"
        exclude = ['pub_date']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 方式1
        # for field in self.fields.values():  # 所有字段有相同属性
        #     print(field)
        #     field.widget.attrs.update({"class": "form-control"})
        # 方式2
        for field in iter(self.fields):
            print(field)
            self.fields[field].widget.attrs.update({"class": "form-control"})

        self.fields["publish"].empty_label = "请选择"
forms

修改后:

 设置指定字段不可修改(disable)

class BookModelForm(forms.ModelForm):
    class Meta:
        model = models.Book
        fields = "__all__"
        widgets = {
            # 方法1 出版社字段禁止修改
            "publish":forms.widgets.Select(attrs={"disabled":"disabled"})
        }
        exclude = ["pub_date"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in iter(self.fields):
            self.fields[field].widget.attrs.update({"class": "form-control"})
        # 法2 title字段禁止修改
        self.fields['title'].widget.attrs.update({"disabled": "disabled"})
forms

禁止修改字段:

禁止修改字段指定默认值

视图中结合initial设置默认值,不然页面中input框中值为空

views.py

def add_book(request):
    pub_obj = Publish.objects.filter(pk=1)
    form_obj = bookmodelform.BookModelForm(initial={"title": "book1", 'publish': pub_obj.first()})
    if request.method == "POST":
        form_obj = bookmodelform.BookModelForm(request.POST)
        if form_obj.is_valid():
            form_obj.save()
            return HttpResponse("注册成功")
    return render(request, "add_model_book.html", {"form_obj": form_obj})
views

效果

关于help_text

forms.py

class BookModelForm(forms.ModelForm):
    class Meta:
        model = models.Book
        fields = "__all__"
        exclude = ["pub_date"]
        help_texts = {
            "title":"书籍名称",
        }


    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({"class":"form-control"})
            # 禁用每个字段的help_text
            field.help_text = ''
forms

initial

在视图类中,为input框设置初始值:

def add_book(request):
    pub_obj = Publish.objects.filter(pk=1)
    form_obj = bookmodelform.BookModelForm(initial={"title": "book1", 'publish': pub_obj.first()})
modelform处理下拉菜单

外键字段显示如下规则:

  1. 指定显示某个出版社
  2. 固定显示某个出版社
  3. 不显示某个出版社,但保存是要能正常保存

默认显示某个出版社

需求:用户不能选择其它出版社,只能是指定出版社

方法1:forms处理,指定city=北京的出版社

forms.py

class BookModelForm(forms.ModelForm):
    class Meta:
        model = models.Book
        fields = "__all__"
        exclude = ["pub_date"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
            field.error_messages.update({"required": "该字段不能为空"})
        # 固定在前端显示哪些字段
        self.fields['publish'].queryset = models.Publish.objects.filter(city="北京")
        # 如果你不想看到那个提示的 '---------' 就给下面的值设置为None
        self.fields['publish'].empty_label = None
        # 如果你不想让前端标签被选中,直接加disabled属性即可
        self.fields['publish'].widget.attrs.update({"disabled": "disabled"})
forms

方法2:views.py视图处理,接收到书籍对应出版社

def add(request):
    # 例如我们这里接收到传来的书籍对应的出版社,也就是添加的这本书必须属于某个出版社,或者让前端默认显示某个出版社
    pub_pk = 3
    pub_obj = Publish.objects.filter(pk=pub_pk)
    # initial参数实现指定出版社
    form_obj = bookmodelform.BookModelForm(initial={'publish': pub_obj.first()})
    if request.method == 'GET':
        return render(request, 'add.html', {"form_obj": form_obj})
    else:
        form_obj = bookmodelform.BookModelForm(request.POST)
        if form_obj.is_valid():
            # 为页面中没有填值的字段,这里额外的添加上也可以
            form_obj.instance.pub_date = datetime.datetime.now().strftime('%Y-%m-%d')
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request, 'add.html', {"form_obj": form_obj})
views

默认显示指定(多个)出版社

外键字段很多,但是我在视图中要根据条件,展示某些记录,就可以这么做。

views.py中调整

def add(request):
    # 例如我们这里接收到传来的书籍对应的出版社名字,也就是添加的这本书必须属于某几个出版社,然后让前端某人显示某几个出版社
    pub_address = '华山'
    pub_obj = Publish.objects.filter(city=pub_address)
    form_obj = bookmodelform.BookModelForm(initial={'publish': pub_obj})
    if request.method == 'GET':
        return render(request, 'add.html', {"form_obj": form_obj})
    else:
        form_obj = bookmodelform.BookModelForm(request.POST)
        if form_obj.is_valid():
            # 为页面中没有填值的字段,这里额外的添加上也可以
            form_obj.instance.pub_date = datetime.datetime.now().strftime('%Y-%m-%d')
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request, 'add.html', {"form_obj": form_obj})
views

默认不显示出版社,在保存时处理

参考处理日期字段的套路即可,就是页面压根不显示出版社信息,在后端保存书籍时,直接根据需求绑定对应的出版社。

views.py

def add(request):
    form_obj = BookModelForm()
    if request.method == 'GET':
        return render(request, 'add.html', {"form_obj": form_obj})
    else:
        form_obj = BookModelForm(request.POST)
        if form_obj.is_valid():
            # 为页面中没有填值的字段,这里额外的添加上也可以
            form_obj.instance.pub_date = datetime.datetime.now().strftime('%Y-%m-%d')
            # 获取从别处传来的出版社id
            pub_pk = 1
            form_obj.instance.publish = Publisher.objects.filter(pk=pub_pk).first()
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request, 'add.html', {"form_obj": form_obj})
views

forms.py

class BookModelForm(forms.ModelForm):

    class Meta:
        model = Book
        fields = '__all__'
        # widgets = {
        #     "pub_date": forms.widgets.DateInput(attrs={"type": 'date'})
        # }

        # 这里的日期每次都要选择,比较麻烦,又不是咱们这里重点,我就给它排除了,前端不展示了
        # 数据录入时单独处理
        exclude = ['pub_date', 'publish']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为所有的字段添加相同的属性
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
            field.error_messages.update({"required": "该字段不能为空"})
forms
modelform处理radio

解决modelform在处理radio时,显示------问题

方式1:forms.py中重写指定字段,例如leve1

models.py

class TestDemo(models.Model):
    name = models.CharField(max_length=32, unique=True)
    level = models.IntegerField(choices=((0, "普通会员"), (1, '黄金会员'), (2, "白金会员")), verbose_name='vip等级')

views.py

def add(request):
    form_obj = bookmodelform.TestModelForm()
    if request.method == 'GET':
        return render(request, 'add.html', {"form_obj": form_obj})
    else:
        form_obj = bookmodelform.TestModelForm(request.POST)
        if form_obj.is_valid():
            form_obj.cleaned_data.pop('level2')
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request, 'add.html', {"form_obj": form_obj})
views

forms.py

class TestModelForm(forms.ModelForm):
    # 重写leve1字段解决显示---问题
    level = forms.CharField(widget=forms.RadioSelect(choices=((0, "普通会员"), (1, "黄金会员"), (2, "白金会员"))))
    class Meta:
        model = models.TestDemo
        fields = ["name", "level"]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
            field.error_messages.update({"required": "该字段不能为空"})

        # 下面是控制页面标签的渲染效果
        self.fields['level'].widget.attrs.update({"class": "form-radio"})
forms

方式2:

models.py中指定default参数指

models.py

class TestDemo(models.Model):
    name = models.CharField(max_length=32, unique=True)
    level = models.IntegerField(choices=((0, "普通会员"), (1, '黄金会员'), (2, "白金会员")), verbose_name='vip等级',default=1) # 指定黄金会员为默认值

forms.py

class TestModelForm(forms.ModelForm):
    # level = forms.CharField(widget=forms.RadioSelect(choices=((0, "普通会员"), (1, "黄金会员"), (2, "白金会员"))))
    class Meta:
        model = models.TestDemo
        fields = ["name", "level"]
        widgets = {
            "level": forms.RadioSelect(
                attrs={"class": "form-radio"},
            )
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
            field.error_messages.update({"required": "该字段不能为空"})

        # 下面是控制页面标签的渲染效果
        self.fields['level'].widget.attrs.update({"class": "form-radio"})
forms
modelform处理日期时间字段

modelform对于日期时间字段的处理,要用到一个叫小组件,其实就是自定义一个类

示例:

models.py

from django.db import models

class TestDataDemo(models.Model):
    create_date = models.DateField(verbose_name="创建日期")
    update_date = models.DateTimeField(verbose_name="更新日期")

urls.py

from django.urls import path, re_path
from . import views

urlpatterns = [
    path('add1/', views.add1),
    re_path('edit1/(?P<pk>\d+)/$', views.edit1),
]

views.py

def add_date(request):
    form_obj = bookmodelform.Test1ModelForm()
    if request.method == 'GET':
        return render(request, 'add.html', {"form_obj": form_obj})
    else:
        form_obj = bookmodelform.Test1ModelForm(request.POST)
        if form_obj.is_valid():
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request, 'add.html', {"form_obj": form_obj})


def edit_date(request, pk):
    obj = TestDataDemo.objects.filter(id=pk).first()
    form_obj = bookmodelform.Test1ModelForm(instance=obj)

    if request.method == 'GET':
        return render(request, 'add.html', {"form_obj": form_obj})
    else:
        form_obj = bookmodelform.Test1ModelForm(request.POST, instance=obj)
        if form_obj.is_valid():
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request, 'add.html', {"form_obj": form_obj})
views

forms.py

from django import forms
from web import models

class DateInput(forms.DateInput):
    """ 自定义日期类 """
    input_type = "date"  # 日期 2022/9/2

class DateTimeInput(forms.DateTimeInput):
    """ 自定义日期时间类 """
    input_type = "datetime-local"  # 日期时间  2022/9/2 12:12

class Test1ModelForm(forms.ModelForm):
    create_date = forms.DateField(
        widget=DateInput(format='%Y-%m-%d'),
        label="日期"
    )
    update_date = forms.DateTimeField(
        widget=DateTimeInput(format="%Y-%m-%d %H:%M:%S"),
        label="日期时间"
    )

    class Meta:
        model = models.TestDataDemo
        fields = ['create_date', 'update_date']
        # widgets = {
        #     "create_date":forms.DateTimeInput(attrs={"type":"date"}),
        #     "update_date":forms.DateTimeInput(attrs={"type":"date"})
        # }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为所有的字段添加相同的属性
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
forms

add.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugin/bootstrap/css/bootstrap.min.css' %}">
{#    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">#}
    <style>
        input, ul, label {
            margin-top: 10px;
        }

        ul {
            padding: 0;
            display: flex;
            flex-direction: row;
        }

        ul li {

            list-style: none;
            padding: 5px;
        }

        .errorlist li {
            color: red;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 off-set-2">
            <form action='' method="post" novalidate>
                {% csrf_token %}
                {{ form_obj }}
                <input type="submit" class="btn btn-danger" />
            </form>
        </div>
    </div>
</div>

</body>

</html>
View Code
bootstrap-datepicker日期控件

引入文件:

# 日期组件样式
bootstrap-datepicker/css/bootstrap-datepicker.min.css
# 图标
font-awesome/css/font-awesome.min.css

# 日期组件js
bootstrap-datepicker/js/bootstrap-datepicker.min.js
bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js

示例:

add_book.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="{% static 'plugin/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
    <!--日期插件css样式-->
    <link rel="stylesheet" href="{% static 'plugin/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
    <link rel="stylesheet" href="{% static 'plugin/font-awesome/css/font-awesome.min.css' %}">  <!--图标-->

</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-8 off-set-2">
            <form action='' method="post" novalidate>
                {% csrf_token %}
                <div class="form-group">
                    <span>
                        <i class="fa fa-calendar" aria-hidden="true"></i>
                    </span>
                    <label for="{{ form_obj.create_date.id_for_label}}">{{ form_obj.create_date.label }}</label>
                    {{ form_obj.create_date }}
                </div>

                  <div class="form-group">
                    <span>
                        <i class="fa fa-calendar" aria-hidden="true"></i>
                    </span>
                    <label for="{{ form_obj.update_date.id_for_label}}">{{ form_obj.update_date.label }}</label>
                    {{ form_obj.update_date }}
                </div>
                
                <input type="submit" class="btn btn-danger"/>
            </form>
        </div>
    </div>
</div>


<script src="{% static 'js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'plugin/bootstrap/js/bootstrap.js' %}"></script>
<!--日期插件js-->
<script src="{% static 'plugin/bootstrap-datepicker/js/bootstrap-datepicker.min.js' %}"></script>
<script src="{% static 'plugin/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js' %}"></script>

<script>
    $(function () {
        initDatePicker(); //初始化
    });

    /*
      时间插件initDatePicker
       */
    function initDatePicker() {
        $("#id_create_date,#id_update_date").datepicker({
            format: 'yyyy-mm-dd',
            minView: 'month',
            startDate: '0',
            language: "zh-CN",
            autoclose: true,
        });

    }

</script>

</body>
</html>
add_book.html

forms.py

class Test2ModelForm(forms.ModelForm):
    class Meta:
        model = models.TestDataDemo
        fields = "__all__"
        labels = {
            "create_date":"创建日期",
            "update_date":"更新日期",
        }
        # 添加属性
        widgets = {
            "create_date": forms.DateTimeInput(attrs={"autocomplete": "off"}),
            "update_date": forms.DateTimeInput(attrs={"autocomplete": "off"}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
            field.widget.attrs['placeholder'] = "请输入{0}".format(field.label)
forms

views不变

ModelForm文件上传

modelform中怎么限制file字段上传文件的后缀?比如说一定要上传zip或者rar的压缩文件

示例:

首先项目根目录下创建media目录,然后在settings.py

# 上传文件
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

models.py

重点在这里,在validators列表中进行文件类型限制

from django.db import models

from django.core import validators

class Asset(models.Model):
    user = models.CharField(max_length=32, verbose_name='用户名')
    avatar = models.ImageField(
        upload_to='avatars/',
        verbose_name='用户头像',
        default='avatars/xxx.png',
        validators=[validators.FileExtensionValidator(['jpg', 'png', 'jpeg'])]
    )
    file = models.FileField(
        upload_to='files/',
        verbose_name='用户文件',
        default='files/xxx.txt',
        validators=[validators.FileExtensionValidator(['pdf', 'zip', 'rar'])]
    )

    def __str__(self):
        return self.user
models

forms.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'tian'
__data__ = '2022/9/28 17:16'
# software: PyCharm


from django import forms
from web import models


class FileModelForm(forms.ModelForm):
    class Meta:
        model = models.Asset
        fields = ["user", "avatar", "file"]
        error_messages = {
            "file": {"invalid_extension": "必须上传文件, 且上传文件的类型必须是 pdf zip rar"},
            "avatar": {"invalid_extension": "必须上传图片,且上传文件的类型必须是 jpg png jpeg"},
        }
        labels = {
            "file": "文件上传",
            "avatar": "图片上传"
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为所有字段添加相同属性
        for field in self.fields.values():
            field.widget.attrs.update({"class": "form-control"})
            field.error_messages.update({"required": "该字段不能为空"})
forms

views.py

def add_file(request):
    """添加用户信息"""
    if request.method == "GET":
        form_obj = filemodel.FileModelForm()
        return render(request, "add_user.html", {"form_obj": form_obj})
    else:
        form_obj = filemodel.FileModelForm(request.POST,request.FILES)
        if form_obj.is_valid():
            form_obj.save()
            return HttpResponse("OK")
        else:
            return render(request,"add_user.html",{"form_obj": form_obj})
views

html页面

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <style>
        ul li {
            color: red;
            list-style: none;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h3>文件上传</h3>
            <form action="" method="post" enctype="multipart/form-data" novalidate>
                {% csrf_token %}
                {{ form_obj }}
                <button type="submit" class="btn btn-danger">提交</button>
            </form>
        </div>
    </div>
</div>

</body>
<script src="{% static 'js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'plugin/bootstrap/js/bootstrap.min.js' %}"></script>
<script></script>
</html>
html

posted on 2022-09-27 12:26  赛兔子  阅读(1)  评论(0编辑  收藏  举报

导航