Django Form组件

Django Form组件

​ 我们在编写向后台数据库提交数据的程序时一定会用到表单,因此需要在HTML页面设计form标签。我们会根据数据输入的需求与页面样式,在form标签中放置各种输入的标签,如input、select等。这会增加写前端页面代码的工作量,但这不是主要的,主要是页面利用表单向后端提交数据时,前后端都要写一些校验代码,比如校验用户是否输入、校验输入格式是否正确等。而且如果用户输入的内容有错误,就需要在页面相应的位置显示对应的错误信息,这不仅增加了代码量,而且许多代码是重复的。

​ Django Form组件的引入弥补了以上不足,它能自动在页面上生成可用的HTML标签,提供数据校验功能,提高代码的质量和效率。Django Form有两种,一种是Form组件,另一个是ModelForm组件。

前期环境准备

利用前面章节的test_orm项目和employee应用开发。

Django Form表单的主要功能

Django Form表单的主要功能是对表单字段进行集中管理,主要功能如下:

  1. 自动生成html表单元素,可减少前端代码编写。一方面字段类型生成默认标签,如CharField对应HTML中的input标签;另一方面可以通过widget属性来渲染成HTML元素,如forms.widgetsSelect()对应HTML中的select标签。
  2. 通过表单字段类型、属性的定义,自动产生校验表单数据的合法性的功能。如EmailField表示email字段,如果不是有效的邮箱会产生错误。
  3. 如果验证错误,将重新显示表单,已输入的数据不会被重置或清空,用户界面友好。
  4. Django Form可以用css、js进行渲染,是页面美观。

Django Form简单开发流程介绍

  • 编写Django Form类。
  • 建立URL和视图函数对应关系。
  • 在视图函数中实例化Django Form类。
  • 视图函数向模板文件发送Django Form实例化对象变量。
  • 模板文件以一定形式显示Django Form实例化对象中存储的字段信息。

编写Django Form对象类

Django Form对象一般用一个名称为forms.py集中存放,因此在应用employee下建立一个forms .py文件,在文件中定义一个test_form类:

# 导入表单相关模板
from django import forms
# 定义表单要继承Form类
class test_form(forms.Form):
    # lable是字段显示名称
    account = forms.CharField(label="账号")
    password = forms.CharField(label="密码")

说明:

  1. Form类必须直接或间接继承自django.forms.Form。
  2. Django Form对象封装了一系列字段和验证规则。
  3. test_from类中有两个字段,可以看出字段定义形式与Django orm中的Model类字段形式相似,括号内放置字段属性,这里的label可以在模板文件中取出来作为字段名称。

建立URL与视图函数对应关系

urlpatterns = [
    path('test_form',testform),
]

视图函数

在views中编写testform():

# 从当前目录导入forms文件 
from . import forms
def testform(request):
    if request.method == "POST":
        # 通过request.POST为test_form对象赋值
        test_form = forms.test_form(request.POST)
        # 表单校验功能
        if test_form.is_valid():
            # 校验通过的数据存放在cleaned_data中,是字典类型的。因此要用get函数取值。
            account = test_form.cleaned_data.get("account")
            pw = test_form.cleaned_data.get("password")
            if (account=='test' and pw=='123'):
                return HttpResponse("登陆成功")
            else:
                return HttpResponse("用户名或密码错误")
        else:
            return HttpResponse("数据输入不合法")
    # 初始化生成一个test_form对象
    test_form = forms.test_form()
    # 通过render()把testform表单对象传递给test_form
    return render(request,'test_form.html',{'testform':test_form})

说明:

由于视图函数要用到forms文件中定义的test_form视图,所以先导入。以上代码先判断提交方式是否为POST,如果是就说明有数据提交过来,就将POST对象中的相关数据赋值给test_form,然后用is_valid判断有效性。判断数据有效性是依据Form类中的字段类型和对字段的有效性约束,有效性约束是通过字段属性来实现的。

如果不是POST就说明是第一次打开网页,通过test_form = forms.test_form()初始化一个test_form对象存放到test_form,通过render函数将变量传递给test_form.html文件。

form.is_valid()返回true后,表单数据被存储在form.cleaned_data中,也就是说Django Form对象把经过校验的、合法的数据存放到表单对象的cleaned_data属性中。这个属性是字典类型,字典的键名就是Form中定义的字段名,字典键值就是提交的数据,因此可以通过get函数取得表单数据。

页面代码

test_form.html:

<body>
    <div align="center">
        <h1>
            测试
        </h1>
        <hr>
        <form action="/test_from/" method="POST">
            {% csrf_token %}
            <!-- 以p标签的形式显示表单的每一个字段 -->
            {{ testform.as_p }}
            <div>
                <input type="submit" value="登录">
            </div>
        </form>
    </div>
</body>

说明:

<form action="/test_from/" method="POST">是网页上的表单,与Django Form表单不同,一个属于前端一个属于后端,两者的对应关系通过模板语言产生。

为了安全,Django要求在表单中放置{% csrf_token %}用来防御CSRF攻击。

我们仅用{{ testform.as_p }}这一行代码就把Django Form表单放到网页,就能显示表单数据了,as_p把表单中每个字段放在p标签中。在模板中显示表单的3种方式:{{ from.as_table }}以表格形式显示表单数据,将字段放在tr标签中。{{ form.as_ul }}以ul标签显示,将字段放在li标签中。{{ form.as_p }}将字段放在p标签。

运行测试

Django Form字段

Django form字段定义形式与Django ORM字段定义形式相似,Django Form字段有许多属性与在页面上显示的样式有关,可以理解为这些属性能作为Django生成的HTML页面的样式。

Django Form 字段属性

Django Form 字段属性也是放在字段定义的括号内,下面是CharField字段的一个样例,括号内都是字段属性。

