Django Form组件
Django Form组件
我们在编写向后台数据库提交数据的程序时一定会用到表单,因此需要在HTML页面设计form标签。我们会根据数据输入的需求与页面样式,在form标签中放置各种输入的标签,如input、select等。这会增加写前端页面代码的工作量,但这不是主要的,主要是页面利用表单向后端提交数据时,前后端都要写一些校验代码,比如校验用户是否输入、校验输入格式是否正确等。而且如果用户输入的内容有错误,就需要在页面相应的位置显示对应的错误信息,这不仅增加了代码量,而且许多代码是重复的。
Django Form组件的引入弥补了以上不足,它能自动在页面上生成可用的HTML标签,提供数据校验功能,提高代码的质量和效率。Django Form有两种,一种是Form组件,另一个是ModelForm组件。
前期环境准备
利用前面章节的test_orm项目和employee应用开发。
Django Form表单的主要功能
Django Form表单的主要功能是对表单字段进行集中管理,主要功能如下:
- 自动生成html表单元素,可减少前端代码编写。一方面字段类型生成默认标签,如CharField对应HTML中的input标签;另一方面可以通过widget属性来渲染成HTML元素,如forms.widgetsSelect()对应HTML中的select标签。
- 通过表单字段类型、属性的定义,自动产生校验表单数据的合法性的功能。如EmailField表示email字段,如果不是有效的邮箱会产生错误。
- 如果验证错误,将重新显示表单,已输入的数据不会被重置或清空,用户界面友好。
- 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="密码")
说明:
- Form类必须直接或间接继承自django.forms.Form。
- Django Form对象封装了一系列字段和验证规则。
- 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":"编码最少十位"
})
- 通用属性
-
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标签字段特有属性
- 字段特有属性
除了所有字段都拥有的通用属性,还有一些属性属于某种类型字段自有的属性,称作特有属性:
- CharField字段属性如下。
- min_length指定字段最小长度。
- max_length指定最大长度。
- strip指定是否清除用户输入的空白,当strip=True清除空白。
- 数值类型字段主要包括IntegerField、DecimalField、FloatField等类型字段,他们有如下属性:
- max_value:指定字段最大值。
- min_value:指定字段最小值。
- max_digits:指定字段的总长度,是DecimalField类型独有的属性。
- decimal_places:指定字段的小数位长度,DecimalField类型独有。
- ChoiceField字段属性说明如下。
- choices限定字段值的可选项,用元组类型设置该属性,如choices=((0,'男'),(1,'女'),)。
- FileField字段属性说明如下。
- allow_empty_file设置上传文件是否允许空文件,是布尔类型属性。
Django Form 常用字段
- CharField字符类型字段,其代码定义如下:
studentinfo = forms.CharField(
min_length=10,
label="学生编码",
initial="1230000001",
error_messages={
"required":"不能为空",
"invalid":"格式错误",
"min_length":"编码最少10位"
})
- 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)
- DateField、TimeField、DateTimeField是日期时间字段。
date=forms.DateField() # 格式为yyyy-mm-dd
time=forms.TimeField() # 格式为hh:mm
dt=forms.DateTimeField() # 格式为yyyy-mm-dd hh:mm
- ChoiceField字段,如果不指定widget,默认在页面上生成select标签。
sex = forms.ChoiceField(
choices=((1,'男'),(2,'女'),),
)
- 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步:
- 第一步用提交的数据给From对象赋值,提交的数据都存在request对象中,因此用from_obj=forms.login_form(request.POST)给form_obj对象赋值,这些数据是从前端网页上提交过来的。
- 第二步对提交数据进行校验,Form对象自动根据字段定义进行校验,判断校验是否成功用is_valid()函数。
- 提交的数据校验不合格或未通过,用render函数把现在的Form对象重新传递回页面。提交数据通过校验后,把数据取出来与数据库表loguser中的account、password值做对比。如果正确就表示登陆成功,跳转到账号列表页面(list_loguser.html).如果不正确,error变量赋值为错误提示信息,并通过render函数把错误信息和Form对象传递回页面。
- 如果不是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>
说明:
- {% load static %}导入静态文件。head标签中引用的JS、CSS文件都下载并存放在静态文件夹下,这个标签中还引用
signin.css样式文件,在静态文件夹下新建这个文件,其中样式代码拷贝自登录页引用的signin.css文件。
-
下面介绍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表单级别上的所有错误。
-
根据上面的介绍,<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> 增加</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> 删除</a>
</td>
<td><a href="/edit_loguser/{{ usr.id }}/" class="btn btn-info"><i
class="fa fa-pencil-square-o"
aria-hidden="true"></i> 修改</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> 增加</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> 删除</a>
</td>
<td><a href="/edit_loguserm/{{ usr.id }}/" class="btn btn-info"><i
class="fa fa-pencil-square-o"
aria-hidden="true"></i> 修改</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})