Django(十五)Form组件

参考博客:

https://www.cnblogs.com/haiyan123/p/7778888.html

 http://www.cnblogs.com/wupeiqi/articles/6144178.html

Form组件

    - 对用户请求的验证
        - AJax
        - Form
    - 生成HTML代码

    a. 创建一个类
    b. 类中创建字段(包含正则表达式)
    c. GET
        obj = Fr()
        obj.user = > 自动生成HTML
        
    d. POST
        obj = Fr(request.POST)
        if obj.is_valid():
            obj.cleaned_data
        else:
            obj.errors
            return .... obj

一、Form组件介绍

Form组件可以做的几件事情:

  1、用户请求数据验证

  2、自动生成错误信息    

  3、打包用户提交的正确信息

  4、如果其中有一个错误了,其他的正确这,保留上次输入的内容

  5、生成HTML标签

二、Form组件的使用

  1、创建规则

class Foo(Form): #必须继承
    username = xxx
    password = xxx
    email = xxx
注意这里的字段必须和input的name字段一致

  2、数据和规则进行匹配

先导入view.py

from django.forms import Form
from django.forms import fields
from django.forms import widgets
from django.shortcuts import render,redirect
from app01 import models
# Create your views here.
from django.forms import Form
from django.forms import fields
from django.forms import widgets
# 1、创建规则
class TeacherForm(Form):  #必须继承Form
    # 创建字段,本质上是正则表达式
    username = fields.CharField(
        required=True,     #必填字段
        error_messages={"required":"用户名不能为空!!"},  #显示中文错误提示
        widget=widgets.TextInput(attrs={"placeholder":"用户名","class":"form-control"})  #自动生成input框
       )
    password = fields.CharField(required=True, error_messages={'required': '密码不能为空'},
                                widget=widgets.TextInput(attrs={'placeholder': '密码', 'class': 'form-control'}))  # 不能为空
    email = fields.EmailField(
        required=True,
        error_messages={"required":"邮箱不能为空!!","invalid":"无效的邮箱"},
        widget=widgets.EmailInput(attrs={"placeholder": "邮箱", "class": "form-control"})  # 自动生成input框
    ) #不能为空且邮箱格式要一致

# 2、使用规则:将数据和规则进行匹配
def teacherindex(request):
    teacher_obj = models.UserInfo.objects.all()
    # print(teacher_obj)
    return render(request,"teacherindex.html",{"teacher_obj":teacher_obj})
def add(request):
    if request.method=="GET":
        form = TeacherForm()  #只是让显示一个input框
        return render(request,"add.html",{"form":form })
    else:
        form = TeacherForm(data=request.POST)
        # print(form)  #<QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
        if form.is_valid():# 开始验证
            # print('执行成功',form.cleaned_data)          # 所有匹配成功,字典
            # {'username': 'asd', 'password': 'sdf', 'email': 'sadf@live.com','ut_id':1}
            form.cleaned_data['ut_id'] = 1  #要分的清是班主任还是讲师
            models.UserInfo.objects.all().create(**form.cleaned_data)
            return redirect("/teacherindex/")
        else:
            # print("=====?",form.errors,type(form.errors))#返回失败的结果
            # print(form.errors["username"][0])   #拿到返回失败的结果,渲染到页面
            return render(request,"add.html",{"form":form})

html

{% block right %}
    <h1>添加老师信息</h1>
    <hr>
    <form method="post" novalidate>
        {% csrf_token %}
        <p>姓名:{{ form.username }}</p>{{ form.errors.username.0 }}
        <p>密码:{{ form.password }}</p>{{ form.errors.password.0 }}
        <p>邮箱:{{ form.email }}</p>{{ form.errors.email.0 }}
        <p><input type="submit" value="提交"></p>
    </form>
{% endblock %}

如果访问视图的是一个GET 请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一个访问该URL 时预期发生的情况。

如果表单的提交使用POST 请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:form = NameForm(request.POST)。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。

我们调用表单的is_valid()方法;如果它不为True,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。

