Web的应用程序核心是让任何用户都能注册账户并能够使用它,不管用户身处何方.
现在我们来创建一些表单,让用户能够添加主题和内容(条目)
还略微的知道Django如何防范对基于表单发起的常见攻击,我们根本无需花费太多的时间考虑程序的安全问题.
我们继续:
在Django中,创建表单的最简单方式就是使用ModelForm,他根据我们在定义的模型中的信息自动创建表单,我们来创建一个名为forms.py的文件,存储到models.py的目录

forms.py
from django import forms
from . models import Topic
class TopicForm(forms.ModelForm):
     class Meta:
     model = Topic
     fields = ['text']
     labels = {'text':''}

我们首先导入模块forms,以及要使用的模型Topic,我们定义了一个名为TopicForm的类,他继承了forms.ModelForm
最简单的ModelForm版本,之嵌套了一个Meta类,由他告诉Django根据哪个模型创建表单,以及在表单中包含哪些字段,然后根据模型Topic创建一个表单,该表单只包含字段text.
labels让Django不要为字段text生成标签

这个新网页的url应该简短且具有描述性,因此当用户要添加新的主题时,我们将切换到/new_topic/
urls.py

#用于添加新主题的网页
url(r'^new_topic/$',views.new_topic,name='new_topic'),
]

这个url模式将提交给视图函数new_topic(),接下来我们将编写这个函数

views.py

from django.http import HttpResponseRedirect
from django.urls import reverse #如果你看到有人和我这里调用的不同,我将不再解释Django版本的更新问题,新版本Django已经把包都更改为了urls包
from .forms import TopicForm

def new_topic(request):
    #添加新主题
    if request.method != 'POST':
        #未提交数据,创建一个新表单
        form = TopicForm()
    else:
        #POST提交数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topics'))#nimade这么长
context = {'form':form}
return render(request,'learning_logs/new_topic.html',context)

我们导入HttpResponseRedirect类,用户提交主题后我们将使用这个类将用户重定向到网页topics。
函数reverse()根据指定的url模型确定url,这意味着Django将在页面被请求时生成url
我们还导入了刚才创建的飙到TopicForm
函数is_valid()核实用户填写了所有必不可少的字段(表单字段默认都是必不可少的),而且输入的数据与要求的字段类型一致,例如字段text少于200个字符.这是在以前models.py中指定的
如果所有字段都有效,我们就可调用save()将表单中的数据写入数据库.保存数据后就可以离开这个页面了,我们使用reverse()获取页面topics的url,将其传递给HttpResponseRedirect(),后者将用户的浏览器重定向到页面topics,在页面topics中用户将在主题列表中看到他刚输入的主题.
下面我们来创建新模板new_topic.html
new_topic.html

{% extends "learning_logs/base.html" %}

{% block content %}
  <p>Add a new topic:</p>
  <form action="{% url 'learning_logs:new_topic' %}" method='post'>
      {% csrf_token %}
      {{ form.as_p }}
      <button name="submit">add topic</button>
  </form>
{% endblock content %}

这个模板继承了base.html,实参action告诉服务器将提交的表单数据发送到哪里,这里我们将它发回给视图函数new_topic().实参method让浏览器以post请求的方式提交数据.
Django使用模板标签{% csrf_token %}来防止攻击者利用表单来获得对服务器未经授权的访问,这种攻击被称为跨站请求伪造.
form.as_p显示表单,从中可知Django是的完成显示表单等任务有多简单.我们只需要包含模板变量{}{ form.as_p }}就可以让Django自动创建显示表单所需的全部字段,修饰符as_p让Django以段落格式渲染所有表单元素,这是一种整洁地显示表单的简单方式.
Django不会为表单创建提交按钮,于是我们使用html语言添加了一个按钮(button)
接下来,我们在页面topics中添加一个到页面new_topic的链接

  <a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a>
{% endblock content %}

这个链接放在了既有主题列表的后面,现在你可以开始添加几个新主题了

