Django项目(五)——创建用户输入

参考书籍:《Python编程从入门到实践》—Eric Matthes

目标:

  • 创建一些表单,让用户能够添加主题和条目。

  • 实现一个用户身份验证系统。

  • 创建一个注册页面,让用户创建账户,并设置一些界面只供登录用户访问。

  • 学习如何保护用户数据安全

在当前只有超级用户能够通过管理网站输入数据。因此我们添加几个页面,让用户能够输入数据

添加新主题

用于添加主题的表单

在Django中,创建表单的最简单方式时使用ModelForm,因此我们在models.py所在目录中创建forms.py文件

from django import forms

from .models import Topic

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ['text']
        labels = {'text': ''}
  • 定义了一个名为TopicForm继承自forms.ModelForm的类,只包含一个内嵌的Metal类,它告诉Django根据哪个模型创建表单,以及在表单中包含哪些字段。
  • 根据模型Topic创建一个表单,该表单只包含字符text,并让Django不要为字段text生成标签

URL模式new_topic

把http://localhost:8000/new_topic/设置为添加新主题的url。

在learning_logs/urls.py中添加

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

视图函数new_topic()

视图函数new_topic()需要处理两种情形:①刚进入new_topic网页,它应该显示一个空表单;②对提交的表单数据进行处理,并将用户重定向至网页topics。

修改views.py

先添加三行头文件

from django.http import HttpResponseRedirect
from django.core.urlresolves import reverse
from .forms import TopicForm

如果出现from django.core.urlresolves import reverse ModuleNotFoundError: No module named 'django.core.urlresolves'的报错django.core.urlresolves改为django.urls

添加函数new_topic()

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'))
    context = {'form':form}
    return render(request,'learning_logs/new_topic.html',context)
  • 导入HttpResponseRedirect类,提交主题后我们就重定向到显示topics的页面
  • 函数reverse()根据指定的URL模型确定URL。

GET请求和POST请求

GET请求:从服务器读取数据

POST请求:用户需要通过表单提交信息

函数new_topic()将请求对象作为参数。用户初次请求该网页时,其浏览器将发送GET请求;用户填写并提交表单时,其浏览器将发送POST请求。

所以我们判断用户的请求是POST还是GET,如果是GET就返回一个空表单,是POST就对填好的表单进行处理。

我们使用用户输 入的数据(它们存储在request.POST中)创建一个TopicForm实例,这样对象form将包含用户提交的信息。

函数is_valid()保证用户填写了所有必不可少的信息,而且输入与要求一致。如果输入正确的话就保存页面。并使用reverse()获取页面topics的URL,并将其传递给HttpResponseRedirect(),这将重定向自页面topics。

模板new_topic

创建新模板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 %}
  • action的作用是告诉服务器将提交的表单数据发送到哪里,method让POST请求的方式提交数据
  • 模板{% csrf_token %}来防止攻击者利用表单来获得对服务器未经授权的访问(跨站请求伪造)
  • 我们只需包含模板变量{{% forms.as_p%}}就能让Django自动创建显示表单所需的所有字段,as_p的作用是让Django以段落格式渲染所有表单元素。
  • button则定义了一个按钮

链接到页面new_topic

在topics.html中添加一个到页面new_topic链接

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

添加新条目

如今我们实现了添加新主题的功能,现在增加在新主题中添加信息的功能。

步骤同之前一样:定义URL->编写视图函数和模板->链接到添加新条目的网页。

添加新条目的表单

创建一个与模型Entry的相关联的表单,但这个表单的定制程度比TopicForm要高些:

修改forms.py

...
from .model import Topic,Entry

...
class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['text']
        labels = {'text': ''}
        widgets = {'text': forms.Textarea(attrs={'col': 80})}
  • 多导入了Entry模型
  • 新类继承了forms.ModelForm,包含的Meta类指出了表单基于的模型以及要在表单中包含哪些字段。这里给字段text指定了一个空标签。
  • 我们定义了属性widgets。小部件(widget)是一个HTML表单元素,如单行文本框、多行文本区域或下拉列表。通过设置属性widgets,可覆盖Django选择的默认小部件。通过让 Django使用forms.Textarea,我们定制了字段’text’的输入小部件,将文本区域的宽度设置为80 列,而不是默认的40列。这给用户提供了足够的空间,可以编写有意义的条目。

