Django入门(二)

跟书《python编程:从入门到实践》,学习用Django编写名为“学习笔记”的Web应用程序。

Web应用程序的核心是让任何用户都能够注册账户并能够使用它,不管用户身处何方。我们可以创建一些表单,让用户能够添加主题和条目,以及编辑既有的条目。


让用户能够输入数据

先来添加几个页面,让用户能够输入数据。可以让用户能够添加新主题、添加新条目以及编辑既有条目,但不能通过管理网站来输入,因为只有超级用户才可以这样做。


添加新主题

首先来让用户能够添加新主题。创建基于表单的页面的方法几乎与前面创建网页一样:定义一个URL,编写一个视图函数并编写一个模板。一个主要差别是,需要导入包含表单的模块forms.py

  • 用于添加主题的表单:

在Django中,创建表单的最简单方式是使用ModelForm。创建一个名为forms.py的文件,将其存储到models.py所在的目录中,并在其中编写第一个表单。

# vim learning_logs/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,且让Django不要为字段text生成标签。

  • URL模式new_topic
# vim learning_logs/urls.py
"""定义learning_logs的URL模式"""

from django.urls import path, re_path
from . import views

app_name='learning_logs'
urlpatterns = [
    # 主页
    path('', views.index, name='index'),

    # 显示所有的主题
    path('topics/', views.topics, name='topics'),

    # 特定主题的详细页面
    re_path(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
    
    # 用于添加新主题的页面
    path('new_topic/', views.new_topic, name='new_topic'),
]

这个URL模式会将请求交给视图函数new_topic()

  • 视图函数new_topic()

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

# vim learning_logs/views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse

from .models import Topic
from .forms import TopicForm

def index(request):
    """学习笔记的主页"""
    return render(request, 'learning_logs/index.html')

def topics(request):
    """显示所有主题"""
    topics = Topic.objects.order_by('date_added')
    context = {
   'topics': topics}
    return render(request, 'learning_logs/topics.html', context)

def topic(request, topic_id):
    """显示一个主题及其详细页面"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {
   'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

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类和刚才创建的表单TopicForm,用户提交主题后我们将使用HttpResponseRedirect类将用户重定向到网页topics。函数reverse()根据指定的URL模型确定URL,这意味着Django将在页面被请求时生成URL。

首先判断请求方法是否是POST——如果请求方法不是POST,返回一个空表单;如果请求方法是POST,将使用用户输入的数据(存储在request.POST中)创建一个TopicForm实例,这样对象form将包含用户提交的信息。

然后要将提交的信息保存到数据库,必须先通过检查确定它们是有效的。函数is_valid()核实用户填写了所有必不可少的字段(表单字段默认都是必不可少的),且输入的数据与要求的字段类型一致。如果所有字段都有效,我们就调用save()将表单中的数据写入数据库。

最后使用reverse()获取页面topics的URL,并将其传递给HttpResponseRedirect(),后者将用户的浏览器重定向到页面topics。在页面topics中,用户将在主题列表中看到他刚输入的主题。

  • GET请求和POST请求:

创建Web应用程序时,将用到的两种主要请求类型是GET请求和POST请求。对于只是从服务器读取数据的页面,使用GET请求;在用户需要通过表单提交信息时,通常使用POST请求。处理所有表单时,我们都将指定使用POST方法。

函数new_topic()将请求对象作为参数。用户初次请求该网页时,其浏览器将发送GET请求;用户填写并提交表单时,其浏览器将发送POST请求。根据请求的类型,我们可以确定用户请求的是空表单(GET请求)还是要求对填写好的表单进行处理(POST请求)。

  • 模板new_topic
# vim learning_logs/templates/learning_logs/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,然后定义了一个HTML表单。实参action告诉服务器将提交的表单数据发送到哪里,这里我们将它发回给视图函数new_topic()。实参method让浏览器以POST请求的方式提交数据。

Django使用模板标签{% csrf_token %}来防止攻击者利用表单来获得对服务器未经授权的访问(这种攻击被称为跨站
请求伪造)。接着显示表单,只需包含模板变量{ { form.as_p }},就可让Django自动创建显示表单所需的全部字段。修
饰符as_p让Django以段落格式渲染所有表单元素。

  • 链接到页面new_topic
# vim learning_logs/templates/learning_logs/topics.html
{% extends "learning_logs/base.html" %}

{% block content %}

  <p>Topics</p>

  <ul>
    {% for topic in topics %}
      <li>
        <a href="{% url 'learning_logs:topic' topic.id %}">{
  { topic }}</a>
      </li>
    {% empty %}
      <li>No topics have been added yet.</li>
    {% endfor %}
  </ul>

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

{% endblock content %}
  • 访问网页:

在这里插入图片描述

在这里插入图片描述


添加新条目

现在已经可以添加新主题了,但是还不能添加新条目,再次定义URL,编写视图函数和模板,并链接到添加新条目的网页。

  • 用于添加新条目的表单:

创建一个与模型Entry相关联的表单。

# vim learning_logs/forms.py
from django import forms

from .models import Topic, Entry

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

class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ['text']
        labels = {
   'text': ''}
        wgdgets = {
   'text': forms.Textarea(attrs={
   'cols': 80})}

除了导入Topic外,还需要导入Entry。新类EntryForm继承了forms.ModelForm,它包含的Meta类指出了表单基于的模型以及要在表单中包含哪些字段。

后面定义了属性widgets,小部件(widget)是一个HTML表单元素,如单行文本框、多行文本区域或下拉列表。通过设置属性widgets,可覆盖Django选择的默认小部件。通过让Django使用forms.Textarea,我们定制了字段'text'的输入小部件,将文本区域的宽度设置为80列,而不是默认的40列。

  • URL模式new_entry

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

# vim learning_logs/urls.py
"""定义learning_logs的URL模式"""

from django.urls import path, re_path
from . import views

app_name='learning_logs'
urlpatterns = [
    # 主页
    path('', views.index, name='index'),

    # 显示所有的主题
    path('topics/', views.topics, name='topics'),

    # 特定主题的详细页面
    re_path(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),

    # 用于添加新主题的页面
    path('new_topic/', views.new_topic, name='new_topic'),

    # 用于添加新条目的页面
    re_path(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()

视图函数new_entry()与视图函数new_topic()比较类似。

# vim learning_logs/views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse

from .models import Topic
from .forms import TopicForm, EntryForm

def index(request):
    """学习笔记的主页"""
    return render(request, 'learning_logs/index.html')

def topics(request):
    """显示所有主题"""
    topics = Topic.objects.order_by('date_added')
    context = {
   'topics': topics}
    return render(request, 'learning_logs/topics.html', context)

def topic(request, topic_id):
    """显示一个主题及其详细页面"""
    topic = Topic.objects.get(id=topic_id)
    entries = topic.entry_set.order_by('-date_added')
    context = {
   'topic': topic, 'entries': entries}
    return render(request, 'learning_logs/topic.html', context)

def new_topic(request):
    """添加新主题"""
    if request.method != 'POST':
        # 未提交数据:创建一个新表单
        form = TopicForm()
    else:
        # POST提交的数据,对数据进行处理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(re
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值