Django表单

    Django表单的一个好处就是我们既可以从零开始自定义,也可以创建ModelForm,它将表单的结果保存到模型里。

    这正是我们想做的:我们将为我们自己的Post模型创建一个表单。

    就像所有Django的重要部分一样,表单有他们自己的文件forms.py。.

    我们需要创建一个文件,把它的名字放在blog目录下。

    好吧,让我们打开它,然后键入以下代码:

    1. from django import forms
    2. from .models import Post
    3. class PostForm(forms.ModelForm):
    4. class Meta:
    5. model = Post
    6. fields = ('title', 'text',)

    首先我们需要导入Django表单(from django import forms)然后,显然是我们的Post模型(`from .models import Post

    PostForm, 正如你所猜想的,是我们表单的名字。 我们需要告诉Django,这个表单是一个ModelForm(所以Django将会为我们变一些魔法)—forms.ModelForm`对此负责。

    下面,我们有class Meta,在这里我们告诉Django哪个模型会被用来创建这个表单(model=Post)。).

    最后,我们可以说哪些字段会在我们的表单里出现。 在这个场景里,我们只想要titletext显示出来—author应该是当前登录的人(你!)然后created_date应该是我们创建文章时自动分配的(比如,在代码里),对吗?

    就是这样!现在我们所有要做的就是在视图里使用表单,然后展现在在模板里。

    所以下次我们将会创建:一个指向页面的链接,一个URL,一个视图和一个模板。

    是时候打开blog/templates/blog/base.html了。我们将添加一个链接到div,命名为page-header

    1. <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>

    请注意我们想要调用我们的新视图post_new.

    添加了新的行后,你的html文件现在应该看起来像这样:

    1. {% load staticfiles %}
    2. <html>
    3. <head>
    4. <title>Django Girls blog</title>
    5. <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    6. <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
    7. <link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
    8. <link rel="stylesheet" href="{% static 'css/blog.css' %}">
    9. </head>
    10. <body>
    11. <div class="page-header">
    12. <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
    13. <h1><a href="/">Django Girls Blog</a></h1>
    14. </div>
    15. <div class="content container">
    16. <div class="row">
    17. <div class="col-md-8">
    18. {% block content %}
    19. {% endblock %}
    20. </div>
    21. </div>
    22. </div>
    23. </body>

    然后保存,刷新 页面,你可以明显地看到一个熟悉的NoReverseMatch错误信息,是吧?

    URL

    我们打开blog/urls.py然后添加一个新行:

    1. url(r'^post/new/$', views.post_new, name='post_new'),

    最终代码会看起来像这样:

    1. from django.conf.urls import url
    2. from . import views
    3. urlpatterns = [
    4. url(r'^$', views.post_list, name='post_list'),
    5. url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
    6. url(r'^post/new/$', views.post_new, name='post_new'),
    7. ]

    刷新网页后,我们看到一个AttributeError,因为我们没有实现post_new视图。让我们现在把它加上吧。

    post_new视图

    现在打开blog/views.py文件,加入下面的各行到行下:

    1. from .forms import PostForm

    还有我们的view

    1. def post_new(request):
    2. form = PostForm()
    3. return render(request, 'blog/post_edit.html', {'form': form})

    为了创建一个新的Post表单,我们需要调用PostForm(),然后把它传递给模板。 我们会回到这个视图,但是现在,让我们为这个表单快速创建一个模板。

    • 要展示表单,我们只需要很简单地加上{% raw %}{{ form.as_p }}{% endraw %}.
    • 上面的这行需要被HTML表单标签包裹:<form method="POST">...</form>
    • 我们需要一个Save按钮。我们通过使用一个HTML按钮来完成:<button type="submit">Save</button>
    • 最后在<form ...>标签后,我们需要加上{% raw %}{% csrf_token %}{% endraw %}。 这个非常重要,因为他会让你的表单变得更安全! Django会提醒你,当你试图保存表单而你又恰巧忘了这一点:

    好,让我们看看HTML 在post_edit.html里应该看起来什么样:

    现在刷新!哇!你的表单显示出来了!

    新表单

    但是,请等一下!当你键入诸如titletext字段,然后视图保存它—下面会发生什么?

    什么都没有!我们再一次回到了同一个页面,然而我们的文本已经消失了…同时没有新的文章被发布。所以错在哪里了呢?

    答案是:没有错误。我们需要在我们的视图里做更多的工作.

    保存表单

    再一次打开blog/views,py。我们在看到post_new中的视图内容是:

    1. def post_new(request):
    2. form = PostForm()
    3. return render(request, 'blog/post_edit.html', {'form': form})

    当我们提交表单,我们都回到相同的视图,但是这个时候我们有一些更多的数据在 request,更具体地说在 request.POST (命名和博客后缀”post”无关,它只是用来帮我们”上传”数据)。 还记得在HTML文件里,我们的<form>定义有一个方法method="POST"? 现在所有从表单来的东西都在request.POST. 你不应该重命名POST为其他任何东西(其他唯一有效的method值是GET,但是我们没有时间去解释它们两者的区别是什么)。

    所以在我们的视图里,我们有了两种不同的情况去处理。 首先:当我们首次访问一个页面,我们想要得到一个空白的页面。 第二:当我们回到视图,要有我们所有我们刚刚键入的数据。 所以我们需要添加一个条件判断(我们为此使用if)。

    1. if request.method == "POST":
    2. [...]
    3. else:
    4. form = PostForm()

    现在去填写[...]。如果methodPOST,那么我们要用表单里的数据构建PostForm,对吗?我们会这样做:

    1. form = PostForm(request.POST)

    很容易吧!下一件事情就是去检查表单是否正确(所有必填字段都要被设置并且不会保存任何不正确的值)。我们将使用form.is_valid()来实现.

    我们检查表单是否正确,如果是我们就保存它!

    1. if form.is_valid():
    2. post = form.save(commit=False)
    3. post.author = request.user
    4. post.published_date = timezone.now()
    5. post.save()

    基本上,我们这里有两件事情:我们使用form.save保存表单,我们添加一个作者(因为 PostForm 中没有author字段,然而这个字段是必须的!)。 commit=False意味着我们还不想保存Post模型—我们想首先添加作者。 大多数情况下,当你使用form.save()时,不会使用commit=False,但是在这种情况下,我们需要这样做。 post.save()会保留更改(添加作者),并创建新的博客文章!

    最后,如果我们能够立即去post_detail页面创建新的博客内容,那将很酷,对吗?为了做到这点,我们需要再导入一个:

    1. from django.shortcuts import redirect

    把它添加到你文件的最开始处。现在我们可以说:创建完新帖子我们就转去post_detail页面。

    1. return redirect('post_detail', pk=post.pk)

    post_detail 是我们想去的视图的名字。 还记得这个视图 需得具有一个 pk 变量吗? 为了把它传递给视图我们使用pk=post.pk, 其中 post 就是我们刚刚创立的博客帖子!

    好吧,我们已经说了很多了,但可能我们想看到整个视图现在看起来什么样,对吗?

    1. def post_new(request):
    2. if request.method == "POST":
    3. form = PostForm(request.POST)
    4. if form.is_valid():
    5. post = form.save(commit=False)
    6. post.author = request.user
    7. post.published_date = timezone.now()
    8. return redirect('post_detail', pk=post.pk)
    9. else:
    10. return render(request, 'blog/post_edit.html', {'form': form})

    让我们看看它是否正常工作。 转到页 http://127.0.0.1:8000//post/new/ ,添加 titletext,将它保存… 看! 新博客文章已经加进来了,我们被重定向到post_detail页面!

    你可能已经注意到在保存博客文章之前我们设置发布日期。稍后,我们讲介绍一个在 Django Girls 教程:扩展中介绍 publish button 。.

    太棒了!

    表单验证

    现在,我们将给你展现Django表单是多么酷。 一篇博客文章需要有titletitle字段。 在我们的Post模型中我们并没有说(和发布日期恰恰相反)这些字段不是必须的,所以Django,默认期望他们是有存储数据的。

    尝试不带titletext内容保存表单。猜猜,会发生什么!

    Django会处理验证我们表单里的所有字段都是正确的。这不是很棒?

    现在我们知道如何添加一个新的表单。 但是如果我们想编辑一个现有的呢? 这和我们刚才做的非常相似。 让我们快速创建一些重要的东西(如果你还不清楚他们,你应该问问你的教练或者看看前面的章节,因为我们已经覆盖了所有的这些步骤)。

    打开 blog/templates/blog/post_detail.html 并添加以下行:

    所以模板看起来像这样:

    1. {% extends 'blog/base.html' %}
    2. {% block content %}
    3. <div class="post">
    4. {% if post.published_date %}
    5. <div class="date">
    6. {{ post.published_date }}
    7. </div>
    8. {% endif %}
    9. <a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
    10. <h1>{{ post.title }}</h1>
    11. <p>{{ post.text|linebreaksbr }}</p>
    12. </div>
    13. {% endblock %}

    blog/urls.py里我们添加这行:

    1. url(r'^post/(?P<pk>[0-9]+)/edit/$', views.post_edit, name='post_edit'),

    我们将复用模板blog/templates/blog/post_edit.html,所以最后缺失的东西就是 view.

    让我们打开blog/views.py,并在文件的最后加入:

    1. def post_edit(request, pk):
    2. post = get_object_or_404(Post, pk=pk)
    3. if request.method == "POST":
    4. form = PostForm(request.POST, instance=post)
    5. if form.is_valid():
    6. post = form.save(commit=False)
    7. post.author = request.user
    8. post.published_date = timezone.now()
    9. post.save()
    10. return redirect('post_detail', pk=post.pk)
    11. else:
    12. form = PostForm(instance=post)
    13. return render(request, 'blog/post_edit.html', {'form': form})

    这看起来几乎完全和我们的post_new视图一样,对吗? 但是不完全是。 第一件事:我们从urls里传递了一个额外的pk参数。 然后:我们得到了Post模型,我们想编辑get_object_or_404(Post, pk=pk),然后当我们创建了一个表单我们用一个实例来传递这篇文章,当我们想保存它:

    1. form = PostForm(request.POST, instance=post)

    当我们只是打开这篇文章的表单来编辑时:

    1. form = PostForm(instance=post)

    好,让我们来试试它是否可以工作!让我们先去post_detail页面。在右上角应该有一个编辑按钮:

    编辑按钮

    当你点击它的时候,你会看到我们博客文章的表单。

    随意修改标题和内容,然后保存更改!

    祝贺你!你的应用程序正在变得越来越完整!

    如果你需要更多关于Django表单的信息,你应该阅读文档:

    安全性

    能够通过点击一条连接进行发布确实不错。 但是现在,任何访问你网站的人都能够发布一条新博客日志,这可能不是你想要的。 那让我们来让这个发布按钮只显示给你,对其他人则不显示。

    blog/templates/blog/base.html中,找到我们 page-header div 和你早些时候在放那里锚点标记。看起来应该像这样:

    1. <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>

    我们要将另一个 {% if %} 标记到这, 这会使链接仅在以管理者身份登录的用户访问时显示。现在来说,管理员就是你! 像这样修改 <a> 标记:

    1. {% if user.is_authenticated %}
    2. <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
    3. {% endif %}

    这个 {% if %} 会使得链接仅仅发送到哪些已经登陆的用户的浏览器。 这并不能完全保护发布新文章,不过这是很好的第一步。 我们将在扩展课程中包含更多安全部分。

    你应已登录,如果你刷新页面,你不会看到有什么不同。不过,在新的浏览器或隐身窗口中加载页,你会看不到这个链接!

    还有一件事:部署时刻!

    • 首先,提交你的新代码,然后将它推送到 Github 上
    1. $ cd my-first-blog
    2. $ source myvenv/bin/activate
    3. (myvenv)$ git pull
    4. [...]
    5. (myvenv)$ python manage.py collectstatic
    • 最后,跳到 并点击重新载入.

    就是这样!祝贺你:)