URL模式new_entry

在添加新条目的页面的URL模式中,需要包含实参topic_id。因为条目必须与特定的主题相关联。

在lerning_logs/urls.py中修改

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

这个URL模式与形为http://localhost:8000/new_entry/ id /的URL匹配,其中 id 是一个与 主题ID匹配的数字。代码(?P<topic_id>\d+)捕获一个数字值,并将其存储在变量topic_id中。请求的URL与这个模式匹配时,Django将请求和主题ID发送给函数new_entry()。

视图函数new_entry()

在views.py中添加

...
form .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)
  • 头文件中添加创建的EntryForm。
  • 调用save()时,我们传递了实参commit=False,让Django创建一个新的条目对象,并将其存储到new_entry中,但不将它保存到数据库中。我们将new_entry的属性topic设置为在这个函数开头从数据库中获取的主题,然后调用save(),且不指定任何实参。这将把条目保 存到数据库,并将其与正确的主题相关联。
  • 其余跟new_topic()函数类似

编写new_entry.html

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

<p><a href="{% '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 %}

<p><a href="{% 'learning_logs:topic' topic.id %}">{{topic}}</a></p>在顶端显示主题,同时也是一个链接

表单的实参action包含URL中的topic_id值,让视图函数能够将新条目关联到正确的主题

链接到页面new_entry

修改topic.html

...
<p>Entries:</p>
<p>
    <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
</p>
<ul>...
...

编辑条目

现在创建一个页面让用户能够编辑既有的条目

URL模式edit_entry

这个页面的URL需要传递要编辑的条目的ID,在learning_logs/urls.py中的urlpatterns添加

#用于编辑条目的页面
url(r'^edit_entry/(?P<entry_id>\d+)/$',views.edit_entry,
    name='edit_entry'),

视图函数edit_entry()

edit_entry()将返回一个表单,让用户能够对条目进行编辑。

页面edit_entry收到GET请求时,edit_entry()将返回一个表单,让用户能够对条目进行编辑。该页面收到POST请求(条目文本经过修订)时,它将修改后的文本保存到数据库中:

from .models import Topic, Entry
...
def edit_entry(request,entry_id):
    """编辑既有条目"""
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic
    if request.method != 'POST':
        #初次请求,使用当前条目填充表单
        form = EntryForm(instance=entry)
    else:
        #POST提交的数据,对数据进行处理
        form = EntryForm(instance=entry,data=request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topic',
                                        args=[topic.id]))
    context = {'entry':entry,'topic':topic,'form':form}
    return render(request,'learning_logs/edit_entry.html',context)

我们需要导入模型Entry。entry = Entry.objects.get(id=entry.id)获得到用户要修改的条目对象,以及与该条目相关联的主题。在请求方法为GET时将执行的if代码块中,我们使用实参instance=entry创建一个EntryForm实例。这个实参让Django创建一个表单,并使用既有条目对象中的信息填充它。 用户将看到既有的数据,并能够编辑它们。

处理POST请求时,我们传递实参instance=entry和data=request.POST,让Django根据既有条目对象创建一个表单实例,并根据request.POST中的相关数据对其进行修改。然后,我们检查表单是否有效,如果有效,就调用save(),且不指定任何实参。接下来,我们重定向到显示条目所属主题的页面,用户将在其中看到其编辑的条目的新版本。

编写模板edit_entry.html

同样在index.html所在目录下添加

{% extends "learning_logs/base.html" %}
{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{topic}}</a></p>
<p>Edit entry:</p>
<form action="{% url 'learning_logs:edit_entry' entry.id%}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">save changes</button>
</form>
{% endblock content %}

实参action将表单发回给函数edit_entry()进行处理。在标签{% url %}中,我们将条目ID作为一个实参,让视图对象能够修改正确的条目对象。

链接到edit_entry

在topic.html中修改

...
{% for entry in entries %}
      <li>
           <p>{{entry.date_added|date:'M d, Y H:i' }}</p>
           <p>{{entry.text|linebreaks}}</p>
           <p>
               <a href="{% url 'learning_logs:edit_entry' entry.id %}">editentry</a>
           </p>
      </li>>
...

效果图:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值