如果is_valid()True,我们将能够在cleaned_data 属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。

注意: form = TeacherForm()  #没有参数,只是一个input框

    form = TeacherForm(data=request.POST) # 数据和规则放置一起 (添加的时候用)

    form = TeacherForm(initial={'username':obj.username,'password':obj.password,'email':obj.email})   # 显示input,并且将数据库中的默认值填写到input框中 (编辑的时候用)

简单实例(一)

class F1Form(forms.Form):
    user = fields.CharField(required=True,
                            max_length=18,
                            min_length=6,
                            error_messages={'required':'用户名不能为空','max_length': '太长了',
                                            'min_length':'太短了'},
                            widget=widgets.TextInput(attrs={'placeholder':"用户名"})
                            )
    pwd = fields.CharField(required=True, min_length=9, error_messages={'min_length': "太短了"})
    age = fields.IntegerField(required=True)
    email = fields.EmailField(required=True,min_length=8, error_messages={'invalid': "格式错误"})

def f1(request):
    if request.method == 'GET':
        obj = F1Form()
        return render(request, 'f1.html', {'obj': obj})
    if request.method == "POST":
        obj = F1Form(request.POST)
        if obj.is_valid():
            print("验证成功", obj.cleaned_data)
            return redirect('http://www.xiaohuar.com')
        else:
            print("验证失败", obj.errors)
            return render(request, 'f1.html', {"obj": obj})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/f1/" method="post" novalidate>
    {% csrf_token %}
    <p>{{ obj.user }}{{ obj.errors.user.0 }}</p>
    <p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p>
    <p>{{ obj.age }}{{ obj.errors.age.0 }}</p>
    <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
    <input type="submit" value="提交">
</form>
</body>
</html>
html

简单实例(二)

