前言 ´・ᴗ・`
- 所谓表单 就是一些要我们填写的表格 比方注册网站的登记表
- 本篇带你用Django 玩玩表单
- 本篇内容将会帮助你学习…
- 1 HTML表单的一般处理方式
- 2 Django forms类 表单处理方式 使用方法
- 3 为图书馆应用创造一个 续借renew 的表单
HTML表单结构
我们看个简单的代码
<form action="/team_name_url/" method="post">
<label for="team_name">Enter name: </label>
<input id="team_name" type="text" name="name_field" value="Default name for team.">
<input type="submit" value="OK">
</form>
- action 就是 我们用户填完表 submit提交以后 我们提交的url地址
- method 就是 请求类型 是get还是post
- get 适合 不更改数据库的表单 比如 搜索框
- post 适合 更改数据库的表单 比如注册 登录 录入个人信息等
处理表单的流程(Django处理的流程)
- 显示默认表单 编辑框里的东西 有时称为占位符placeholder
此时表单被称为未绑定(un bind) 没有任何来自用户的信息注入 绑定啥呢? - 提交请求 服务器接收数据,并将其绑定到表单。
这时 数据绑定到表单了 - 检查 净化数据 然后从数据中提取必要信息 转换成python对象 比如日期对象
所谓检查内容 避免类似XSS攻击的恶意代码 删除可能用于向服务器发送恶意内容的无效字符 - 根据数据库模型 对数据进行限制 (例如,在正确的日期范围内,不是太短或太长等)
因为前面已经把数据弄成对象了 现在比较值 限制什么都很简单
当然现在简单的限制数据检查 是让前端来干了 分担服务器工作
复杂的检查一般是AJAX 也就动态加载 对表单信息的验证结果 而不是重新刷新页面(太low了?) - 验证检查值是否适合该字段
- 如果数据无效,重新显示表单
- 如果数据都有效,执行必要的操作(例如保存数据,发送表单和发送电子邮件,返回搜索结果,上传文件等)
- 完成所有操作后,将用户重定向到另一个页面
Forms
Django提供了Forms 这个工具来封装这些表单
其思维是 既然数据最终走向的是数据库
那么语法也应该接近ORM的写法
我们看例子就懂了
from django import forms
class RenewBookForm(forms.Form):
renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
除了DateField 还有其他的 如:
BooleanField, CharField, ChoiceField, TypedChoiceField, DateField, DateTimeField, DecimalField, DurationField, EmailField, FileField, FilePathField, FloatField, ImageField, IntegerField, GenericIPAddressField, MultipleChoiceField, TypedMultipleChoiceField, NullBooleanField, RegexField, SlugField, TimeField, URLField, UUIDField, ComboField, MultiValueField, SplitDateTimeField, ModelMultipleChoiceField, ModelChoiceField.
然而我觉得 用到的时候再说是最好的方式
这些field有些共有属性
- required: 如果为True,则该字段不能为空
- label: 在 HTML 中呈现字段时使用的标签。如果未指定label,则 Django 将通过大写第一个字母、并用空格替换下划线(例如续订日期)的方式,从字段名称创建一个。
- label_suffix: django连编辑框前的说明文字都包了2333
默认情况下,标签后面会显示冒号(例如续借日期: )。此参数允许您指定包含其他字符的不同后缀。 - initial: 显示表单时,字段的初始值 就是占位符的意思
- widget: 要使用的显示小部件(这个后面会讲是什么“小部件”)
- help_text :可以在表单中显示的附加文本,用于说明如何使用该字段。
- error_messages: 字段的错误消息列表。如果需要,您可以使用自己的消息,覆盖这些消息。
- validators: 验证时 调用的验证函数列表
- localize: 启用表单数据输入的本地化 就是语言的问题啦
- disabled: 如果为True 无法编辑其值 禁用状态
这里 对于我们图书馆应用 我们就 设定一个 续借的表单好了
我们命名为“renewal_date” 作为 续借日期的字段field 名字
验证数据
我们想验证数据 可以采用 覆盖原有的验证函数来实现
在view class 视图那边 我们可以这么写 locallibrary/catalog/forms.py:
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
import datetime #for checking renewal date range.
class RenewBookForm(forms.Form):
renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
def clean_renewal_date(self):
data = self.cleaned_data['renewal_date']
#检测日期是不是过去的日期
if data < datetime.date.today():
raise ValidationError(_('Invalid date - renewal in past'))
#检测 还书日期是否在1个月内
if data > datetime.date.today() + datetime.timedelta(weeks=4):
raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
# Remember to always return the cleaned data.
return data
- 注意命名 clean_renewal_date 验证函数都是clean_<字段名称> 这种格式的
- 注意 cleaned_data 函数 这玩意太强了 直接净化数据中那些不合法字段(比如恶意代码之类的),获取到有用的信息 然后转成合法的python对象 这里就是date日期对象
- ValidationError 这玩意就是说 验证不通过 然后弹出错误信息给客户看 用
_()
函数
搞定form的设置 我们添加url:locallibrary/catalog/urls.py
path('book/<uuid:pk>/renew/', views.renew_book_librarian, name='renew-book-librarian'),
这句话就是 匹配form action填写的url地址 利用uuid格式 提取关键值(在这里 就是书的id)
然后存到pk(primary key缩写)的变量中去
然后我们用view的函数renew_book_librarian处理提交上来的数据
view处理提交的数据
上面把锅甩给了view 这里我们view就要面对现实了
大致思路就是区分method 是POST还是GET?两种上传数据的方式带来两种不同的处理方式
上代码:
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
import datetime
from .forms import RenewBookForm
def renew_book_librarian(request, pk):
#首先找到书的实例(我们是续借具体的书 书都找不到还续借啥?)
book_inst=get_object_or_404(BookInstance, pk = pk)
# 如果是POST方法
if request.method == 'POST':
# 用POST解析 然后用form对象去给数据建模 也就是格式化 结构化数据
form = RenewBookForm(request.POST)
#然后调用 clean_renewal_date()
if form.is_valid():
book_inst.due_back = form.cleaned_data['renewal_date']
book_inst.save()
# redirect to a new URL:
return HttpResponseRedirect(reverse('all-borrowed') )
# If this is a GET (or any other method) create the default form.
else:
proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
-
RenewBookForm 这里 首先是 这个就是我们之前forms.py 那个类
这里涉及几个概念
一个是 这就是典型的 结构化 也就是裸数据弄成python对象以后 让对比值 限制值 so easy
第二是 这也是称为“绑定”binding数据操作 -
return HttpResponseRedirect(reverse(‘all-borrowed’) )
这就是之前说的 表单成功提交 数据ok的话 我们重定向到另一个页面 (受view管理的当然) -
get_object_or_404(): 根据模型的主键值,从模型返回指定的对象,如果记录不存在,则引发Http404 异常(未找到)。
-
HttpResponseRedirect: 这将创建指向指定URL的重定向(HTTP状态代码 302)。
-
reverse(): 反向定位 之前也讲过 通过映射的名字“all-borrow” 得到url值 然后作为参数让HTTP重定向
然后 如果GET方式 或者POST数据不合法 一样render打包数据回去 送你一个页面
加上上一节 我们限定了 只有图书管理员才能有的权限“set_book_as_returned”
所以对于这个“all borrow”页面 我们可以加装饰器来限定:
@permission_required('catalog.can_mark_returned')
模板编辑
/catalog/templates/catalog/book_renew_librarian.html
{% extends "base_generic.html" %}
{% block content %}
<h1>Renew: {{bookinst.book.title}}</h1>
<p>Borrower: {{bookinst.borrower}}</p>
<p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: {{bookinst.due_back}}</p>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form }}
</table>
<input type="submit" value="Submit" />
</form>
{% endblock %}
这里注意:在表单标签内添加的{% csrf_token %} ,是 Django 跨站点伪造(XSS攻击)保护的一部分。
然后 我们再简单限制上一节 有关图书管理员的管理页面 “续借按钮”的设计 bookinstance_staff_management.html
{% extends "base_generic.html" %}
{% block content %}
<h1>Borrowed books</h1>
{% if bookinstance_list %}
<ul>
{% for bookinst in bookinstance_list %}
<li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
<a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }}) - {{bookinst.borrower}} - {% if perms.catalog.set_book_as_returned %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a> {% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no books borrowed.</p>
{% endif %}
{% endblock %}
总结 ´◡`
runserver http://127.0.0.1:8000/catalog/borrowed/
invalidate input:
下一节我们简要复习并拓展之前学习的内容 完善我们的图书馆应用
服务端编程(十四)- Django - 视图 模板设计的补充
我的专栏 希望能够帮到你 ( •̀ ω •́ )✧
-
本文专栏
手把手带你学后端(服务端) -
想学习数据库嘛? 不妨从MySQL入手
MySQL专栏 -
python这么火 想要深入学习python 玩一下简单的应用嘛?
python应用 -
谢谢大佬支持! 萌新有礼了:)