但是用户还想添加新的条目(文章),我们将再次定义url,编写视图函数和模板,并链接到添加新条目的网页,但是在此之前,我们需要在forms.py中新增一个类
我们需要创建一个与模型Entry相关联的表单

from .models import Topic,Entry#除了导入Topic这里还需要导入Entry
class EntryForm(forms.ModelForm):#新类EntryForm继承了forms.ModelForm
    class Meta:
        model = Entry
        fields = ['text']
        labels = {'text':''}
        widgets = {'text':forms.Textarea(attrs={'cols':80})}#html的小部件widget

小部件widget是一个HTML表单元素,入单行文本框/多行文本区域或下拉列表.通过设置属性widgets,可覆盖Django选择的默认小部件.通过让Django使用forms.Textarea,我们定制了字段'text'的输入小部件,将文本区域设置为80列,而不是默认的40列.这给用户提供了足够空间.
在添加新条目的页面的url模式中,需要包含实参topic_id,因为条目必须与特定的主题相关联.
该url模式如下
urls.py

    #添加新条目的网页
    url(r'^new_entry/(?P<topic_id>\d+)/$',views.new_entry,name='new_entry'),

这个url模式与形式为/new_entry/id/ 的url匹配,其中id是一个与主题匹配的数字,在正则表达式捕获一个数字值,并将其存储在标量topic_id中,请求的url与这个模式匹配时,Django将请求和主题id发送给函数enw_entry()

视图函数new_entry()与函数new_topic()很像

from .forms import TopicForm,EntryForm
def new_entry(request,topic_id):
    '''在特定的主题中添加新条目'''
    topic = Topic.objects.get(id=topic_id)
    if request.method != 'POST':
        #未提交数据创建一个空表单
        form = EntryForm()
    else:
        #POST提交的数据,对数据进行处理
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            new_entry.save()
            return HttpResponseRedirect(reverse('learning_logs:topic',args=[topic_id]))
    context = {'topic':topic,'form':form}
    return render(request,'learning_logs/new_entry.html',context)

我们修改了import语句,在其中包含了刚创建的EntryForm.new_entry()的定义包含形参topic_id,用于存储从url中获得的值,渲染页面以及处理表单数据时,都需要知道针对的是哪个主题.因此我们使用topic_id来获得正确的主题.
调用save()时,我们传递了实参commit=Flase,让Django创建一个新的条目对象,并将其存储到new_entry中,但是不将他保存到数据库.我们将new_entry的属性topic设置为在这个函数开头从数据库中获取的主题,然后调用save(),且不指定任何实参.这将条目保存到数据库,并将其与正确的主题关联.
调用reverse()时,需要提供两个实参,要根据他来生成url的url模式名称,列表args,其中包含要包含在url中的所有实参.在这里列表args只有一个元素:topic_id.接下来调用HttpResponseRedirect()将用户重定向到显示新增条目所属的主题页面,用户将在该页面条目列表中看到新添加的条目
模板new_entry.html

{% extends "learning_logs/base.html" %}
{% block content %}
  <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a> </p>

  <p>Add a new entry:</p>
  <form action="{% url 'learning_logs:new_entry' topic.id %}"method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">add entry</button>
  </form>

{% endblock content %}

表单的实参action包含url中的topic_id值,让视图函数能够将新条目正确关联到正确的主题,除此之外这个模板与new_topic.html完全相同

接下来,我们需要在显示特定主题的页面中添加到页面new_entry的链接:
topic.html

{% extends 'learning_logs/base.html' %}
{% block content %}

    <p>Topic:{{ topic }}</p>

    <p>Entries:</p>
    <p>
      <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
    </p>
    <ul>
    {% for entry in entries %}
        <li>
            <p>{{ entry.date_added|date:'M d,Y H:i' }}</p>
            <p>{{ entry.text|linebreaks }}</p>
        </li>
    {% empty %}
        <li>
            There are no entries for this topic yet.
        </li>
    {% endfor %}
    </ul>
{% endblock content %}

最后修改:2022 年 12 月 05 日
如果觉得我的文章对你有用,请随意赞赏