"""django_form URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', views.users),
    path('add_user/', views.add_user),
    re_path(r'edit_user-(d+)/', views.edit_user),
]
urls.py
from django.shortcuts import render, redirect
from app01 import models
from app01 import forms
# Create your views here.

def users(request):
    obj_list = models.UserInfo.objects.all()
    return render(request, "user.html", {"obj_list": obj_list})

def add_user(request):
    if request.method == "GET":
        obj = forms.UserForm()
        return render(request, "add_user.html", {"obj": obj})
    if request.method == "POST":
        obj = forms.UserForm(request.POST)
        if obj.is_valid():
            models.UserInfo.objects.create(**obj.cleaned_data)
            return redirect("/users/")
        else:
            return render(request, "add_user.html", {"obj": obj})

def edit_user(request,nid):
    if request.method == "GET":
        data = models.UserInfo.objects.filter(id=nid).first()
        obj = forms.UserForm({'username': data.username,'email': data.email})
        return render(request, "edit_user.html", {'obj': obj, 'nid': nid})
    else:
        obj = forms.UserForm(request.POST)
        if obj.is_valid():
            models.UserInfo.objects.filter(id=nid).update(**obj.cleaned_data)
            return redirect("/users/")
        else:
            return render(request, 'edit_user.html', {'obj': obj, 'nid': nid})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<a href="/add_user/">添加</a>

{% for item in obj_list %}
    <li>
        {{ item.id }}--{{ item.username }}--{{ item.email }}
        <a href="/edit_user-{{ item.id }}/">编辑</a>
    </li>
{% endfor %}

</body>
</html>
users.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/add_user/" method="post" novalidate>
    {% csrf_token %}
    <p>{{ obj.username }}{{ obj.errors.username.0 }}</p>
    <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
    <input type="submit" value="提交">
</form>

</body>
</html>
add_user.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/edit_user-{{ nid }}/" method="post" novalidate>
        {% csrf_token %}
        <p>{{ obj.username }}{{ obj.errors.username.0 }}</p>
        <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
        <input type="submit" value="提交">
    </form>
</body>
</html>
edit.html
from django import forms as dforms
from django.forms import fields

class UserForm(dforms.Form):
    username = fields.CharField()
    email = fields.EmailField()
forms.py

Widgets

每个表单字段都有一个对应的Widget 类,它对应一个HTML 表单Widget,例如<input type="text">

在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField 具有一个TextInput Widget,它在HTML 中生成一个<input type="text">

字段的数据

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

注:此时,你依然可以从request.POST 中直接访问到未验证的数据,但是访问验证后的数据更好一些。

在上面的联系表单示例中,is_married将是一个布尔值。类似地,IntegerField 和FloatField 字段分别将值转换为Python 的int 和float

实例:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
 
class MyForm(Form):
    user = fields.CharField(
        widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'})
    )
 
    gender = fields.ChoiceField(
        choices=((1, ''), (2, ''),),
        initial=2,
        widget=widgets.RadioSelect
    )
 
    city = fields.CharField(
        initial=2,
        widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
    )
 
    pwd = fields.CharField(
        widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
    )
创建form类
from django.shortcuts import render, redirect
from .forms import MyForm
 
 
def index(request):
    if request.method == "GET":
        obj = MyForm()
        return render(request, 'index.html', {'form': obj})
    elif request.method == "POST":
        obj = MyForm(request.POST, request.FILES)
        if obj.is_valid():
            values = obj.clean()
            print(values)
        else:
            errors = obj.errors
            print(errors)
        return render(request, 'index.html', {'form': obj})
    else:
        return redirect('http://www.google.com')
views函数处理
<form action="/" method="POST" enctype="multipart/form-data">
    <p>{{ form.user }} {{ form.user.errors }}</p>
    <p>{{ form.gender }} {{ form.gender.errors }}</p>
    <p>{{ form.city }} {{ form.city.errors }}</p>
    <p>{{ form.pwd }} {{ form.pwd.errors }}</p>
    <input type="submit"/>
</form>
生成HTML
        obj = forms.MyForm()
        txt = "<input type='text'/>"
        from django.utils.safestring import mark_safe
        txt = mark_safe(txt)
        return render(request, "index.html", {'obj': obj, 'txt':txt})

#或者在html文件加{{ txt|safe }},使用以上方式不需要再加'|safe'

Form类

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

1、Django内置字段如下:

  1 Field
  2     required=True,               是否允许为空
  3     widget=None,                 HTML插件
  4     label=None,                  用于生成Label标签或显示内容
  5     initial=None,                初始值
  6     help_text='',                帮助信息(在标签旁边显示)
  7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  8     show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
  9     validators=[],               自定义验证规则
 10     localize=False,              是否支持本地化
 11     disabled=False,              是否可以编辑
 12     label_suffix=None            Label内容后缀
 13  
 14  
 15 CharField(Field)
 16     max_length=None,             最大长度
 17     min_length=None,             最小长度
 18     strip=True                   是否移除用户输入空白
 19  
 20 IntegerField(Field)
 21     max_value=None,              最大值
 22     min_value=None,              最小值
 23  
 24 FloatField(IntegerField)
 25     ...
 26  
 27 DecimalField(IntegerField)
 28     max_value=None,              最大值
 29     min_value=None,              最小值
 30     max_digits=None,             总长度
 31     decimal_places=None,         小数位长度
 32  
 33 BaseTemporalField(Field)
 34     input_formats=None          时间格式化   
 35  
 36 DateField(BaseTemporalField)    格式:2015-09-01
 37 TimeField(BaseTemporalField)    格式:11:12
 38 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 39  
 40 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
 41     ...
 42  
 43 RegexField(CharField)
 44     regex,                      自定制正则表达式
 45     max_length=None,            最大长度
 46     min_length=None,            最小长度
 47     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 48  
 49 EmailField(CharField)      
 50     ...
 51  
 52 FileField(Field)
 53     allow_empty_file=False     是否允许空文件
 54  
 55 ImageField(FileField)      
 56     ...
 57     注:需要PIL模块,pip3 install Pillow
 58     以上两个字典使用时,需要注意两点:
 59         - form表单中 enctype="multipart/form-data"
 60         - view函数中 obj = MyForm(request.POST, request.FILES)
 61  
 62 URLField(Field)
 63     ...
 64  
 65  
 66 BooleanField(Field)  
 67     ...
 68  
 69 NullBooleanField(BooleanField)
 70     ...
 71  
 72 ChoiceField(Field)
 73     ...
 74     choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
 75     required=True,             是否必填
 76     widget=None,               插件,默认select插件
 77     label=None,                Label内容
 78     initial=None,              初始值
 79     help_text='',              帮助提示
 80  
 81  
 82 ModelChoiceField(ChoiceField)
 83     ...                        django.forms.models.ModelChoiceField
 84     queryset,                  # 查询数据库中的数据
 85     empty_label="---------",   # 默认空显示内容
 86     to_field_name=None,        # HTML中value的值对应的字段
 87     limit_choices_to=None      # ModelForm中对queryset二次筛选
 88      
 89 ModelMultipleChoiceField(ModelChoiceField)
 90     ...                        django.forms.models.ModelMultipleChoiceField
 91  
 92  
 93      
 94 TypedChoiceField(ChoiceField)
 95     coerce = lambda val: val   对选中的值进行一次转换
 96     empty_value= ''            空值的默认值
 97  
 98 MultipleChoiceField(ChoiceField)
 99     ...
100  
101 TypedMultipleChoiceField(MultipleChoiceField)
102     coerce = lambda val: val   对选中的每一个值进行一次转换
103     empty_value= ''            空值的默认值
104  
105 ComboField(Field)
106     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
107                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
108  
109 MultiValueField(Field)
110     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
111  
112 SplitDateTimeField(MultiValueField)
113     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
114     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
115  
116 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
117     path,                      文件夹路径
118     match=None,                正则匹配
119     recursive=False,           递归下面的文件夹
120     allow_files=True,          允许文件
121     allow_folders=False,       允许文件夹
122     required=True,
123     widget=None,
124     label=None,
125     initial=None,
126     help_text=''
127  
128 GenericIPAddressField
129     protocol='both',           both,ipv4,ipv6支持的IP格式
130     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
131  
132 SlugField(CharField)           数字,字母,下划线,减号(连字符)
133     ...
134  
135 UUIDField(CharField)           uuid类型
136     ...

注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串

>>> import uuid

    # make a UUID based on the host ID and current time
    >>> uuid.uuid1()    # doctest: +SKIP
    UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

    # make a UUID using an MD5 hash of a namespace UUID and a name
    >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
    UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')

    # make a random UUID
    >>> uuid.uuid4()    # doctest: +SKIP
    UUID('16fd2706-8baf-433b-82eb-8c7fada847da')

    # make a UUID using a SHA-1 hash of a namespace UUID and a name
    >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
    UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')

    # make a UUID from a string of hex digits (braces and hyphens ignored)
    >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')

    # convert a UUID to a string of hex digits in standard form
    >>> str(x)
    '00010203-0405-0607-0809-0a0b0c0d0e0f'

    # get the raw 16 bytes of the UUID
    >>> x.bytes
    b'x00x01x02x03x04x05x06x07x08	
x0bx0c
x0ex0f'

    # make a UUID from a 16-byte string
    >>> uuid.UUID(bytes=x.bytes)
    UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
UUID

2、Django内置插件:

# 单radio,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单radio,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 单select,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单select,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多选select,值为列表
# user = fields.MultipleChoiceField(
#     choices=((1,'上海'),(2,'北京'),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 单checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, '上海'), (2, '北京'),),
#     widget=widgets.CheckboxSelectMultiple
# )

注意:

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
 
    user = fields.ChoiceField(
        # choices=((1, '上海'), (2, '北京'),),
        initial=2,
        widget=widgets.Select
    )
 
    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
        #
        self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

self.fields语句一定要在调用父类构造函数之后,因为调用父类构造函数之后才有字段fields

方式二:

使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

from django import forms
from django.forms import fields
from django.forms import widgets
from django.forms import models as form_model
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
 
class FInfo(forms.Form):
    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
Form重点:
    - 字段
        用于保存正则表达式
        ChoiceField *****
        MultipleChoiceField
        CharField
        IntegerField
        DecimalField
        DateField
        DateTimeField
        EmailField
        GenericIPAddressField
        FileField
        
        RegexField

实例

from django.shortcuts import render
from django import forms
from django.forms import fields
from django.forms import widgets
class TestForm(forms.Form):
    user = fields.CharField(
        required=True, # 是否必填
        max_length=12, # 最大长度
        min_length=3,  # 最小长度
        error_messages={}, # 错误提示
        #widget = widgets.Select(), # 定制HTML插件
        label='用户名',
        initial='请输入用户',
        help_text='asdfasdf',
        show_hidden_initial=False,
        # validators=[]
        disabled=True,
        label_suffix='->' # obj.as_p 会显示出来
    )
    age = fields.IntegerField(
        label='年龄',
        max_value= 12,
        min_value=5,
        error_messages={
            'max_value':'太大了'
        }
    )

    email = fields.EmailField(
        label='邮箱'
    )

    img = fields.FileField()

    city = fields.TypedChoiceField(
        coerce=lambda x: int(x),
        choices=[(1,'上海',),(2,'北京'),(3,'沙河'),],
        initial=2
    )
    bobby = fields.MultipleChoiceField(
        choices=[(1,'刚娘'),(2,'铁娘'),(3,'钢弹')],
        initial=[1,2]
    )

    xoo = fields.FilePathField(
        path='app01'
    )

def test(request):
    if request.method == 'GET':
        #obj = TestForm({'city':3})
        obj = TestForm()
        return render(request,'test.html',{'obj':obj})
    else:
        obj = TestForm(request.POST,request.FILES)
        obj.is_valid()
        print(obj.cleaned_data)
        return render(request, 'test.html', {'obj': obj})
views
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="POST" action="/test/" novalidate enctype="multipart/form-data">

        {% csrf_token %}
        <p>{{ obj.user.label }}{{ obj.user }}</p>
        <p>{{ obj.age.label }}{{ obj.age }}{{ obj.errors.age.0 }}</p>
        <p>{{ obj.email.label }}{{ obj.email }}</p>
        <p>{{ obj.img.label }}{{ obj.img }}</p>
        <p>{{ obj.city.label }}{{ obj.city }}</p>
        <p>{{ obj.bobby.label }}{{ obj.bobby }}</p>
        <p>{{ obj.xoo.label }}{{ obj.xoo }}</p>
        <input  type="submit" value="提交"/>
    </form>
</body>
</html>
html

ModelChoiceField

特殊的单选或多选时,数据源是否能实时更新?

ModelChoiceField获取的值可以实时更新

from app01 import models
from django.forms.models import ModelChoiceField
class LoveForm(forms.Form):
    price = fields.IntegerField()
    user_id = fields.IntegerField(
        # widget=widgets.Select(choices=[(0,'alex'),(1,'刘皓宸'),(2,'杨建'),])
        widget=widgets.Select()
    )

    user_id2 = ModelChoiceField(
        queryset=models.UserInfo.objects.all(),
        to_field_name='id'
    )

    def __init__(self,*args,**kwargs):
        # 拷贝所有的静态字段,复制给self.fields
        super(LoveForm,self).__init__(*args,**kwargs)
        self.fields['user_id'].widget.choices = models.UserInfo.objects.values_list('id', 'username')


def love(request):
    obj = LoveForm()
    return render(request,'love.html',{'obj':obj})
Views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>价格:{{ obj.price }}</p>
    <p>姓名:{{ obj.user_id }}</p>
    <p>姓名:{{ obj.user_id2 }}</p>
</body>
</html>
html

 注意:依赖models中的str方法

原文地址:https://www.cnblogs.com/xiangtingshen/p/10670522.html