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: ...
随机推荐
- jsp 回车代替tab 自动切换text焦点
方法一keyCode (IE11以后失效) <html> <head> <meta http-equiv="Content-Type" content ...
- DOS系统变量
%ALLUSERSPROFILE% : 列出所有用户Profile文件位置.%APPDATA% : 列出应用程序数据的默认存放位置.%CD% : 列出当前目录.%CLIENTNAME% : 列出联接到 ...
- Spring boot @ConfigurationProperties 和@Value
@ConfigurationProperties @Value 功能 批量注入配置文件中的属性 一个个指定 松散绑定(松散语法) 支持 不支持 SpEL 不支持 支持 JSR303数据校验 支持 ...
- Qt 的事件
一个事件由一个特定的QEvent子类来表示,如QMouseEvent.QKeyEvent 处理一个事件的方法: 方法一:重新实现部件的paintEvent.mousePressEvent等事件处理函数 ...
- PCM 编码
PCM编码,即无损编码(抽样->量化->编码) 这里的 无损 是个广义概念,任何数字编码都有损,只不过PCM的“损”最小:通常所说的无损编码都是指PCM编码 wav音频参数: 最重要的三个 ...
- 吴裕雄 python 数据处理(2)
import pandas as pd data = pd.read_csv("F:\\python3_pachongAndDatareduce\\data\\pandas data\\hz ...
- node.js实时编译,不需要重启
npm -g install supervisor 全局安装这个,然后编译依一次文件就可以了,之后修改这个文件不用编译也可以了
- Electron mouse events 参数解析
1.https://electronjs.org/docs/api/web-contents 2.通常用: monitorEvents(document.body, 'mouse') 检测正常的值: ...
- 使用httplib打开链接
[使用httplib打开链接] urllib打开网页时,不会自动跳转,而http会自动跳转,所以使用httplib去打开链接,以获取内容. 参考:https://docs.python.org/2.7 ...
- 4. Median of Two Sorted Arrays(Array; Divide-and-Conquer)
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...