Django入门与实践-第13章:表单处理(完结)
http://127.0.0.1:8000/boards/1/
http://127.0.0.1:8000/boards/2/
http://127.0.0.1:8000/boards/3/
http://127.0.0.1:8000/boards/1/new/http://127.0.0.1:8000/boards/2/new/
http://127.0.0.1:8000/boards/3/new/
这是我们在前一个教程绘制的线框图。我现在意识到这个可能是一个不好的例子,因为这个特殊的表单涉及到处理两个不同模型数据:Topic(subject)和 Post(message)。 GET 可能是最常见的请求类型了。它用于从服务器请求数据。每当你点击了一个链接或者直接在浏览器中输入了一个地址时,你就创建一个 GET 请求。 POST 用于当我们想更改服务器上的数据的时候。一般来说,每次我们发送数据给服务器都会导致资源状态的变化,我们应该使用 POST 请求发送数据。 Django 使用 CSRF Token(Cross-Site Request Forgery Token) 保护所有的POST 请求。
这是一个避免外部站点或者应用程序向我们的应用程序提交数据的安全措施。
应用程序每次接收一个 POST 时,都会先检查 CSRFToken。
如果这个 request 没有 token,或者这个 token是无效的,它就会抛弃提交的数据。 csrf_token 的模板标签:
{% csrf_token %} 下面是示范我们如何检索数据:
subject = request.POST['subject']
message = request.POST['message'] 创建表单正确的姿势
自从我们开始使用 Forms,我们已经走了很长一段路。终于,是时候使用Forms API 了。
Forms API 可在模块 django.forms 中得到。
Django 使用两种类型的form: forms.Form 和 forms.ModelForm 。
Form 类是通用的表单实现。我们可以使用它来处理与应用程序 model 没有直接关联的数据。
ModelForm 是 Form 的子类,它与 model 类相关联。 让我们去掉一些多余的部分,只看表单处理的核心部分:
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save()
return redirect('board_topics', pk=board.pk)
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'form': form})
首先我们判断请求是 POST 还是 GET。
如果请求是 POST,这意味着用户向服务器提交了一些数据。
所以我们实例化一个将 POST 数据传递给 form 的form 实例: form = NewTopicForm(request.POST) 。 然后,我们让 Django 验证数据,检查 form 是否有效,我们能否将其存入数据库: if form.is_valid(): 。
如果表单有效,我们使用 form.save()将数据存入数据库。
save() 方法返回一个存入数据库的 Model 实例。
所以,因为这是一个 Topic form, 所以它会返回 topic = form.save() 创建的 Topic。
然后,通用的路径是把用户重定向到其他位置,以避免用户通过按 F5 重新提交表单,并且保证应用程序的流程走向。 现在,如果数据是⽆效的,Django 会给 form 添加错误列表。
然后,视图函数不会做任何处理并且返回最后一句: return render(request,'new_topic.html', {'form': form}) 。
这意味着我们需要更新new_topic.html 以显示错误。 如果请求是 GET,我们只需要使用 form = NewTopicForm() 初始化一个新的空表单。 这个 form 有三个渲染选项: form.as_table , form.as_ul 和form.as_p 。
这是一个快速的渲染表单所有字段的方法。
顾名思义, as_table 使用 table 标签来格式化输入, as_ul 使用 li 标签。 当使⽤ Bootstrap 或者其他的前端库时,我比较喜欢使用一个叫做 djangowidget-tweaks 的 Django 库。
它可以让我们更好地控制渲染的处理,在保证默认值的情况下,只需在上面添加额外的自定义设置。 一些 render_field 模板标签的例子:
{% render_field form.subject class="form-control" %}
{% render_field form.message class="form-control" placeholder=form.message.label %}
{% render_field field class="form-control" placeholder="Writea message!" %}
{% render_field field style="font-size: 20px" %}
#myproject/urls.py
from django.conf.urls import url
from django.contrib import admin
from boards import views urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics'),
url(r'^boards/(?P<pk>\d+)/new/$', views.new_topic, name='new_topic'),
url(r'^admin/', admin.site.urls),
]
#boards/views.py
from django.shortcuts import render, get_object_or_404
from .models import Board def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
return render(request, 'new_topic.html', {'board': board})
<!--templates/new_topic.html--> {% extends 'base.html' %}
{% block title %}Start a New Topic{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
<li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %} {% block content %}
{% endblock %}
<!--templates/new_topic.html--> {% extends 'base.html' %}
{% block title %}Start a New Topic{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
<li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %} {% block content %}
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="id_subject">Subject</label>
<input type="text" class="form-control" id="id_subject"name="subject">
</div>
<div class="form-group">
<label for="id_message">Message</label>
<textarea class="form-control" id="id_message" name="message" rows="5"></textarea>
</div>
<button type="submit" class="btn btn-success">Post</button>
</form>
{% endblock %}
#boards/views.py
from django.contrib.auth.models import User
from django.shortcuts import render, redirect, get_object_or_404
from .models import Board, Topic, Post def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk) if request.method == 'POST':
subject = request.POST['subject']
message = request.POST['message'] user = User.objects.first() # TODO: 临时使??个账号作为登录?户 topic = Topic.objects.create(
subject=subject,
board=board,
starter=user
) post = Post.objects.create(
message=message,
topic=topic,
created_by=user
) return redirect('board_topics', pk=board.pk) # TODO:redirect to the created topic page
return render(request, 'new_topic.html', {'board': board})
<!--templates/topics.html--> {% extends 'base.html' %}
{% block title %}
{{ board.name }} - {{ block.super }}
{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
<li class="breadcrumb-item active">{{ board.name }}</li>
{% endblock %} {% block content %}
<div class="mb-4"> <!--我们已经修改了topics.html模板,让我们创建?个能让我们转到new topic页?的按钮:-->
<a href="{% url 'new_topic' board.pk %}" class="btn btn-primary">New topic</a>
</div>
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Topic</th>
<th>Starter</th>
<th>Replies</th>
<th>Views</th>
<th>Last Update</th>
</tr>
</thead>
<tbody>
{% for topic in board.topics.all %}
<tr>
<td>{{ topic.subject }}</td>
<td>{{ topic.starter.username }}</td>
<td>0</td>
<td>0</td>
<td>{{ topic.last_updated }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
# boards/forms.py
from django import forms
from .models import Topic
class NewTopicForm(forms.ModelForm):
message = forms.CharField(widget=forms.Textarea(), max_length=4000)
class Meta:
model = Topic
fields = ['subject', 'message']
# boards/views.py
from django.contrib.auth.models import User
from django.shortcuts import render, redirect, get_object_or_404
from .forms import NewTopicForm
from .models import Board, Topic, Post def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
user = User.objects.first() # TODO: get the currently logged in user
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save(commit=False)
topic.board = board
topic.starter = user
topic.save()
post = Post.objects.create(
message=form.cleaned_data.get('message'),
topic=topic,
created_by=user
)
return redirect('board_topics', pk=board.pk) # TODO: redirect to the created topic page
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'board': board,'form': form})
<!--我们甚至修复了最后两个测试。-->
<!--Django Forms API 不仅仅是处理和验证数据。它还为我们生成 HTML。-->
<!--templates/new_topic.html--> {% extends 'base.html' %}
{% block title %}Start a New Topic{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
<li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %} {% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success">Post</button>
</form>
{% endblock %}
# boards/forms.py
from django import forms
from .models import Topic class NewTopicForm(forms.ModelForm):
message = forms.CharField(
widget=forms.Textarea(),
max_length=4000,
help_text='The max length of the text is 4000.'
) class Meta:
model = Topic
fields = ['subject', 'message']
# 我们也可以为表单字段设置额外的属性:
# boards/forms.py from django import forms
from .models import Topic class NewTopicForm(forms.ModelForm):
message = forms.CharField(
widget=forms.Textarea(
attrs={'rows': 5, 'placeholder': 'What is on your mind?'}
),
max_length=4000,
help_text='The max length of the text is 4000.'
)
class Meta:
model = Topic
fields = ['subject', 'message']
# 用BootStrap 表单渲染
# pip install django-widget-tweaks
# myproject/settings.py
INSTALLED_APPS = [
'widget_tweaks',
]
# templates/new_topic.html {% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Start a New Topic{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boar ds</a></li>
<li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %} {% block content %}
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
{{ field.label_tag }}
{% render_field field class="form-control" %}
{% if field.help_text %}
<small class="form-text text-muted">
{{ field.help_text }}
</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-success">Post</button>
</form>
{% endblock %}
# 现在要实现 Bootstrap 4 验证标签,我们可以修改 new_topic.html 模板。
# templates/new_topic.html {% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Start a New Topic{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boar ds</a></li>
<li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %} {% block content %}
<form method="post" novalidate>
{% csrf_token %} {% for field in form %}
<div class="form-group">
{{ field.label_tag }} {% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %} {% if field.help_text %}
<small class="form-text text-muted">
{{ field.help_text }}
</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-success">Post</button>
</form>
{% endblock %}
# 复用表单模板
# 模板看起来有点复杂,是吧?有个好消息是我们可以在项目中重复使用它。
# 在 templates 文件夹中,创建一个新的文件夹命名为 includes:
# templates/includes/form.html {% load widget_tweaks %} {% for field in form %}
<div class="form-group">
{{ field.label_tag }} {% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" % }
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %} {% if field.help_text %}
<small class="form-text text-muted">
{{ field.help_text }}
</small>
{% endif %}
</div>
{% endfor %}
<!--templates/new_topic.html--> {% extends 'base.html' %}
{% block title %}Start a New Topic{% endblock %} {% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
<li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %} {% block content %}
<form method="post" novalidate>
{% csrf_token %}
{% include 'includes/form.html' %}
<button type="submit" class="btn btn-success">Post</button>
</form>
{% endblock %}
Django入门与实践-第13章:表单处理(完结)的更多相关文章
- Django入门与实践-第16章:用户登录(完结)
# myproject/settings.py LOGIN_REDIRECT_URL = 'home' EMAIL_BACKEND = 'django.core.mail.backends.conso ...
- Django入门与实践-第14章:用户注册(完结)
http://127.0.0.1:8000/signup/ django-admin startapp accounts INSTALLED_APPS = [ 'accounts', ] # mypr ...
- Django入门与实践-第17章:保护视图(完结)
http://127.0.0.1:8000/boards/1/ #boards/views.py from django.contrib.auth.decorators import login_re ...
- Django入门与实践-第21章:迁移(完结)
http://127.0.0.1:8000/boards/1/ python manage.py migrate #boards/models.py class Topic(models.Model) ...
- Django入门与实践-第22章:基于类的视图
http://127.0.0.1:8000/boards/1/topics/2/posts/2/edit/ http://127.0.0.1:8000/ #boards/views.py from d ...
- Django入门与实践-第19章:主题回复(完结)
http://127.0.0.1:8000/boards/1/topics/1/reply/ http://127.0.0.1:8000/boards/1/topics/1/ #myproject/u ...
- Django入门与实践-第26章:个性化工具(完结)
http://127.0.0.1:8000/boards/1/topics/62/reply/ 我觉得只添加内置的个性化(humanize)包就会很不错. 它包含一组为数据添加“人性化(human t ...
- Django入门与实践-第12章:复用模板(完结)
http://127.0.0.1:8000/http://127.0.0.1:8000/boards/1/http://127.0.0.1:8000/boards/2/http://127.0.0.1 ...
- Django入门与实践-第11章:URL 分发(完结)
http://127.0.0.1:8000http://127.0.0.1:8000/boards/1/http://127.0.0.1:8000/boards/2/http://127.0.0.1: ...
随机推荐
- J2SE 8的输入输出--序列化
1. 普通序列化 implements Serializable 继承Serializable接口 class Employee implements Serializable { private S ...
- MS SQL 2005 无法建立用户实例
SC.EXE stop "MSSQL$SQLEXPRESS"RD /S /Q "%USERPROFILE%\Local Settings\Application Data ...
- Python在pycharm中编程时应该注意的问题汇总
1.缩进问题 在 pycharm 中点击 enter 自动进行了换行缩进,此时应该注意:比如 if else 语句,后面跟着打印输出 print 的时候,一定注意是要if语句下的输出还是else ...
- 首届阿里巴巴在线技术峰会,9位大V演讲整理!
https://yq.aliyun.com/articles/57826 感谢参加阿里巴巴在线技术峰会.7月19日的3场专家分享:Blink.Docker.电商互动:7月20日的云数据库十大经典案 例 ...
- java.lang.NullPointerException - 如何处理空指针异常
当应用程序试图null在需要对象的情况下使用时抛出.这些包括: 调用null对象的实例方法. 访问或修改null对象的字段. 把长度null当作一个数组. 像访问或修改null阵列一样访问或修改插槽. ...
- Js 过滤emoji表情...持续补充中..
原文来自: https://www.cnblogs.com/tsjTSJ/p/7065544.html 最全最详细的用JS过滤Emoji表情的输入 在前端页面开发过程中,总会碰到不允许输入框输入e ...
- testng + Ignore 忽略测试方法
使用testng的时候,有时候会忽略掉某些测试方法,暂时不跑,简单整理一下一些方法.转载还请说明下 1.使用@Test(enable=false)方法 @Feature("查询") ...
- hdoj1043 Eight(逆向BFS+打表+康拓展开)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 思路: 由于自己对康拓展开用的太少,看到这个题没想到康拓展开,最开始打算直接转换为数字,但太占内 ...
- iOS9 UIWindow rootViewController
在iOS9中App被其他应用唤起的时候Crash,正常启动或者调试模式都不会Crash. 通过XCode - Window -Device,查看设备的log,如下 Assertion failure ...
- log4j每天,每小时产生一日志文件
log4j每天,每小时产生一日志文件 2016年08月05日 14:14:33 阅读数:6254 一.之前的文章中有log4j的相关配置以及属性的介绍,下面我们先把配置列出来: log4j.roo ...