Studentinfo = forms.CharField(
	# 设置最小长度为10
	min_length=10,
	# 字段名称
	label="学生编码",
	# 设置初始值
	initial="1230000001",
	# 设置错误信息
	error_messages={
        "required":"不能为空",
        "invalid":"格式错误",
        "min_length":"编码最少十位"
    })
  1. 通用属性
  • label可以生成HTML文件中的label标签,或者通过模板变量把label的内容显示到HTML文件中,形如label="工资额"。

  • label_suffix指的是label内容后缀,默认为:。例如label="工资额"在HTML文件中可显示为工资额:。

  • initial可指定字段的初始值,形如initial=66。

  • help_text可以指定帮助信息或者字段的描述信息,可以通过一定方式显示在HTML文件中,形如help_text="输入手机号"。

  • error_messages指定错误信息,字典的键名是该类型字段的错误类型。

  • required指定字段是否可以为空,如required=True。

  • disabled指定字段可否被编辑。

  • widget可以指定字段的HTML标签形式。

    如果不指定widget,Django会根据字段的类型指定默认标签:

    class userinfo(forms.Form):
        name = forms.CharField(label='名字')
        url = forms.URLField(label='邮箱',required=False)
        comment = forms.CharField()
    

    在浏览器显示时,HTML网页中按字段类型使用默认标签,如CharField在网页上生成type="text"的input标签,URLField生成type="url"的input标签,主要形式如下:

    <p><label for="id_name">名字:</label><input type="text" name="name" required id="id_name"></p>
    <p><label for="id_url">邮箱:</label><input type="url" name="url" id="id_url"></p>
    <p><label for="id_comment">comment:</label><input type="text" name="comment" required id="id_comment"></p>
    

    如果指定了widget,就会按照widget指定的类型在HTML网页中生成对应的标签。

    pwd = forms.CharField(
    	min_length=8,
    	label="密码",
    	widget = forms.widget.PasswordInput(attrs={'class':'password'},render_value=True))
    

    以上代码通过widget指定字段的输入框类型为密码输入型,并通过attrs传递一个字典值以设置input标签的各种属性,这里设置标签的样式类为password。另一个参数render_value=True表示输入框校验不通过时,保留当前输入值。

    在浏览器显示时,在网页中生成type="password"的input标签:

    <p><label for="id_pwd">密码:</label><input type="password" name="pwd" class="password" minlength="8" required id="id_pwd"></p>
    

    字段的widget属性可以通过不同设置生成各类HTML标签。

    widget=forms.widgets.RadioSelect() # 生成type="radio"的input标签
    widget=forms.widgets.Select() # 生成select标签
    widget=forms.widgets.SelectMultiple() # 生成多选的select标签
    widget=forms.widgets.CheckboxInput() # 生成type="checkbox"的input标签字段特有属性
    
  1. 字段特有属性

除了所有字段都拥有的通用属性,还有一些属性属于某种类型字段自有的属性,称作特有属性:

  1. CharField字段属性如下。
  • min_length指定字段最小长度。
  • max_length指定最大长度。
  • strip指定是否清除用户输入的空白,当strip=True清除空白。
  1. 数值类型字段主要包括IntegerField、DecimalField、FloatField等类型字段,他们有如下属性:
  • max_value:指定字段最大值。
  • min_value:指定字段最小值。
  • max_digits:指定字段的总长度,是DecimalField类型独有的属性。
  • decimal_places:指定字段的小数位长度,DecimalField类型独有。
  1. ChoiceField字段属性说明如下。
  • choices限定字段值的可选项,用元组类型设置该属性,如choices=((0,'男'),(1,'女'),)。
  1. FileField字段属性说明如下。
  • allow_empty_file设置上传文件是否允许空文件,是布尔类型属性。

Django Form 常用字段

  1. CharField字符类型字段,其代码定义如下:
studentinfo = forms.CharField(
	min_length=10,
	label="学生编码",
	initial="1230000001",
	error_messages={
        "required":"不能为空",
        "invalid":"格式错误",
        "min_length":"编码最少10位"
    })
  1. IntegerField、DecimalField、FloatField是数值类型字段。
integ=forms.IntegerField(max_value=100,min_value=10)
fl=forms.FloatField(max_value=199.88,min_value=10.66)
de=forms.DecimalField(max_value=2199.88,min_value=210.37,max_digits=7,decimal_places=2)
  1. DateField、TimeField、DateTimeField是日期时间字段。
date=forms.DateField()   # 格式为yyyy-mm-dd
time=forms.TimeField()   # 格式为hh:mm
dt=forms.DateTimeField() # 格式为yyyy-mm-dd hh:mm
  1. ChoiceField字段,如果不指定widget,默认在页面上生成select标签。
sex = forms.ChoiceField(
	choices=((1,'男'),(2,'女'),),
)
  1. EmailField字段
url = forms.EmailField(label='邮箱',required=False)

样例6: Django Form组件开发

样例主要实现登录功能和账号信息的增删改查,实现这些功能的视图函数都是运用Django Form组件向模板文件传递数据,相应模板也引入了Bootstrap框架进行了美化。

在项目test_orm中创建一个新的应用test_form。

开发准备

建立数据库表

在models文件中编写代码:

from django.db import models
# 导入自定义模块,这个模块将上传文件按一定格式命名并存储
from utils.rename_upload import RenameUpload
class loguser(models.Model):
    account = models.CharField(max_length=32, verbose_name='登录账号')
    password = models.CharField(max_length=20, verbose_name='密码')
    email = models.EmailField(verbose_name='邮箱')
    gender = models.CharField(max_length=1)
    hobby = models.CharField(max_length=20)
    hair = models.CharField(max_length=1)
    img = models.ImageField(upload_to='image', storage=RenameUpload(),blank=True,null=True)

说明:

导入自定义模块RenameUpload,这个模块将上传文件按一定格式命名并存储。

最后一行代码增加了一个名为img的ImageField字段,用于存储上传图片的地址,并通过upload_to='image'指明上传图片的存放位置。upload_to与settings中的BASE_DIR和MEDIA_ROOT有关。BASE_DIR为/test_orm/,MEDIA_ROOT=os.path.join(BASE_DIR,'media'),也就是/test_orm/media/。因此这里图片存放的位置/test_orm/media/image/。在数据库表的ImageField字段只存储相对路径,这里的img保存在数据库表中的字段值形式为"image/文件名"。

输入python manage.py makemigrations和python manage.py migrate。

编写上传插件RenameUpload

数据模型loguser有一个字段是图像字段,因此在业务流程就有了上传图片文件的功能。上传文件多了可能会遇到文件名重复的情况,为了避免这一点,需要编写一个插件让上传文件按一定的格式命名,避免名字重复。

