Django表单的一个好处就是我们既可以从零开始自定义,也可以创建ModelForm
,它将表单的结果保存到模型里。
表单有他们自己的文件, forms.py
把它放在 blog
目录下。
blog
└── forms.py
编辑 forms.py
,然后写入以下代码。
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
from django import forms
导入Django表单from .models import Post
从 models 导入 Post
模型class PostForm(forms.ModelForm):
class 是定义Django 名称 PostForm 是我们表单的名字,意思是告诉Django,这个 forms(表单)是一个ModelForm。class Meta
,告诉Django model = Post
模型 是postfields = ('title', 'text',)
是需要填入的内容。
在base.html 增加一个 发帖链接
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
就放在<div class="page-header">
的后面, 博客标题的前面。
放好后是这样。
{% load staticfiles %}
<html>
<head>
<title>末伏的博客</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
<link href="https://fonts.googleapis.com/css?family=Baloo+Tammudu" rel="stylesheet">
</head>
<body>
<div class="page-header">
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<h1><a href="/">末伏的博客</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
访问页面 提示错误, 很好理解,我们没有给这个 加上链接。
URL
我们打开blog/urls.py
然后添加一个新行:
url(r'^post/new/$', views.post_new, name='post_new'),
最终代码会看起来像这样:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.post_list, name='post_list'),
url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
url(r'^post/new/$', views.post_new, name='post_new'),
]
刷新网页后,我们看到一个AttributeError
,因为我们没有实现post_new
视图。让我们现在把它加上吧。
post_new视图
现在打开blog/views.py
文件,加入下面的各行到from
行下:
from .forms import PostForm
还有我们的view:
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
为了创建一个新的Post
表单,我们需要调用PostForm()
,然后把它传递给模板。 我们会回到这个视图,但是现在,让我们为这个表单快速创建一个模板。
但是点击进去 报错,不存在,pos_edit.html
创建 post_edit.html 模板
我们需要在blog/templates/blog
目录下创建一个文件post_edit.html
。为了创建一个表单,我们需要几件事情:
- 要展示表单,我们只需要很简单地加上
{{ form.as_p }}
. - 上面的这行需要被HTML表单标签包裹:
<form method="POST">...</form>
- 我们需要一个
Save
按钮。我们通过使用一个HTML按钮来完成:<button type="submit">Save</button>
- 最后在
<form ...>
标签后,我们需要加上{% csrf_token %}
。 这个非常重要,因为他会让你的表单变得更安全! Django会提醒你,当你试图保存表单而你又恰巧忘了这一点:
post_edit.html 完整内容
{% extends 'blog/base.html' %}
{% block content %}
<h1>New post</h1>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}
可以显示,但是输入内容并且保存后,首页不会有。
保存表单
再一次打开blog/views,py
。我们在看到post_new
中的视图内容是:
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
当我们提交表单,我们都回到相同的视图,但是这个时候我们有一些更多的数据在 request
,更具体地说在 request.POST
(命名和博客后缀”post”无关,它只是用来帮我们”上传”数据)。 还记得在HTML文件里,我们的<form>
定义有一个方法method="POST"
? 现在所有从表单来的东西都在request.POST
. 你不应该重命名POST
为其他任何东西(其他唯一有效的method
值是GET
,但是我们没有时间去解释它们两者的区别是什么)。
所以在我们的视图里,我们有了两种不同的情况去处理。 首先:当我们首次访问一个页面,我们想要得到一个空白的页面。 第二:当我们回到视图,要有我们所有我们刚刚键入的数据。 所以我们需要添加一个条件判断(我们为此使用if
)。
if request.method == "POST":
[...]
else:
form = PostForm()
现在去填写[...]
。如果method
是 POST
,那么我们要用表单里的数据构建PostForm
,对吗?我们会这样做:
form = PostForm(request.POST)
很容易吧!下一件事情就是去检查表单是否正确(所有必填字段都要被设置并且不会保存任何不正确的值)。我们将使用form.is_valid()
来实现.
我们检查表单是否正确,如果是我们就保存它!
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
基本上,我们这里有两件事情:我们使用form.save
保存表单,我们添加一个作者(因为 PostForm
中没有author
字段,然而这个字段是必须的!)。 commit=False
意味着我们还不想保存Post
模型—我们想首先添加作者。 大多数情况下,当你使用form.save()
时,不会使用commit=False
,但是在这种情况下,我们需要这样做。 post.save()
会保留更改(添加作者),并创建新的博客文章!
最后,如果我们能够立即去post_detail
页面创建新的博客内容,那将很酷,对吗?为了做到这点,我们需要再导入一个:
from django.shortcuts import redirect
把它添加到你文件的最开始处。现在我们可以说:创建完新帖子我们就转去post_detail
页面。
return redirect('post_detail', pk=post.pk)
post_detail
是我们想去的视图的名字。 还记得这个视图 需得具有一个 pk
变量吗? 为了把它传递给视图我们使用pk=post.pk
, 其中 post
就是我们刚刚创立的博客帖子!
好吧,我们已经说了很多了,但可能我们想看到整个视图现在看起来什么样,对吗?
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
让我们看看它是否正常工作。 转到页 http://127.0.0.1:8000//post/new/,添加 title
和 text
,将它保存… 看! 新博客文章已经加进来了,我们被重定向到post_detail
页面!
你可能已经注意到在保存博客文章之前我们设置发布日期。稍后,我们讲介绍一个在 Django Girls 教程:扩展中介绍 publish button 。.
太棒了!
表单验证
现在,我们将给你展现Django表单是多么酷。 一篇博客文章需要有title
和title
字段。 在我们的Post
模型中我们并没有说(和发布日期
恰恰相反)这些字段不是必须的,所以Django,默认期望他们是有存储数据的。
尝试不带title
和text
内容保存表单。猜猜,会发生什么!
Django会处理验证我们表单里的所有字段都是正确的。这不是很棒?
因为我们最近使用过Django管理界面,系统目前认为我们已经登录了。 有几种情况可能导致我们被登出(关闭浏览器,重新启动数据库等等)。 如果你发现当你创建一个文章时得到了一个指向未登录用户错误的时候,前往管理页面
http://127.0.0.1:8000/admin
,再登录。 这会暂时解决问题。 有一个一劳永逸的方法在等着你,可以看看只要教程后的Homework: add security to your website! 章节。
编辑表单
现在我们知道如何添加一个新的表单。 但是如果我们想编辑一个现有的呢? 这和我们刚才做的非常相似。 让我们快速创建一些重要的东西(如果你还不清楚他们,你应该问问你的教练或者看看前面的章节,因为我们已经覆盖了所有的这些步骤)。
打开 blog/templates/blog/post_detail.html
并添加以下行:
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
所以模板看起来像这样:
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endblock %}
在blog/urls.py
里我们添加这行:
url(r'^post/(?P<pk>[0-9]+)/edit/$', views.post_edit, name='post_edit'),
我们将复用模板blog/templates/blog/post_edit.html
,所以最后缺失的东西就是 view.
让我们打开blog/views.py
,并在文件的最后加入:
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
这看起来几乎完全和我们的post_new
视图一样,对吗? 但是不完全是。 第一件事:我们从urls里传递了一个额外的pk
参数。 然后:我们得到了Post
模型,我们想编辑get_object_or_404(Post, pk=pk)
,然后当我们创建了一个表单我们用一个实例
来传递这篇文章,当我们想保存它:
form = PostForm(request.POST, instance=post)
当我们只是打开这篇文章的表单来编辑时:
form = PostForm(instance=post)
好,让我们来试试它是否可以工作!让我们先去post_detail
页面。在右上角应该有一个编辑按钮:
可以编辑了
如果你需要更多关于Django表单的信息,你应该阅读文档: https://docs.djangoproject.com/en/1.8/topics/forms/
安全性
能够通过点击一条连接进行发布确实不错。 但是现在,任何访问你网站的人都能够发布一条新博客日志,这可能不是你想要的。 那让我们来让这个发布按钮只显示给你,对其他人则不显示。
在 blog/templates/blog/base.html
中,找到我们 page-header
div
和你早些时候在放那里锚点标记。看起来应该像这样:
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
我们要将另一个 {% if %}
标记到这, 这会使链接仅在以管理者身份登录的用户访问时显示。现在来说,管理员就是你! 像这样修改 <a>
标记:
{% if user.is_authenticated %}
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
{% endif %}
这个 {% if %}
会使得链接仅仅发送到哪些已经登陆的用户的浏览器。 这并不能完全保护发布新文章,不过这是很好的第一步。 我们将在扩展课程中包含更多安全部分。
你应已登录,如果你刷新页面,你不会看到有什么不同。不过,在新的浏览器或隐身窗口中加载页,你会看不到这个链接!
现在,在admin退出后就不会有发布和编辑按钮了
部署
- 首先,提交你的新代码,然后将它推送到 Github 上 ``` $ git status $ git add —all . $ git status $ git commit -m “Added views to create/edit blog post inside the site.” $ git push
- 然后,在一个 [PythonAnywhere 的 Bash 终端](https://www.pythonanywhere.com/consoles/)里运行:
$ cd my-first-blog
$ source myvenv/bin/activate
(myvenv)$ git pull
[...]
(myvenv)$ python manage.py collectstatic
[...]
```
- 最后,跳到 Web 标签页 并点击重新载入.