项目启动后,用户通过浏览器向Web服务器发起请求,Web服务器将请求传递到要处理该请求的Django项目,Django接收用户通过浏览器发起的请求,urls.py文件根据URL地址分发路由,将请求交给views.py中相应的视图;视图处理请求(此时涉及数据存取),并将处理结果与模板结合生成响应数据返回给Web服务器,服务器将数据返回到浏览器,最终呈现给用户。
Django使用MTV架构,该架构由模型(Model)、模板(Template)、视图(View)三部分组成,各部分的职责如下。
Django项目的数据模型定义在模型文件models.py中,模板文件存储在templates目录(需手动创建与配置)中,业务逻辑存储在视图文件views.py中。此外Django项目还有一个核心文件urls.py,用于实现路由分发功能。
一个典型的HTML表单如下
静态表格
该表单由静态HTML实现,要直接在浏览器中访问该表单,需将其放在Django项目的static静态资源文件夹中。
第一步:新建项目文件chapter01,在项目中创建新的应用。
django-admin startproject chapter01
python manage.py app
第二步:在settings.py注册app,配置文件夹templates的路径,以及数据库mysql
#templates路径
'DIRS': [os.path.join(BASE_DIR, 'templates')],
#mysql数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test',
'HOST':'localhost',
'PORT': 3306,
'USER':'root',
'PASSWORD':'myy040715'
}
}
在settings.py中,默认静态资源URL访问路径配置:
STATIC_URL = '/static/'
第四步:app中创建文件夹:static
第五步:将照片放在static文件夹中
第六步:测试:http:127.0.0.1:8000/static/pic.png
第七步:在static文件夹下创建静态文件(表格.html)
静态表格
测试(http://127.0.0.1:8000/static/表格.html):静态表格
根目录下创建public_statics文件夹(与应用app同级)
在settings.py中配置
STATICFILE_DIRS=[os.path.join(BASE_DIR,'public_statics'),]
创建静态文件(htmlform.html)
Document
定义视图函数(views.py)
from django.shortcuts import render
# Create your views here.
def getdata(request):
data=''
number=''
if 'data' in request.POST:
data=request.POST['data']
number = data * 2
return render(request,'htmlform.html',{'current_data':data,'number':number})
配置根路由(urls.py)
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('app.urls')),
]
配置子路由:
from django.contrib import admin
from django.urls import path
from app import views
urlpatterns = [
path('getdata/',views.getdata),
]
模板中的HTML表单中,要改变表单就必须修改模板文件。 Django表单通过扩展django.forms.Form类可在视图中动态生成表单。
定义视图函数(views.py)
from django import forms
class dataForm(forms.Form):
data = forms.CharField(label='请输入数据')
#使用表单类和视图
def useDataForm(request):
if request.method == 'POST':
form = dataForm(request.POST)
msg='数据已提交'
else:
form = dataForm()
msg = '初始表单'
return render(request,'temdataform.html',{'form':form,'msg':msg})
创建模板文件(temdataform.html)
Document
{{msg}}
配置子路由(urls.py)
path('dform/',views.useDataForm),
元素中的段落。
配置视图函数(views.py)
def useDataForm3(request):
return render (request,'temdataform3.html',{'form':dataForm()})
创建模板文件(temdataform3.html)
Document
表格样式渲染的结果:
段落样式渲染的表单
列表样式渲染的表单
配置子路由(urls.py)
path('dform3/',views.useDataForm3),
表单字段定义包含字段名、字段类型和字段参数3个部分 、
class dataForm(forms.Form):
data = forms.CharField(label='请输入数据') #定义表单字段
data为字段名,字段渲染生成的
定义视图函数(views.py)
class dataForm2(forms.Form):
data = forms.CharField(label='请输入数据',)
a = forms.BooleanField(label='a')
sex = forms.ChoiceField(label='sex')
date = forms.DateField(label='date')
def useDataForm4(request):
return render (request,'temdataform4.html',{'form':dataForm2()})
创建模板文件(temdataform4.html)
Document
{{msg}}
配置子路由(urls.py)
path('dform4',views.useDataForm4),
参数required用于设置当前字段是否为必需字段。
#在终端里运行
python manage.py shell
from django import forms
name=forms.CharField()
name.clean('') #报错
name.clean(' ') #报错
name.clean(None) #报错
name.clean(0)
name.clean(True)
name.clean(False)
name.clean(123)
name.clean('as')
label参数用于设置表单字段被渲染为HTML
class test(forms.Form):
... name=forms.CharField(label='请输入姓名')
...
>>> print(test())
#在创建表单时,可使用auto_id=False来简化渲染结果
d=test(auto_id=False)
print(d)
label_suffix属性用于设置表单字段被渲染为HTML
>>> class test(forms.Form):
... addr=forms.CharField(label='联系地址',label_suffix='*')
...
>>> print(test())
#initial参数用于设置字段的初始值
>>> class test(forms.Form):
... name=forms.CharField(initial='someone')
...
>>> print(test(auto_id=False))
#也可在创建表单对象时提供初始值:
>>> d=test({'name':'Lining'},auto_id=False)
>>> print(d)
help_text参数用于设置字段的帮助信息,帮助信息被渲染为元素
class test(forms.Form):
... name=forms.CharField(help_text='姓名包含字母、数字等字符')
...
>>> print(test(auto_id=False))
name=forms.CharField(error_messages={'required':'必须提供name字段数据'})
name.clean('11')
disabled参数被设置为True时,不允许表单字段渲染成的HTML元素与用户交互
class test(forms.Form):
... addr=forms.CharField(label='联系地址',disabled=True)
...
>>> print(test())
表单验证即对表单中的数据进行验证,检验表单各个字段的数据是否符合该字段的约束条件。通常,Django根据字段类型执行默认的校验操作。
Django类字段常见验证操作
下面定义一个validate_lt()函数,它在字符串包含小于或大于符号时抛出ValidationError异常。
>>> from django.core.exceptions import ValidationError
>>> def validata_lt(value):
... if"<" in value or ">" in value:
... raise ValidationError("不允许小于号或大于号")
...
>>> str=forms.CharField(validators=[validata_lt]) #定义字段,引用自定义异常函数
>>> str.clean("1<99")
#校验函数内,出现不符合校验标准的情况必须抛出ValidationError异常,异常信息作为校验错误的提示信息
request.POST包含了表单采用POST请求方法提交数据,可以使用表单校验传过来的数据是否合法,还能将校验的错误信息保存,还可以判断forms有没有绑定相关的数据。
#设置了数据的表单称为绑定表单(is_bound属性为True),没有数据的表单称为未绑定表单(is_bound属性为False)。
>>> class test(forms.Form):
... name=forms.CharField(max_length=50)
... age=forms.IntegerField(max_value=50)
...
>>> d=test() #创建空表单
>>> d.is_bound #结果为False,说明表单未绑定
>>> d=test({}) #绑定空值时,表单也被绑定
>>> d.is_bound #结果为True,说明表单已绑定
>>> d=test({'name':'mike','age':20}) #绑定具体数据
>>> d.is_bound
调用表单is_valid()方法时会执行数据校验,当所有字段数据均合法时,方法返回True,否则返回False。
#打开python manage.py shell的时候,都要导入表单。
>>> from django import forms
class test(forms.Form):
... age=forms.IntegerField(max_value=35)
... name=forms.CharField(max_length=20)
...
>>> d=test({'name':'zhangsan','age':20}) #绑定具体数值
>>> d.is_valid() #字段数据符合form标单参数要求,通过校验
>>> d=test({'name':'xiaoming','age':36})
>>> d.is_valid()
执行校验时,Django为表单对象创建cleaned_data属性。 通过校验的数据是“干净的”,被保存在表单的cleaned_data属性中。 cleaned_data属性只能在执行校验之后访问,否则会触AttributeError异常。
>>> d=test({'name':'xiaoming','age':20})
>>> d.cleaned_data #触发异常
>>> d.is_valid()
True
>>> d.cleaned_data
{'age': 20, 'name': 'xiaoming'}
>>>
如果有数据没有通过校验,is_valid()方法返回Flase,cleaned_data属性中保存了已经通过校验的字段数据,error属性保存未通过校验的字段的错误信息。
>>> d=test({'name':'zhangsan','age':50})
>>> d.is_valid()
>>> d.cleaned_data
>>> d.errors.as_json()
>>> d.errors.get_json_data()
Form对象的另一个作用是将自身转为HTML。为此,我们只需要简单的使用print()方法可以看到From对象的HTML输出。
class test(forms.Form):
... age=forms.IntegerField(max_value=35)
... name=forms.CharField(max_length=20)
...
>>> print(test())
如果表单绑定了数据,则HTML输出包含数据的HTML文本。
d=test({'name':'zhangsan','age':50})
>>> print(d)
手动处理表单字段时可将每个表单字段视为表单属性,通过{{form.name_of_field}}的形式访问。Django允许在表单模板中自定义表单字段的渲染效果。在模板中,用{{form.字段名}}格式来访问表单字段。在表单模板中,也可用{%for%}循环来遍历表单字段。表单字段的常用属性如下。
相比较Django对表单字段的自动解析,手动处理更加灵活,开发者可以有选择的处理字段,也可以调整字段顺序。下面定义成绩表单的例子
定义视图函数(views.py)
class test(forms.Form):
name=forms.CharField(max_length=50,label='姓名')
age=forms.IntegerField(max_value=50,min_value=15,label="年龄",help_text='年龄小于15且不大于50')
def useTest(request):
if request.method == 'POST':
form = test(request.POST)
else:
form = test()
return render(request, 'temtest.html', {'form': form})
创建模板文件(temtest.html)
Document
配置子路由:
path('diyfield/',views.useTest),
在表单模板中,若每个表单字段使用相同的HTML,也可用{%for%}循环来遍历表单字段。
在templates里面创建模板文件(temtestfor.html)
Document
遍历表单字段
定义视图函数(views.py)
def useTestFor(request):
return render (request,'temtestfor.html',{'form':test()})
配置子路由:
path('diyfor/',views.useTestFor),
表单集是表单对象的集合,用于处理多个表单。利用表单集,用户可以同时提交一组表单,在数据库中添加多条记录。
可调用django.forms模块提供的formset_factory()工厂类方法创建表单集类,定义视图(views.py)
#创建表单集
class GoodForm(forms.Form):
name = forms.CharField(label='商品')
price = forms.DecimalField(label='价格')
stock = forms.IntegerField(label='库存')
sales = forms.IntegerField(label='销量')
from django.forms import formset_factory
from django.shortcuts import render
def useFormset(request):
classTestFormset = formset_factory(GoodForm, extra=2)
if request.method == 'POST':
formset = classTestFormset(request.POST)
else:
formset = classTestFormset()
return render(request, 'temformset.html', {'formset': formset})
创建模板文件(temformset.html)
商品表单集合
商品表单集合
配置子路由(app里面urls.py)
path('formset/',views.useFormset),
表单集中的表单分为空表单和非空表单,表单集中空表单的数量默认为1。在页面中查看渲染得到的HTML代码可以发现,代码输出结果中只有一个空表单,这是因为表单集默认只显示一个表单,另外表单集中的表单尚未绑定数据。
如以下可知:extra可以控制表单集中空表单的数量,默认值为1,
设置表单集的初始数据(initial)
像表单一样,以上代码使用initial参数为表单集设置了初始值。由于initial参数接收的字典中只有一个元素, 所以表单集中包含 了一个绑定了初始值的表单和两个空表单。
限制表单的最大数量
利用参数max_num可以控制表单集中表的表单数量。max_num设置为None,那么表单最多包含1000张表单。
定义模型(models.py):
from django.db import models
class person(models.Model):
name=models.CharField(max_length=8)
age=models.SmallIntegerField()
生成迁移文件,执行数据的迁移
python manage.py makemigrations
python manage.py migrate
模型表单有两个特点:
定义视图函数(views.py):
#模型表单
from .models import person
from django.forms import ModelForm
class personForm(ModelForm):
class Meta:
model = person
fields = '__all__'
def usePersonForm(request):
if request.method == 'POST':
mform = personForm(request.POST)
if mform.is_valid():
ps=person.objects.filter(name=request.POST['name'])
if ps.count()==0:
mform.save()
msg='数据已保存!'
else:
msg='数据库已存在相同姓名的数据,请勿重复提交!'
else:
msg='表单数据有错'
else:
mform = personForm()
msg="请输入数据添加新纪录"
return render(request,'temmodelform.html',{'mform':mform,'msg':msg})
视图在使用POST请求时,视图通过request.POST 获得客户端提交的数据。将requestPOST作为参数初始化表单,执行表单验证操作,可以检查数据是否有效。在数据有效时,用客户端提交的书名作为条件执行查询。当数据库中不存在相同姓名时,执行表单保存操作,将数据写入数据库。
创建模板文件(temmodelform.html)
Document
{{msg}}
配置子路由(urls.py)
path('mform/',views.usePersonForm),
通常情况下,模型表单字段与模型字段保持一致。 Django允许在模型表单中覆盖模型字段定义
定义模型(views.py)
#自定义模型表单字段
from django.forms import ValidationError
def validate_age(value):
if int(value) < 20:
raise ValidationError('年龄不能小于20!',code='min_value')
elif int(value) >50:
raise ValidationError('年龄不能大于50!',code='max_value')
class personFormDIY(ModelForm):
#重定义age字段
age=forms.CharField(validators=[validate_age],label='年龄',\
widget=forms.NumberInput(),\
help_text= '年龄为[20,50]以内的整数')
class Meta:
model=person
fields = ['name','age']
labels = {'name':'姓名'}
help_texts ={'name':'姓名为中英文字符串',}
widgets = {'name' : forms.Textarea(attrs={'cols':30,'rows':2}),}
def usePersonFormDIY(request):
if request.method == 'POST':
mform = personFormDIY(request.POST)
if mform.is_valid():
ps=person.objects.filter(name=request.POST['name'])
if ps.count()==0:
mform.save()
msg='数据已保存!'
else:
msg='数据库已存在相同姓名的数据,请勿重复提交!'
else:
msg='表单数据有错'
else:
mform = personForm()
msg="请输入数据添加新纪录"
return render(request,'temmodelformdiy.html',{'mform':mform,'msg':msg})
创建模板文件(temmodelformdiy.html)
Document
{{msg}}
配置子路由:
path('mdiy/',views.usePersonFormDIY)
资源指应用于表单的CSS和JavaScript文件。
当定义表单或小部件时,可以在Media子类中为其定义资源。在渲染表单时,Django会将资源文件包含到HTML中。
通过扩展小部件定义表单资源的基本格式如下。 class 自定义小部件类名称(forms.内置小部件类名称):
class Media:
css={'设备类型': ('CSS资源文件URL',……),……}
js= ('JavaScript资源文件URL',……)
Media子类的css属性用于设置CSS资源。css属性中的键设置CSS资源中的样式单适用的设备类型 资源文件的URL可以使用相对路径或绝对路径。CSS和JavaScript资源文件属于静态资源,通常将其放在项目的static文件夹中
可用的类型名称如下。
以图6-17的自定义的模型表单bookformly为例,通过css静态文件优化表单样式
第一步:在项目同名文件夹下创建static文件夹,将css样式单文件diyform.css放入static文件夹内
.helptext{
color:cadetblue;
font-size:12px;
}
.errorlist{
color:red;
font-size: 12px;
}
p{
color: darkgoldenrod;
font-size: 18px;
}
body{
background: url(./pic1.jpg) no-repeat center center fixed;
background-size: cover;
padding-top: 20px;
}
form {
width: 543px;
height: 300px;
margin: 0 auto;
padding: 20px;
border: 1px solid rgba(0,0,0,0.2);
border-radius: 5px;
background: rgba(0,0,0,0.5);
overflow: hidden;
}
input{
width: 96px;
height: 28px;
border: 1px solid rgba(255,255,255,0.4);
border-radius: 4px;
display: inline-block;
font-size: 16px;
color: #fff;
background: rgba(255,255,255,0.4) no-repeat 16px 16px;
margin-bottom: 10px;
padding-left: 25px;
padding-right: 20px;
}
修改temmodelformdiy.html,添加{{bform.media}}变量,以及css的链接路径
Document
{{bform.media}}
{{msg}}