在/test_orm/目录上新建文件夹utils,在其下新建文件rename_upload.py:

# -*- coding: UTF-8 -*-
# 导入文件存储类
from django.core.files.storage import FileSystemStorage
class RenameUpload(FileSystemStorage):
    from django.conf import settings
    def __init__(self,location=settings.MEDIA_ROOT,base_url=settings.MEDIA_URL):
        # 初始化
        super(RenameUpload,self).__init__(location,base_url)
    # 重写_save()方法,参数name为上传文件名称
    def _save(self,name,content):
        # 导入要用到的模板:文件操作模块、时间模块、随机数模块
        import os,time,random
        # 取得文件扩展名
        ext = os.path.splitext(name)[1]
        # 取得文件所在的目录
        d = os.path.dirname(name)
        # 按照一定的格式命名文件,‘年月日时分秒-随机数’
        fn = time.strftime('%Y%m%d%H%M%S')
        fn = fn+'_%d' % random.randint(0,100)
        # 给文件加上扩展名形成完整的文件名
        name = os.path.join(d,fn+ext)
        # 调用父类方法
        return super(RenameUpload, self)._save(name,content)

说明:

代码建立了继承于FileSystemStorage类的RenameUpload,这个类有两个方法,一个是init方法,另一个是_save()方法,都是继承来的。

init方法主要把settings文件中涉及文件存放的两个变量MEDIA_ROOT、MEDIA_URL传递给location、base_url参数,这两个变量决定文件的保存位置和文件URL的前缀。

_save()重写父类的方法,主要功能是把文件改成年月日时分秒_随机数.扩展名的形式

settings文件中的配置

在settings中注册应用,指定上传图片、文件等的存放位置:

MEDIA_URL='/media/'
MEDIA_ROOT=os.path.join(BASE_DIR,'media')

安装插件

pip install pillow

图片文件路径的配置

要将图片显示在网页上,必须指定能够找到图片文件的路径,在urls中设置:

from django.urls import path,include
from test_form import views
# 导入静态服务函数
from django.conf.urls.static import static
# 导入settings文件
from . import settings
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',views.login),
# 用静态服务函数static()指定上传文件URL
] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

说明:

以上代码用static()函数指定一个对应关系,因此要先通过from django.conf.urls.static import static导入相关模块。由于用到settings中的变量,所以通过from . import settings导入配置。

通过前面的介绍可以知道settings.MEDIA_URL值为/media/,settings.MEDIA_ROOT的值为/test_orm/media/,这样就把前缀/media/与/test_orm/media/路径对应起来。也就是说如果HTML文件中有一个URL的前缀是/media/,就会被Django模板引擎解析为/test_orm/media/。

将static函数返回值加在urlpatterns的列表项后,即可实现静态文件路径设置:

  • static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

登录页面

编写登录Form

首先编写一个登录的Django Form,打开/test_orm/test_orm/forms.py文件,编写:

from django import forms
class login_form(forms.Form):
    account = forms.CharField(
        min_length=2,
        label='账号'
    )
    pwd = forms.CharField(
        # 要求密码长度最少为6位
        min_length=6,
        label='密码'
        # 设置标签为密码输入形式
        widget = forms.widgets.PasswordInput(attrs={'class':'password'},render_value=True)
    )

说明:

login_form继承于forms.Form,建立了两个字段,用于账号和密码输入。

以上代码定义了两个CharField字段,这个类型的字段如果不指定widget属性,在网页上将被Django生成input标签。

pwd字段通过forms.widgets.PasswordInput(attrs={'class':'password'},render_value=True)语句设置对应HTML页面上的type="password"的input标签,并且样式类为password;render_value=True表示如果输入有误也要保持输入框的内容不被清除。

编写登录视图函数

from django.shortcuts import render,redirect,HttpResponse
from . import forms
from . import models
def login(request):
    if request.method == 'POST':
        # 用POST提交的数值给From对象赋值
        from_obj = forms.login_form(request.POST)
        # 对提交的数据进行校验
        if from_obj.is_valid():
            account = from_obj.cleaned_data['account']
            pwd = from_obj.cleaned_data['pwd']
            user_obj = models.loguser.objects.filter(account=account,password=pwd).first()
            if user_obj:
                # 登录成功进入账号列表页面
                return redirect('/list_loguser/')
            else:
                # 用户或密码不对,把错误信息和Form对象传给login.html页面
                error = '用户或密码错误'
                return render(request,'login.html',{'form_obj':from_obj,'errmsg':error})
        else:
            # 未通过校验,把Form对象传给login.html页面
            return render(request,'login.html',{'form_obj':from_obj})
    # 第一次打开网页,先生成一个Form对象
    form_obj = forms.login_form()
    # 把Form对象传到页面文件
    return render(request,'login.html',{'from_obj':form_obj})

说明:

  • 首先判断页面请求方式,如果是POST请求,那么是页面向后端视图函数提交数据,一般正确取出数据分3步:
    1. 第一步用提交的数据给From对象赋值,提交的数据都存在request对象中,因此用from_obj=forms.login_form(request.POST)给form_obj对象赋值,这些数据是从前端网页上提交过来的。
    2. 第二步对提交数据进行校验,Form对象自动根据字段定义进行校验,判断校验是否成功用is_valid()函数。
    3. 提交的数据校验不合格或未通过,用render函数把现在的Form对象重新传递回页面。提交数据通过校验后,把数据取出来与数据库表loguser中的account、password值做对比。如果正确就表示登陆成功,跳转到账号列表页面(list_loguser.html).如果不正确,error变量赋值为错误提示信息,并通过render函数把错误信息和Form对象传递回页面。
    4. 如果不是POST请求,则是第一次打开网页,需初始化一个Form对象,传递给HTML页面。

编写HTML文件

<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>测试Django Form</title>
</head>
<body>
<div align="center">
	<h1>测试Django form</h1>
	<hr>
	<form action="/test_form/" method="post">
		{% csrf_token %}
<!--		以p标签显示表单字段-->
		{{ testform.as_p }}
		<div><input type="submit" value="登录"></div>
	</form>
</div>
</body>
</html>

测试

在urls中加入以下代码:

from django.contrib import admin
from django.urls import path,include
from test_form import views
urlpatterns = [
    ...
    path('login/',views.login),
]

登录页面改善

上面编写的登录页面是用Django Form自动生成的形式,生成的样式单一、不够美观,对其进行改善,采用Bootstrap框架改善。从网站的起步页面上找到登录页例子,打开这个页面、查看其源码并复制到login.html进行修改,修改后的代码如下:

{% load static %}
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>登录页面</title>
    <!-- Bootstrap core CSS -->
    <link rel="icon" href="{% static 'favicon.ico' %}"  >
    <link href="{% static 'bootstrap/css/bootstrap.min.css'%}" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fontawesome/css/font-awesome.min.css'%}">
    <!-- Custom styles for this template -->
    <link href="{% static 'signin.css' %}" rel="stylesheet">
</head>
<body>
<div class="container">

    <form action='/login/' method='post' class="form-signin">
        {% csrf_token %}
        <h2 class="form-signin-heading">请登录</h2>
        <label for="{{ form_obj.account.id_for_label }}" class="sr-only">{{ form_obj.account.label }}</label>
        {{ form_obj.account }}
        <br>
        <label for="{{ form_obj.pwd.id_for_label }}" class="sr-only">{{ form_obj.pwd.label }}</label>
        {{ form_obj.pwd }}
        <button class="btn btn-lg btn-primary btn-block" type="submit">登录</button>
    </form>
</div> <!-- /container -->
<script src="{% static 'jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'bootstrap/js/bootstrap.min.js'%}"></script>
</body>
</html>

说明:

  1. {% load static %}导入静态文件。head标签中引用的JS、CSS文件都下载并存放在静态文件夹下,这个标签中还引用

signin.css样式文件,在静态文件夹下新建这个文件,其中样式代码拷贝自登录页引用的signin.css文件。

  1. 下面介绍Form表单在HTML文件中有关模板变量的意义,这里假设视图函数传到HTML文件中的表单对象的变量名为form_obj,表单字段名为name。

    • {{ form_obj.as_table }}、{{ form_obj.as_ul }}、{{ form_obj.as_p }}:生成form_obj表单在页面上3种样式。
    • {{ form_obj.name }}:生成input、select等标签,标签的类型与name字段数据类型和widget属性有关。
    • {{ form_obj.name.label }}:在页面上生成的name字段label属性中指定字符串。
    • {{ form_obj.name.label_tag }}:直接生成一个label标签
    • {{ form_obj.name.id_for_label }}:生成字符串与{{ form_obj.name }}生成的标签的id值相同,用于label标签的for属性。
    • {{ form_obj.name.errors }}:生成name字段在后端验证后返回来的所有错误。
    • {{ form_obj.name.errors.0 }}:生成name字段在后端验证后返回来的第一个错误。
    • {{ form_obj.errors }}:返回form_obj表单级别上的所有错误。
  2. 根据上面的介绍,<label for="{{ form_obj.account.id_for_label }}''>被Django模板系统解析为<label for="id_account">,其中的for用来设置与label有对应关系的input标签的id,{{ form_obj.account.id_for_label }}可以取得这个id。{{ form_obj.account.label }}取得form_obj对象中account字段的label值,被Django模板系统解析为“账号”。{{ form_obj.account }}可以被模板语言系统解析为<input type="text" name="account" required id="id_account">。

    为了使Form表单字段文本框样式与Bootstrap样式相融合,我们需要通过表单字段的widget属性进行设置,修改后的login_form代码如下:

    from django import forms
    class login_form(forms.Form):
        account = forms.CharField(
            min_length=2,
            label='账号',
            # 设置的class样式为form_control,这个是Bootstrap样式
            widget=forms.widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入账号','autofocus':True})
        )
        pwd = forms.CharField(
            # 要求密码长度最少为6位
            min_length=6,
            label='密码',
            # 设置标签为密码输入形式
            widget = forms.widgets.PasswordInput(attrs={'class':'form-control','placeholder':'请输入密码'},render_value=True)
        )
    

    说明:

    account字段通过widget属性设置了TextInput类型,对应type="text"的input标签。并通过attrs={'class':'form-control','placeholder':'请输入账号','autofocus':True}设置标签样式类,是Bootstrap定义的样式类。PasswordInput对应type='password'的input标签。

列表页面

登录视图函数中如果登录信息输入正确,通过return redirect('list_loguser/')进入账号列表页面。

视图函数:

def list_loguser(request):
    users = models.loguser.objects.all()
    return render(request,'list_loguser.html',{'user_list':users})

在urls中加入路径path('list_loguser/',views.list_loguser),

页面文件list_loguser.html:

{% load static %}
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Django Form样例</title>
    <!-- Bootstrap core CSS -->
    <link rel="icon" href="{% static 'favicon.ico' %}"  >
    <link href="{% static 'bootstrap/css/bootstrap.min.css'%}" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fontawesome/css/font-awesome.min.css'%}">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-offset-2 col-md-8">
<div class="page-header">
  <h1> 样例首页<small>--账号信息列表</small></h1>
</div>
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">账号信息</h3> <!--这里加标题 //-->
                </div>
                <div class="panel-body"> <!--将表格放在这个<div class="panel-body">的标签中 //-->
                    <div class="row">
                        <div class="col-md-3 pull-right" style="margin-bottom:15px ">
                            <div><a href="/add_loguser/" class="btn btn-primary pull-right"><i
                                    class="fa fa-user-plus fa-fw"
                            ></i>&nbsp;增加</a><!--给增加记录按钮加bootstrap样式、fontawesome图标 //-->
                            </div>
                        </div>
                    </div>
                    <table class="table table-bordered table-condensed table-striped table-hover">
                        <!--给表格增加bootstrap样式 //-->
                        <thead>
                        <tr>
                            <th>账号</th>
                            <th>邮件</th>
                            <th>性别</th>
                            <th>爱好</th>
                            <th>头发数量</th>
                            <th colspan="2">操作</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for usr in usr_list %}

                        <tr>
                            <td>{{ usr.account }}</td>
                            <td>{{ usr.email }}</td>
                            <td>
                                {% if usr.gander == '1' %}
                                男
                                {% else %}
                                女
                                {% endif %}
                            </td>
                            <td>
                                 {% if usr.hobby == '1' %}
                                游泳
                                {% elif usr.hobby == '2' %}
                                自行车
                                {% else %}
                                跑酷
                                {% endif %}
                            </td>
                            <td>
                                 {% if usr.hair == '1' %}
                                很多
                                {% elif usr.hair == '2' %}
                                一般
                                {% else %}
                                很少
                                {% endif %}</td>

                            <td><a href="/del_loguser/{{ usr.id }}/" class="btn btn-danger"><i
                                    class="fa fa-trash-o fa-fw"
                                    aria-hidden="true"></i>&nbsp;删除</a>
                            </td>
                            <td><a href="/edit_loguser/{{ usr.id }}/" class="btn btn-info"><i
                                    class="fa fa-pencil-square-o"
                                    aria-hidden="true"></i>&nbsp;修改</a>
                            </td>
                        </tr>
                        {% empty %}
                        <tr>
                            <td colspan="7">无相关记录!</td>
                        </tr>
                        {% endfor %}
                        </tbody>
                    </table>

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

账号增加

定义Form类:

首先建立一个Form类,打开forms文件:

class loguser_form(forms.Form):
    # 定义id字段,用来保存数据库表的主键值,为了在修改时定位到某条记录
    id = forms.IntegerField(label='',widget=forms.widgets.NumberInput(attrs={'hidden':'true'}),required=False)
    account = forms.CharField(
        label='账号',
        widget=forms.widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入账号','autofocus':True}))
    password = forms.CharField(
        label='密码',
        widget=forms.widgets.TextInput(attrs={'class':'form-control','placeholder':'请输入密码'}))
    email = forms.EmailField(
        label='邮箱',
        widget=forms.widgets.EmailInput(attrs={'class':'form-control','placeholder':'请输入邮箱'}))
    gender = forms.ChoiceField(
        # 设置字段首选项
        choices=((1,'男'),(2,'女')),
        label='性别',
        initial='1',
        widget=forms.widgets.RadioSelect())
    hobby = forms.ChoiceField(
        # 设置字段首选项
        choices=((1,'游泳'),(2,'自行车'),(3,'跑酷'),),
        label='爱好',
        initial='3',
        widget=forms.widgets.Select())
    hair = forms.ChoiceField(
        # 设置字段首选项
        choices=((1, '很多'), (2, '一般'), (3, '很少'),),
        label='发量',
        widget=forms.widgets.RadioSelect())
    img = forms.ImageField(label='头像',required=False)

说明:

id字段作为数据库表记录的唯一识别字段,在读取、修改、保存数据时作为记录的定位条件(相当于SQL中的where条件),将字段的属性required设为false是因为增加记录时不需要此字段的值。因为form中的所有字段默认required=True,如果不设置,在增加记录时,由于字段没有赋值而通不过校验,数据无法提交。另外,hidden=true设置该字段不在页面上显示。

img字段是ImageField类型,设计图片上传、存储等流程,所以要先安装pil模块。

在urls中加入path('add_loguser/',views.add_loguser)

视图函数add_loguser():

def add_loguser(request):
    if request.method == 'POST':
        # 由于有上传图片文件,所以参数中增加request.FILES
        form_obj = forms.loguser_form(request.POST or None,request.FILES or None)
        if form_obj.is_valid():
            # 在数据库中新增一条记录
            loguser_obj = models.loguser.objects.create(
                account=form_obj.cleaned_data['account'],
                password=form_obj.cleaned_data['password'],
                email=form_obj.cleaned_data['email'],
                gender=form_obj.cleaned_data['gender'],
                hobby=form_obj.cleaned_data['hobby'],
                hair=form_obj.cleaned_data['hair'],
                img=form_obj.cleaned_data['img']
            )
            return redirect('/list_loguser/')
        else:
            # 数据未通过校验,把loguser_form对象传给增加页面
            return render(request,'add_loguser.html',{'formobj':form_obj})
    # 第一次打开页面,初始化一个空的表单对象
    form_obj = forms.loguser_form()
    # 定向到增加页面,并传递参数
    return render(request,'add_loguser.html',{'formobj':form_obj})

说明:

代码按照if request.method == 'POST':的逻辑判断是否为第一次打开页面,如果请求方式是POST,那么是页面向后端提交数据,用以下4步把数据增加到数据库:

  • 首先通过form_obj = forms.loguser_form(request.POST or None,request.FILES or None)语句把提交的数据传给Form表单对象,这里传递了两个参数request.POST、request.FILES。通常情况下表单字段只有常规数据类型,不包含文件类型字段,只需要request.POST参数,如果字段中有ImageField、FileField等文件类型字段,必须要有request.FILES参数。语句中or None指的是如果提交的数据不存在,其值设为None。提交数据给loguser_form表单对象赋值完成时,相应的图片文件也传到了指定的目录,本例中为media下的img文件夹。
  • 接着是数据校验,校验功能是Form表单自带的,用if form_obj.is_valid()判断即可。
  • 数据校验通过就把合格数据存放在Form表单对象的cleaned_data属性中,这个属性以字典形式存储合格数据,提取数据要用Form对象.clean_data['字段名']或Form对象.clean_data.get('字段名')的形式。
  • 最后新建记录,保存到数据库中。

此外,如果提交的数据校验不成功,代码通过render(request,'add_loguser.html',{'formobj':form_obj})把保存loguser_form对象的变量formobj传给HTML文件,这个formobj包含错误信息,这些错误信息通过{{ form_obj.name.errors }}、{{ form_obj.name.errors.0 }}、{{ form_obj.errors }}等模板变量显示在页面上。

如果不是POST请求,则是第一次打开页面,初始化一个loguser_form对象,对象每个字段的定义都会影响它在页面的显示样式。

页面文件:

add_loguser.html:

{% load static %}
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Django Form样例</title>
    <!-- Bootstrap core CSS -->
    <link rel="icon" href="{% static 'favicon.ico' %}"  >
    <link href="{% static 'bootstrap/css/bootstrap.min.css'%}" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fontawesome/css/font-awesome.min.css'%}">
</head>

<body>
<div class="container">
    <div class="row">
        <div class="col-md-offset-3 col-md-6">
            <div class="page-header">
                <h1>Django Form测试
                    <small>--账户增加</small>
                </h1>
            </div>
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">账号增加</h3> <!--这里加标题 //-->
                </div>
                <div class="panel-body"> <!--将表格放在这个<div class="panel-body">的标签中 //-->
                    <form action="/add_loguser/" method="post" enctype="multipart/form-data" class="form-horizontal">
                        {% csrf_token %}
                        {{ formobj.id }}
                        <div class="form-group">
                            <label for="{{ formobj.account.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.account.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.account }}
                                <span class="help-block">{{ formobj.account.errors.0 }}</span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="{{ formobj.password.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.password.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.password }}
                                <span class="help-block">{{ formobj.password.errors.0 }}</span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="{{ formobj.email.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.email.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.email }}
                                <span class="help-block">{{ formobj.email.errors.0 }}</span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="{{ formobj.gander.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.gander.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.gander }}
                                <span class="help-block">{{ formobj.gander.errors.0 }}</span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="{{ formobj.hobby.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.hobby.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.hobby }}
                                <span class="help-block">{{ formobj.hobby.errors.0 }}</span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="{{ formobj.hair.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.hair.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.hair }}
                                <span class="help-block">{{ formobj.hair.errors.0 }}</span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label for="{{ formobj.img.id_for_label }}" class="col-md-2 control-label">
                                {{ formobj.img.label }}</label>
                            <div class="col-md-8">
                                {{ formobj.img }}
                                <span class="help-block">{{ formobj.img.errors.0 }}</span>
                            </div>
                        </div>
                        <div align="center">
                            <input type="submit" class="btn btn-primary" value="增加">
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>

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



{# 这里写注释 #}

{% comment %}
这是一个多行注释
这是第二行
这是第三行
{% endcomment %}

说明:

页面运用了Bootstrap框架页头、面板组件。在面板内部加入Bootstrap框架的表单组件,采用的方式也是复制样例代码并改造。具体有以下三步:

  • 首先在form标签中加入<form action="/add_loguser/" method="post" enctype="multipart/form-data" class="form-horizontal">前两个属性指明处理请求的URL和数据提交方式,enctype规定了在提交数据时对表单数据进行编码的方式,当enctype="multipart/form-data"时不对提交数据编码。按照HTTP约定,在表单包含文件上传控件时,必须使用该值。
  • 由于在loguser_form类中定义{{ formobj.id }}的属性为hidden,所以不会显示出来。
  • <div class="form-group">...</div>是Bootstrap表单中的一个字段单元,一般有几个字段就有几个这样的单元。我们在每个单元固定位置通过模板变量放置字段名称和字段值,主要有{{ formobj.xx.id_for_label }}用来显示数据文本框的id、{{ formobj.xx.id.label }}显示Form字段中定义的label属性值、{{ formobj.xx }}生成数据文本框或数据输入HTML标签、{{ formobj.xx.errors.0 }}显示字段数据错误信息的第一条等。(xx为Form字段名)。

账号修改

视图函数:

在urls中加入path('edit_loguser/<int:loguser_id>/',views.edit_loguser),

其中参数<int:loguser_id>的参数名为loguser_id,视图函数将用这个参数的值获取数据表的记录。这个参数在list_loguser.html文件中进行了赋值,代码如下:<a href="/edit_loguser/{{ user.id }}/" class="btn btn-info">...修改</a>。

账号信息修改的视图函数是edit_loguser(),代码如下:

def edit_loguser(request,loguser_id):
    if request.method == 'POST':
        form_obj = forms.loguser_form(request.POST or None,request.FILES or None)
        if form_obj.is_valid():
            id = form_obj.cleaned_data['id']
            # 取出id对应的记录
            loguser_obj = models.loguser.objects.get(id=id)
            loguser_obj.account=form_obj.cleaned_data['account']
            loguser_obj.password=form_obj.cleaned_data['password']
            loguser_obj.email=form_obj.cleaned_data['email']
            loguser_obj.gender=form_obj.cleaned_data['gender']
            loguser_obj.hobby=form_obj.cleaned_data['hobby']
            loguser_obj.hair=form_obj.cleaned_data['hair']
            loguser_obj.img=form_obj.cleaned_data['img']
            # 如果图片为空,是因为没有上传新的文件
            # 需要从预先保存了图片地址的字段img1中取值
            if not loguser_obj.img:
                loguser_obj.img=request.POST.get('img1')
            loguser_obj.save()
            imgname=loguser_obj.img
            return render(request,'edit_loguser.html',{'formobj':form_obj,'img':imgname})
        else:
            return render(request,'add_loguser.html',{'formobj':form_obj})
    # 请求方式不是POST,执行以下代码
    # 取得的值,以字典集合的形式存在obj_list中
    obj_list = models.loguser.objects.filter(id=loguser_id).values('id','account','password','email','gender','hobby','hair','img')
    # 取出第一个字典
    dic = obj_list[0]
    # imgname保存img字段的值
    imgname=dic['img']
    # 用字典值给loguser_form对象赋值
    form_obj=forms.loguser_form(initial=dic)
    return render(request,'edit_loguser.html',{'formobj':form_obj,'img':imgname})

edit_loguser.html

{% load static %}
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Django Form样例</title>
    <!-- Bootstrap core CSS -->
    <link rel="icon" href="{% static 'favicon.ico' %}"  >
    <link href="{% static 'bootstrap/css/bootstrap.min.css'%}" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fontawesome/css/font-awesome.min.css'%}">
</head>

<body>
<div class="container">
    <div class="row">
        <div class="col-md-offset-2 col-md-8">
            <div class="page-header">
                <h1>Django Form测试
                    <small>--账户修改</small>
                </h1>
            </div>
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">账号增加</h3> <!--这里加标题 //-->
                </div>
                <div class="panel-body"> <!--将表格放在这个<div class="panel-body">的标签中 //-->
                    <div class="media">
                        <div class="media-left">
                            <a href="#">
                                <img class="media-object img-circle" style="width:200px;height:200px;"
                                     src="/media/{{ img }}" alt=" 头像">
                            </a>
                        </div>
                        <div class="media-body">
                            <h4 class="media-heading">账户信息</h4>
                            <form action="" method="post" enctype="multipart/form-data" class="form-horizontal">
                                {% csrf_token %}
                                {{ formobj.id }}
                                <div class="form-group">
                                    <label for="{{ formobj.account.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.account.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.account }}
                                        <span class="help-block">{{ formobj.account.errors.0 }}</span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="{{ formobj.password.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.password.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.password }}
                                        <span class="help-block">{{ formobj.password.errors.0 }}</span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="{{ formobj.email.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.email.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.email }}
                                        <span class="help-block">{{ formobj.email.errors.0 }}</span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="{{ formobj.gander.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.gander.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.gander }}
                                        <span class="help-block">{{ formobj.gander.errors.0 }}</span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="{{ formobj.hobby.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.hobby.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.hobby }}
                                        <span class="help-block">{{ formobj.hobby.errors.0 }}</span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="{{ formobj.hair.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.hair.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.hair }}
                                        <span class="help-block">{{ formobj.hair.errors.0 }}</span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label for="{{ formobj.img.id_for_label }}" class="col-md-2 control-label">
                                        {{ formobj.img.label }}</label>
                                    <div class="col-md-8">
                                        {{ formobj.img }}
                                        <span class="help-block">{{ formobj.img.errors.0 }}</span>
                                    </div>
                                </div>
                                <input type="hidden" name="img1" id="img1" value="{{ img }}">
                                <div align="center">
                                    <input type="submit" class="btn btn-primary" value="保存">
                                    <a href="/list_loguser/" class="btn btn-success">返回</a>
                                </div>
                            </form>

                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

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

账号删除

del_loguser():

def del_loguser(request,loguser_id):
    obj=models.loguser.objects.get(id=loguser_id)
    obj.delete()
    return redirect('/list_loguser/')

在urls中加入path('del_loguser/<int:loguser>/',views.del_loguser),

Django ModelForm组件

Django ModelForm组件就是把model和form组合起来,它能够实现Django Form组件的所有功能,而不需要逐个对字段进行定义。

Django ModelForm定义

一般Django ModelForm定义形式如下,注意要指明model参数:

from . import models
class loguser_modelform(forms.ModelForm):
    class Meta:
        # 指定数据模型loguser,以loguser类的定义为基础建立表单
        model=models.loguser
        # __all__表示列出所有的字段
        field="__all__"
        # 排除hair、email两个字段
        exclude=['hair','email']
        label={
            'account':'账号',
            'gender':'性别',
            'hobby':'爱好',
            'img':'头像'
        }

说明:

  • 定义的类首先要继承于ModelForm,类的参数大部分写在class Meta中。

  • class Meta主要有以下常用参数。

    • model参数:对应的数据模型中的类,用数据模型中的定义表单。

      model=models.loguser
      
    • field参数:指定ModelForm字段,如果是__all__就列出所有字段。

      # 用所有的字段建立表单,即取所有字段生成表单字段
      field="__all__"
      # 仅用account和email两个字段建立表单
      field = ['account','email']
      
    • exclude参数:指定ModelForm要排除的字段。

      exclude=['hair','email']
      
    • label参数:指字段的label信息,是字典类型,键名为数据模型中的字段名。

      label = {
          'account':'账号',
          'gender':'性别',
          'hobby':'爱好',
          'img':'头像'
      }
      
    • help_texts参数:指帮助提示信息,是字典类型,键名为数据模型中的字段名。

      help_texts = {
          'email':'按照邮箱格式输入'
      }
      
    • widgets参数:定义字段在页面上的插件,是字典类型,键名为数据模型中的字段名。

      widgets = {
          'password':forms.widgets.PasswordInput(attrs={'class':'form-control'}),
          'account':forms.widgets.TextInput(attrs={'class':'form-control'}),
      }
      
    • error_messages参数:定义错误信息,是字典类型,键名为数据模型中的字段名。

      error_messages = {
          'account':{'max_length':'字符超长'}
      }
      

Django ModelForm主要方法

Django ModelForm的方法主要有数据校验和数据保存。

数据校验:

Django ModelForm的数据校验与Django Form的验证类型类似。当数据从网页上提交过来、ModelForm调用is_valid()时,表单自动对数据验证,并存放在ModelForm对象的cleaned_data属性中。

数据保存:

Django ModelForm通过save()方法就可以把表单对象绑定的数据直接保存到数据库表中,这是因为ModelForm字段是基于数据模型(数据库表)的字段建立的,对应关系明确。保存方式有两种:创建和修改。

ModelForm有一个关键字参数instance,这个参数接收数据模型实例(可以理解为数据库表记录)作为参数值。如果初始ModelForm对象提供instance参数,则save()将创建新实例(也就是新增一条记录)并保存。

新增一条记录的代码片段,不带参数instance:

# 根据POST提交的数据创建一个新的ModelForm对象
form_obj=forms.loguser_modelform(request.POST)
# 表单数据校验
if form_obj.is_valid():
    # 在数据库中新增一条记录
    form_obj.save()

修改一条记录的代码片段,需要带参数instance:

# 取出符合条件的记录
loguser_obj=models.loguser.objects.get(id=loguser_id)
if request.method=='POST':
    # 当带有instance参数时,就修改记录。
    form_obj = forms.loguser_modelform(request.POST,instance=loguser_obj)
    if form_obj.is_valid():
        form_obj.save()

样例7: Django ModelForm开发

ModelForm表单类

在forms.py中加入loguser_modelform类:

from . import models
class loguser_modelform(forms.ModelForm):
    gender = forms.ChoiceField(
        choices=((1,'男'),(2,'女'),),
        label='性别',
        initial='1',
        widget=forms.widgets.RadioSelect())
    hobby = forms.ChoiceField(
        choices=((1,'游泳'),(2,'自行车'),(3,'跑酷'),),
        label='爱好',
        initial='3',
        widget=forms.widgets.Select())
    hair = forms.ChoiceField(
        # 设置字段首选项
        choices=((1, '很多'), (2, '一般'), (3, '很少'),),
        label='发量',
        widget=forms.widgets.RadioSelect())

    class Meta:
        model = models.loguser
        fields = "__all__"
        labels = {
            'account': '账户',
            'password': '密码',
            'email': '邮箱',
            'img': '头像'
        }
        widgets = {
            'account': forms.widgets.TextInput(
                attrs={'class': 'form-control', "placeholder": "请输入账号", "autofocus": True}),
            'password': forms.widgets.TextInput(attrs={'class': 'form-control', "placeholder": "请输入密码"}),
            'email': forms.widgets.EmailInput(attrs={'class': 'form-control', "placeholder": "请输入邮箱"})
        }

说明:

由于这个loguser_modelform类关联models.loguser,所以需要引入models导入loguser所在的模块。loguser_modelform需要继承forms.ModelForm。gender、hobby、hair这三个字段需要choice属性,在ModelForm的一般字段形式中无法定义choice属性,所以采用自定义字段,形式与Form中定义形式一样,需要写在class Meta前面。

通过__all__语句获取models.loguser中的全部字段作为loguser_modelform的字段。

通过设置labels属性设置字段名,如果不设置则显示数据模型中定义的verb_name,如果没有设置verb_name,则显示数据模型中设置的字段名。

通过widgets设置标签以及标签属性。

列表页面

{% load static %}
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Django ModelForm样例</title>
    <!-- Bootstrap core CSS -->
    <link rel="icon" href="{% static 'favicon.ico' %}"  >
    <link href="{% static 'bootstrap/css/bootstrap.min.css'%}" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fontawesome/css/font-awesome.min.css'%}">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-offset-2 col-md-8">
<div class="page-header">
  <h1> 样例首页<small>--账号信息列表(Django ModelForm)</small></h1>
</div>
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title">账号信息</h3> <!--这里加标题 //-->
                </div>
                <div class="panel-body"> <!--将表格放在这个<div class="panel-body">的标签中 //-->
                    <div class="row">
                        <div class="col-md-3 pull-right" style="margin-bottom:15px ">
                            <div><a href="/add_loguserm/" class="btn btn-primary pull-right"><i
                                    class="fa fa-user-plus fa-fw"
                            ></i>&nbsp;增加</a><!--给增加记录按钮加bootstrap样式、fontawesome图标 //-->
                            </div>
                        </div>
                    </div>
                    <table class="table table-bordered table-condensed table-striped table-hover">
                        <!--给表格增加bootstrap样式 //-->
                        <thead>
                        <tr>
                            <th>账号</th>
                            <th>邮件</th>
                            <th>性别</th>
                            <th>爱好</th>
                            <th>头发数量</th>
                            <th colspan="2">操作</th>
                        </tr>
                        </thead>
                        <tbody>
                        {% for usr in usr_list %}

                        <tr>
                            <td>{{ usr.account }}</td>
                            <td>{{ usr.email }}</td>
                            <td>
                                {% if usr.gander == '1' %}
                                男
                                {% else %}
                                女
                                {% endif %}
                            </td>
                            <td>
                                 {% if usr.hobby == '1' %}
                                游泳
                                {% elif usr.hobby == '2' %}
                                自行车
                                {% else %}
                                跑酷
                                {% endif %}
                            </td>
                            <td>
                                 {% if usr.hair == '1' %}
                                很多
                                {% elif usr.hair == '2' %}
                                一般
                                {% else %}
                                很少
                                {% endif %}</td>

                            <td><a href="/del_loguserm/{{ usr.id }}/" class="btn btn-danger"><i
                                    class="fa fa-trash-o fa-fw"
                                    aria-hidden="true"></i>&nbsp;删除</a>
                            </td>
                            <td><a href="/edit_loguserm/{{ usr.id }}/" class="btn btn-info"><i
                                    class="fa fa-pencil-square-o"
                                    aria-hidden="true"></i>&nbsp;修改</a>
                            </td>
                        </tr>
                        {% empty %}
                        <tr>
                            <td colspan="7">无相关记录!</td>
                        </tr>
                        {% endfor %}
                        </tbody>
                    </table>

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

list_loguser()视图函数:

def list_loguser(request):
    users = models.loguser.objects.all()
    return render(request,'list_loguser1.html',{'user_list':users})

账号增加

add_loguserm():

def add_loguserm(request):
    if request.method=='POST':
        form_obj=forms.loguser_modelform(request.POST or None,request.FILES or None) #由于有上传图象文件,所以参数中增加request.FILES
        #print(form_obj)
        if form_obj.is_valid(): #表单数据校验
            form_obj.save() #在数据库表中新增一条记录
            return redirect('/list_loguserm/') #增加记录后生定向到列表页面
        else:
            return render(request, 'add_loguser1.html', {'formobj': form_obj}) #数据未通过校验,重新加到增加页面
    form_obj=forms.loguser_modelform() #第一次打开页面,初始化一个表单对象
    return render(request,'add_loguser1.html',{'formobj':form_obj}) #定向到增加页面,并传递参数

说明:

数据通过校验。直接通过form_obj.save()保存数据,不需要一个个字段进行赋值。因为ModelForm类对象与数据库表直接关联,这里loguser_modelform对象赋值时没有给instance参数值,所以这次保存是新增一条记录并保存。

账号修改

账号修改视图函数edit_loguserm()与前面介绍的Django Form样例中的略有不同,主要是要通过参数instance进行初始化。我们知道ModelForm对象如果不传入instance参数时,执行save函数时将创建一条新记录,如果传入instance值,执行save函数会修改相应的记录并保存。

def edit_loguserm(request,loguser_id):
    loguser_obj = models.loguser.objects.get(id=loguser_id)#取出符合条件的记录
    if request.method=='POST':
        form_obj = forms.loguser_modelform(request.POST or None, request.FILES or None,instance=loguser_obj)
        if form_obj.is_valid():
            loguser=form_obj.save()
            imgname=loguser.img
            return render(request, 'edit_loguser1.html', {'formobj': form_obj, 'img': imgname})
        else:
            return render(request, 'add_loguser1.html', {'formobj': form_obj})
    imgname=loguser_obj.img #取记录中的图像文件的地址
    form_obj=forms.loguser_modelform(instance=loguser_obj)#用数据记录数据初始化一个表单对象
    return render(request, 'edit_loguser1.html', {'formobj': form_obj,'img': imgname})
posted @ 2021-07-26 17:17  KKKyrie  阅读(185)  评论(0编辑  收藏  举报