【原文翻译】Django by Example·第一章|Building a Blog Application(开发个人博客系统)@笔记

Django by Example·第一章|Building a Blog Application(开发个人博客系统)@笔记

Antonio Melé写的这本书相信很多朋友都听过,结构内容都可圈可点,遂做了一点记录,以备不时之需。


部分内容引用自原书,如果大家对这本书感兴趣
请支持原版Django by Example·Antonio Melé
在这里插入图片描述


前言·第一章

第一章主要介绍了Django的基本用法和一些必要的基础知识。主要包含了一下内容:

  • Installing Django and creating your first project
  • Designing models and generating model migrations
  • Creating an administration site for your models
  • Working with QuerySet and managers
  • Building views, templates, and URLs
  • Adding pagination to list views
  • Using Django class-based views

1 Django 的安装

Django在Python 2.7或3版本中运行良好。在本书的示例中,使用的是Python3。如果你使用的是Linux或Mac OS X,则可能安装了Python。如果您不确定您的计算机中是否安装了Python,可以通过在终端中键入Python来验证。如果您看到类似以下内容,则您的计算机中安装了Python:

Python 3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information. >>>

如果您安装的Python版本低于3,或者您的计算机上未安装Python,请从http://www.python.org/download/并安装它。

1.1 创建独立的python环境

建议您使用virtualenv创建独立的Python环境,这样您就可以为不同的项目使用不同的包版本,这比在系统范围内安装Python包更实用。使用virtualenv的另一个优点是安装Python包不需要任何管理权限。在shell中运行以下命令以安装virtualenv:

pip install virtualenv

安装virtualenv后,使用以下命令创建隔离环境:

virtualenv my_env

这将创建包含Python环境的my_env/目录。在虚拟环境处于活动状态时安装的任何Python库都将进入my_env/lib/python3.5/site-packages目录。

激活虚拟环境(在my_env\scripts目录下):

activate

这样就进入了虚拟环境中。您可以用deactivate退出虚拟环境。

1.2 安装Django

进入虚拟环境之后,您可以使用pip安装django:

pip install Django==1.8.6

Django将安装在虚拟环境的Python site-packages/目录中。

现在检查Django是否已成功安装。在终端上运行python并导入Django以检查其版本:

import django
django.VERSION
django.VERSION(1, 8, 5, ‘final’, 0)

如果您得到这个输出,Django已经成功安装在您的机器上。

Django可以通过其他几种方式安装。您可以在以下位置找到完整的安装指南https://docs.djangoproject.com/en/1.8/topics/install/.


2 创建您的第一个项目

书中的第一个Django项目是一个完整的博客网站。Django提供了一个命令,允许您轻松创建初始项目文件结构。从shell运行以下命令:

django-admin startproject mysite 

这将创建一个名为mysite的Django项目。

让我们看看生成的项目结构:
Django项目结构

这些文件如下:

  • manage.py: 用于与项目交互的命令行程序。它是django-admin.py工具的一个封装。您不需要编辑此文件。

  • mysite/:项目目录。包含以下文件:

    • _ init_.py:一个空文件,告诉Python将mysite目录视为Python模块。
    • settings.py: 项目的设置和配置文件。包含初始默认设置,后期做项目配置需要编辑此文件。
    • urls.py: URL模式所在的位置。此处定义的每个URL都映射到一个视图,或者应用的url模块。
    • wsgi.py:将项目作为wsgi应用程序运行的配置文件。

执行迁移:

cd mysite
python manage.py migrate

执行迁移后,初始应用程序的表已在数据库中创建。


3 启动开发服务器

Django自带一个轻量级的web服务器,可以快速运行代码,而无需花费时间配置生产服务器。当您运行Django开发服务器,它不断检查代码中的更改。它会自动重新加载,使您在代码更改后不再手动重新加载。但是,它可能不会注意到某些操作,例如向项目中添加新文件,因此在这些情况下,您必须手动重新启动服务器。

通过在项目的根文件夹中键入以下命令启动开发服务器:

python manage.py runserver

然后你可以看到:

Performing system checks…
System check identified no issues (0 silenced). November 5, 2015 - 19:10:54
Django version 1.8.6, using settings ‘mysite.settings’ Starting development server at http://127.0.0.1:8000/Quit the server with CONTROL-C.

现在,打开URLhttp://127.0.0.1:8000/在浏览器中。您应该会看到一个页面,一个起飞的小火箭,看到这个,说明你成功了。

您可以指示Django在自定义主机和端口上运行开发服务器,或者告诉它您想加载不同的设置文件来运行项目。例如,可以按如下方式运行manage.py命令:

python manage.py runserver 127.0.0.1:8001 --settings=mysite.settings

这对于处理需要不同设置的多个环境非常方便。请记住,此服务器仅用于开发,不适合用于生产。为了在生产环境中部署Django,您应该使用真正的Web服务器(如Apache、Gunicorn或uWSGI)将其作为Web服务器网关接口(WSGI)应用程序运行。关于如何在不同的web服务器上部署Django的更多信息,请访问https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/。


4 项目的配置

让我们打开settings.py文件,看看我们项目的配置。Django在这个文件中包含了几个设置,但这些只是Django所有可用设置的一部分。您可以在中查看所有设置及其默认值https://docs.djangoproject.com/en/1.8/ref/settings/.

4.1 值得一看的配置项

  • DEBUG : 是一个布尔值,用于打开/关闭项目的调试模式。如果设置为True,当应用程序抛出未捕获的异常时,Django将显示详细的错误页面。当您将项目部署到生产环境时,请记住必须将其设置为False。切勿在启用DEBUG的情况下将站点部署到生产环境中,因为这样会暴露项目的敏感数据。

  • ALLOWED_HOSTS:在调试模式打开或运行测试时不应用。一旦您要将站点移动到生产环境并设置调试,如果设置为False,则必须将域名/主机添加到ALLOWED_HOSTS中让它为Django网站服务。

  • INSTALLED_APPS:不管是测试环境还是生产环境,这个设置告诉Django哪些应用程序在这个站点上处于活动状态。默认情况下,Django包含以下应用程序:

    • django.contrib.admin: 这是后台管理站点。
    • django.contrib.auth:这是身份验证框架。
    • django.contrib.contenttypes:这是内容类型的框架。
    • django.contrib.sessions:用于管理session的框架。
    • django.contrib.messages:用于消息管理的框架。
    • django.contrib.staticfiles:用于管理静态文件的框架。
  • MIDDLEWARE_CLASSES:元组,包含要执行的中间件。

  • ROOT_URLCONF:项目的根URL。

  • DATABASES: 是包含项目中使用的所有数据库的设置的字典。必须始终存在默认数据库。默认配置使用SQLite3数据库。

  • LANGUAGE_CODE:定义Django站点的默认语言代码,默认是英语。


5 创建应用

现在,让我们创建第一个Django应用程序。我们将从头开始创建一个博客应用程序。在项目的根目录中,运行以下命令:

python manage.py startapp blog

这将创建应用程序的基本结构,如下所示:
django应用的基本结构

这些文件如下:

  • admin.py: 在这里,您可以注册该模型以将其包含到Django管理站点中。使用Django管理站点是可选的。
  • migrations:此目录将包含应用程序的数据库迁移。迁移允许Django跟踪模型更改并相应地同步数据库。
  • models.py:应用程序的数据模型。所有Django应用程序都需要一个models.py文件,但这个文件可以留空。
  • tests.py:在这里可以为应用程序添加测试。
  • views.py:应用程序的逻辑在这里。每个视图都接收一个HTTP请求,处理它,并返回一个响应。

6 设计博客的数据模型

我们将从定义博客的初始数据模型开始。模型是一个Python类,它是django.db.models.Model的子类,其中每个属性表示一个数据库字段。Django将为models.py文件中定义的每个模型创建一个表。当您创建模型时,Django为您提供了一个实用的API来轻松查询数据库。

首先,我们将定义Post模型。将以下行添加到博客应用程序的models.py文件中:

from django.db import models 
from django.utils import timezone
from django.contrib.auth.models import User

class Post(models.Model):
	STATUS_CHOICES = (
		('draft', 'Draft'),
		('published', 'Published'),
	)
	title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique_for_date='publish')
    author = models.ForeignKey(User, related_name='blog_posts', on_delete=models.DO_NOTHING)
    body = models.TextField()
    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, 

	class Meta:
		ordering = ('-publish',)
	
		def __str__(self):
			return self.title

这是我们定义的博客文章的基本模型。让我们来看看我们刚刚为此模型定义的字段:

  • title:博客文章标题的字段。该字段是CharField,它对应SQL数据库中的VARCHAR列。
  • slug: slug是一个用于URL的字段。slug是一个仅包含字母、数字、下划线或连字符的短标签。我们将使用slug字段为我们的博客文章构建漂亮、SEO友好的URL。我们在这个字段中添加了unique_for_date参数,这样我们就可以使用帖子的日期和slug来构建帖子的URL。Django将防止多个帖子在同一日期发布相同的slug。
  • author:此字段为外键。此字段用于定义一对多关系。它意味着每个用户可以编写多个帖子,而每个帖子对应一个用户。对于这个字段,Django将使用相关模型的主键在数据库中创建一个外键。在本例中,我们依赖于Django身份验证系统的User模型。我们使用related_name属性指定反向关系的名称,从User到Post。我们稍后将进一步了解这一点。
  • body: 帖子的正文。此字段是TextField,它转换为SQL数据库中的TEXT列。
  • publish: 时间字段类型表示文章发布的时间。我们使用Django的timezone.now方法作为默认值。它的值和settings.py中配置的时区有关。
  • created:时间字段类型表示创建文章的时间。因为我们在这里设置了autonow_add为True,创建对象时将自动保存日期。
  • updated:时间字段类型表示创建文章更新时间。因为我们在这里设置了autonow_add为True,保存对象时将自动修改此日期。
  • status:这是一个显示帖子状态的字段。我们使用choice参数,因此该字段的值只能设置为给定的选项之一。

Django还提供了其他不同类型的字段,您可以使用它们来定义模型。您可以在中找到所有字段类型https://docs.djangoproject.com/en/1.8/ref/models/fields/.

模型中的类Meta包含元数据。我们告诉Django在查询数据库时,默认情况下按发布时间降序排序。可以使用负前缀指定降序。

为了使SQLite能够正常处理日期时间。需要安装pytz,使用以下命令打开shell并安装pytz:

pip install pytz

Django默认支持时区感知日期时间。您可以使用项目的settings.py文件中的USE_TZ设置来激活/停用时区支持。


7 激活你的应用

为了让Django能够跟踪我们的应用程序并能够为其模型创建数据库表,我们需要激活它。为此,编辑settings.py文件并将博客添加到INSTALLED_APPS设置中:

INSTALLED_APPS = (
	'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',    
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # 新添加的
)

激活应用后,django可以跟踪视图和数据模型的变更。


8 生成迁移文件 执行迁移

此步是为了根据数据模型在数据库中创建数据表。Django附带了一个迁移系统,用于跟踪您对模型所做的更改,并将其传播到数据库中。migrate命令对INSTALLED_APPS中列出的所有应用程序应用迁移;它将数据库与当前模型和迁移同步。

首先,我们需要为刚刚创建的新模型生成迁移文件。在项目的根目录中,输入以下命令:

python manage.py makemigrations blog

你可以看到以下输出:

Migrations for ‘blog’:
0001_initial.py:
– Create model Post

Django刚刚在blog应用程序的迁移目录中创建了一个文件0001_initial.py。您可以打开该文件以查看迁移文件的内容。

让我们来看看Django将在数据库中执行的SQL代码,以便为我们的模型创建表。sqlmigrate命令接受迁移名称并返回其SQL而不运行它。运行以下命令检查其输出:

python manage.py sqlmigrate blog 0001

执行此命令后将输出如下内容:

BEGIN;
CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(250) NOT NULL, "slug" varchar(250) NOT NULL, "body" text NOT NULL, "publish" datetime NOT NULL, "created" datetime NOT NULL, "updated" datetime NOT NULL, "status" varchar(10) NOT NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id")); 
CREATE INDEX "blog_post_2dbcba41" ON "blog_post" ("slug");
CREATE INDEX "blog_post_4f331e2f" ON "blog_post" ("author_id");
COMMIT;

输出SQL语句格式取决于您使用的数据库。上面的输出由SQLite数据库决定。如您所见,Django通过组合应用程序名称和模型的小写名称(blog_post)来生成表名,但您也可以使用db_table属性在模型的Meta类中指定它们。Django会自动为每个模型创建一个主键,但您也可以在一个模型字段上覆盖指定primary_key=True的主键。

接下来让我们执行迁移,从而在数据库中创建相应的表,执行迁移实际上是执行迁移文件。输入以下命令:

python manage.py migrate

你会得到如下的输出:

Applying blog.0001_initial… OK

我们刚刚为INSTALLED_APPS中列出的应用程序执行了迁移,包括我们的blog应用程序。执行迁移后,数据库中将会创建相应的数据库表。

如果你编辑了models.py文件,添加、删除或更改了现有模型的字段,或者添加新模型,则必须再次使用makemigrations命令进行生成迁移文件。迁移将允许Django跟踪模型更改。然后,使用migrate命令执行迁移,以使数据库与模型保持同步。


9 为模型创建管理站点

现在我们已经定义了Post模型,我们将创建一个简单的管理站点来管理该模型对应的数据库表。Django自带一个内置的管理界面,对编辑内容非常有用。Django管理站点是通过读取模型元数据并为编辑内容提供界面来动态构建的。您可以开箱即用,也可以添加一些配置项。

请记住,django.contrib.admin已经包含在我们项目的INSTALLED_APPS设置中,否则你将无法创建站点管理员。

9.1 创建超级管理员

首先,我们需要创建一个用户来管理管理站点。运行以下命令:

python manage.py createsuperuser

您将看到以下输出。您需要输入所需的用户名、电子邮件和密码:

Username (leave blank to use ‘admin’): admin Email address: admin@admin.com
Password: ********
Password (again): ********
Superuser created successfully.

9.2 Django 的管理站点

现在,使用命令python manage.py runserver启动开发服务器并打开http://127.0.0.1:8000/admin/在浏览器中。您应该看到管理登录页面,如下图所示:
django管理员界面
使用在上一步中创建的用户的凭据登录。您将看到管理站点索引页面,如下图所示:

django管理站点首页
您在这里看到的组和用户模型,这两个模型是Django身份验证框架的一部分,位于Django.contrib.auth中。如果单击用户,您将看到之前创建的用户。博客应用程序的Post模型与此User模型有关系。记住,这是由author字段定义的关系。但是现在您还看不到blog应用的模型,因为我们还没有将该模型添加到管理站点中。


10 将模型添加到管理站点

让我们将您的blog应用中的post模型添加到管理站点。编辑blog应用程序的admin.py文件,添加如下代码:

from django.contrib import admin
from .models import Post

admin.site.register(Post)

现在,在浏览器中重新加载管理站点。您应该在管理站点中看到您的Post模型,如下所示:

django站点添加自定义模型
这很容易,对吧?当您在Django管理站点注册一个模型时,您将获得一个用户友好的界面,该界面通过对模型进行内省而生成,允许您以简单的方式列出、编辑、创建和删除对象。

单击Posts右侧的添加链接以添加新帖子。您将看到Django为模型动态生成的创建表单,如下所示:

在这里插入图片描述
Django为每种类型的字段使用不同的表单小部件。甚至像DateTimeField这样的复杂字段也可以用JavaScript日期选择器这样的简单界面显示。

填写表格并单击“保存”按钮。您应该被重定向到帖子列表页面,并显示一条成功的消息和刚刚创建的帖子,如下图所示:
在这里插入图片描述


11 自定义模型的显示方式

现在我们将了解如何自定义管理站点,比如自定义显示的字段、添加过滤器、搜索等功能。编辑blog应用程序的admin.py文件,并将其更改为:

from django.contrib import admin
from .models import Post

class PostAdmin(admin.ModelAdmin):
	list_display = ('title', 'slug', 'author', 'publish', 'status')

admin.site.register(Post)

我们告诉Django管理站点,我们的模型是使用从ModelAdmin继承的自定义类注册到管理站点的。在这个类中,我们可以包含有关如何在管理站点中显示模型以及如何与其交互的信息。list_display属性允许您设置要在管理对象列表页面中显示的模型字段。

让我们使用更多自定义模型的选项吧,在blog应用下的admin.py中修改代码:

class PostAdmin(admin.ModelAdmin):
	list_display = ('title', 'slug', 'author', 'publish', 'status')
    list_filter = ('status', 'created', 'publish', 'author')
    search_fields = ('title', 'body')
    prepopulated_fields = {'slug': ('title',)}
    raw_id_fields = ('author',)
    date_hierarchy = 'publish'
    ordering = ['status', 'publish']

返回浏览器并重新加载文章列表页面。现在将如下所示:
在这里插入图片描述
您可以看到,发布列表页面上显示的字段是您在list_display属性中指定的字段。列表页面现在包含一个右侧边栏,允许您通过list_filter属性中包含的字段过滤结果。页面上出现了一个搜索栏。这是因为我们已经使用search_fields属性定义了一个可搜索字段列表。在搜索栏的正下方,有一个可以快速浏览日期层次结构的栏。这是由date_hierarchy属性定义的。您还可以看到,默认情况下,帖子按“status”和“publish”列排序。您已经使用排序属性指定了默认顺序。

现在点击Addpost链接。您也会在这里看到一些变化。作为你
键入新文章的标题,slug字段将自动填充。我们已经告诉Django使用prepopulated_fields属性用标题字段的输入预填充slug字段。此外,现在author字段显示了一个查找小部件,当您有数千个用户时,该小部件的缩放比下拉选择输入要好得多,如下图所示:
在这里插入图片描述
通过几行代码,我们定制了模型在管理站点中的显示方式。有很多方法可以定制和扩展Django管理站点。在本书的后面,还将进一步介绍这一点。


12 使用QuerySet和管理器(Django的ORM)

现在你已经有了一个功能齐全的管理站点来管理你的博客内容,是时候学习如何从数据库中检索信息并与之交互了。Django提供了一个强大的数据库抽象API,可以让你轻松地创建、检索、更新和删除对象。Django对象关系映射器(ORM)与MySQL、PostgreSQL、SQLite和Oracle兼容。请记住,您可以通过编辑项目的settings.py文件中的DATABASES设置来定义项目的数据库。Django可以同时处理多个数据库,您甚至可以编程数据库路由器,以您喜欢的任何方式处理数据。

一旦您创建了数据模型,Django就为您提供了一个与它们交互的免费API。您可以在以下位置找到官方文档的数据模型参考https://docs.djangoproject.com/en/1.8/ref/models/.

12.1 使用ORM创建对象

在终端中使用以下命令打开Python shell:

python manage.py shell

然后输入以下代码:

>>> from django.contrib.auth.models import User 
>>> from blog.models import Post
>>> user = User.objects.get(username='admin')
>>> Post.objects.create(title='One more post',
						slug='one-more-post',
						body='Post body.',
						author=user
						)
>>> post.save()

让我们分析一下这段代码的作用。
首先,我们获取用户名为admin的用户对象:

user = User.objects.get(username='admin')

get()方法允许您从数据库中检索单个对象。请注意,此方法需要一个与查询匹配的结果。如果数据库未返回任何结果,则此方法将引发DoesNotExist异常,如果数据库返回多个结果,则将引发MultipleObjectsReturn异常。这两个异常都是正在执行查询的模型类的属性。

然后我们创建一个带有自定义标题、slug和正文的Post实例;我们将之前检索到的用户设置为文章的作者:

post = Post(title='Another post', slug='another-post', body='Post body.', author=user)

最后,我们使用save()方法将Post对象保存到数据库:

post.save()

此操作在将执行INSERT SQL语句。我们已经了解了如何先在内存中创建一个对象,然后将其持久化到数据库中,但我们也可以使用create()方法直接将该对象创建到数据库中:

Post.objects.create(title='One more post', slug='one-more-post', body='Post body.', author=user)

12.2 使用ORM修改对象

现在,将文章标题更改为其他内容,然后再次保存对象:

>>> post.title = 'New title'
>>> post.save()

对象已经存在的话,save()方法执行UPDATE SQL语句。

12.3 检索对象

12.3.1 检索全部对象

Django对象关系映射(ORM)基于QuerySet。QuerySet是数据库中的对象集合,可以有几个过滤器来限制结果。您已经知道如何使用get()方法从数据库中检索单个对象。每个Django模型至少有一个管理器,默认的管理器称为对象。您可以使用模型管理器获得QuerySet对象。要从表中检索所有对象,只需在默认对象管理器上使用all()方法,如下所示:

>>> all_posts = Post.objects.all()

这就是我们如何创建一个返回数据库中所有对象的QuerySet。请注意,此QuerySet尚未执行。Django的QuerySet是惰性查询的;只有在对具体的对象进行操作时才会执行相应的SQL对数据库中的内容进行修改。这种行为使QuerySet非常高效。如果我们不将QuerySet设置为变量,而是将其直接写入Python shell,则会执行QuerySet的SQL语句,因为此时我们在shell中打印出了这个QuerySet:

>>> Post.objects.all()
12.3.2 使用filter()方法

这是Django ORM提供的条件查询,使用这个方法我们可以查询特定条件下的QuerySet。要过滤QuerySet,可以使用管理器的filter()方法。例如,我们可以使用以下QuerySet检索2015年发布的所有帖子:

>>>Post.objects.filter(publish__year=2015)

也可以按多个字段进行筛选。例如,我们可以使用用户名admin检索作者在2015年发布的所有帖子:

Post.objects.filter(publish__year=2015, author__username='admin')

这相当于构建链接多个过滤器的同一QuerySet(filter()的链式用法):

Post.objects.filter(publish__year=2015).filter(author__username='admin')

我们使用两个下划线(publish__year)的字段查找方法构建查询,但我们也使用两个底线(author__username)访问相关模型的字段。

12.3.3 使用exclude()方法

可以使用管理器的exclude()方法从QuerySet中排除某些结果。例如,我们可以检索2015年发布的所有标题不以Why开头的帖子:

Post.objects.filter(publish__year=2015)..exclude(title__startswith='Why')
12.3.4 使用order_by()给QuerySet排序

可以使用管理器的order_by()方法按不同的字段对结果进行排序。例如,可以检索按标题排序的所有对象:

Post.objects.order_by('title')

order_by()结果默认升序。可以使用负号前缀指示降序,如下所示:

Post.objects.order_by('-title')

12.4 删除对象

如果要删除对象,可以从对象实例中删除:

post = Post.objects.get(id=1) 
post.delete()

当然您也可以在查询集QuerySet上使用delete(),以此实现批量删除的效果。需要注意的是,删除对象也会删除任何从属关系。

12.5 什么情况下,查询将执行SQL语句

您可以将任意数量的筛选器连接到一个QuerySet,并且在真正操作QuerySet之前,您不会访问数据库。仅在以下情况下将会访问数据库:

  • 第一次迭代QuerySet时。
  • 对QuerySet执行切片操作时。
  • 当你缓存或者打印QuerySet时。
  • 当你对QuerySet中的对象调用repr()或len()时。
  • 当您显式调用list()时。
  • 当您在bool()、或、and或if等语句中测试它时。

13 如何创建模型管理器

上述对数据模型的各种操作,使用的都是Django自带的管理器。但我们也可以为模型定义自定义管理器。我们将创建一个自定义管理器来检索所有发布状态的帖子。

有两种方法可以将管理器添加到模型中:您可以添加额外的管理器方法或修改初始管理器QuerySet。第一个类似Post.objects.my_manager(),后面的类似Post.my_manager.all()。我们的管理器将允许我们使用Post.published检索帖子。

编辑博客应用程序的models.py文件以添加自定义管理器:

class PublishedManager(models.Manager):
	def get_queryset(self):
		return super(
			PublishedManager,
			self
		).get_queryset().filter(status='published')

		
class Post(models.Model):
	# ...
	
	objects = models.Manager()  # The default manager.  							
	PublishedManager() # Our custom manager.						
		

get_queryset()是返回要执行的QuerySet的方法。我们使用它在最终的QuerySet中包含自定义过滤器。我们已经定义了自定义管理器,并将其添加到Post模型中;我们现在可以使用它来执行查询。例如,我们可以使用以下方法检索标题以Who开头的所有已发布帖子:

Post.published.filter(title__startswith='Who')

14 定义视图函数

既然您已经掌握了一些关于如何使用ORM的知识,那么就可以构建blog应用程序的视图了。Django视图只是一个Python函数,它接收web请求并返回web响应。在视图内部,定义了返回所需响应的所有逻辑。

首先,我们将创建应用程序视图,然后为每个视图定义一个URL模式;最后,我们将创建HTML模板来呈现视图生成的数据。每个视图都将呈现一个传递变量的模板,并将返回一个HTTP响应和呈现的输出。

14.1 创建一个显示帖子列表的视图

让我们先创建一个视图来显示帖子列表。编辑博客应用程序的views.py文件,添加如下代码:

from django.shortcuts import render, get_object_or_404
from .models import Post

def post_list(request):
	posts = Post.published.all()
	
	return render(
		request,
		'blog/post/list.html',
		{'posts': posts}
	)

您刚刚创建了第一个Django视图。post_list视图将请求对象request作为唯一的参数。请记住,所有视图都需要此参数。在这个视图中,我们使用之前创建的已发布管理器检索所有具有已发布状态的帖子。

最后,我们使用Django提供的render()快捷方式,用给定的模板渲染帖子列表。此函数将请求对象request作为参数、模板路径和用于呈现给定模板的变量。它返回一个HttpResponse对象和呈现的文本(通常是HTML代码)。render()快捷方式将请求上下文考虑在内,因此给定模板可以访问模板上下文处理器设置的任何变量。模板上下文处理器只是将变量设置到上下文中的可调用程序。您将在第3章“扩展博客应用程序”中学习如何使用它们。

14.1 创建一个显示帖子详细内容的视图

让我们创建第二个视图来显示一篇文章的详情。将以下函数添加到views.py文件:

def post_detail(request, year, month, day, post):
    post = get_object_or_404(
    			Post,
    			slug=post,
    			status='published',
    			publish__year=year,
    			publish__month=month,
    			publish__day=day
    		)
    
    return render(request,
    			'blog/post/detail.html',
    			{'post': post},
    		)

这是帖子详细内容视图。该视图使用年、月、日和帖子参数来检索具有给定段和日期的已发布帖子。注意,当我们创建Post模型时,我们向slug字段添加了unique_for_date参数。通过这种方式,我们可以确保在给定的日期内只有一个带有slug的帖子,因此,我们可以按日期和slug检索单个帖子。在详细视图中,我们使用get_object_or_404()快捷方式来检索所需的Post。此函数检索与给定参数匹配的对象,如果找不到对象,则启动HTTP 404(未找到)异常。最后,我们使用render()快捷方式使用模板渲染检索到的帖子。


15 为你的视图添加URL模式

URL模式由一个Python正则表达式,一个视图和一个允许在项目范围内命名的名称组成。Django运行每个URL模式,并在与请求的URL匹配的第一个模式处停止。然后,Django导入匹配URL模式的视图并执行它,传递HttpRequest类的实例和关键字或位置参数。

如果您以前没有使用过正则表达式,您可能需要先看看python的正则表达式链接

在blog应用程序的目录中创建一个urls.py文件,并添加以下行:

from django.conf.urls import url 
from . import views

urlpatterns = [
	# post views
    url(r'^$', views.post_list, name='post_list'),
    url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$', views.post_detail, name='post_detail'),
]

第一个URL模式不接受任何参数,并映射到post_list视图。第二个模式采用以下四个参数,并映射到post_detail视图。让我们看看URL模式的正则表达式:

  • year:需要四位数字。
  • month:需要两位数字。
  • day: 需要两位数字。
  • post:可以由单词和连字符组成。

现在,您必须将blog应用程序的URL模式包含到项目的URL模式中。编辑项目mysite目录中的urls.py文件,在其中添加以下内容:

from django.conf.urls import include, url from django.contrib import admin

urlpatterns = [
	url(r'^admin/', include(admin.site.urls)),
	url(r'^blog/', include('blog.urls', namespace='blog', app_name='blog')),
]

通过这种方式,您告诉Django在blog/path下包含blog urls.py中定义的URL模式。你给它命名为blog,这样你就可以很容易地引用这组URL。

15.1 使用get_absolute_url()方法返回标准的URL

您可以使用上一节中给post_detail定义的URL来获取该视图的响应。但是Django中的约定是向模型中添加一个get_absolute_url()方法,该方法返回对象的规范url。对于此方法,我们将配合使用reverse()方法,该方法允许您通过URL的名称和传递可选参数来构建URL。编辑models.py文件并添加以下内容:

from django.core.urlresolvers import reverse 

Class Post(models.Model):
	# ...
	def get_absolute_url(self):
		return reverse(
				'blog:post_detail',
				args=[
					self.publish.year,
					self.publish.strftime('%m'),
					self.publish.strftime('%d'),
					self.slug,
					]
				)

我们将在模板(HTMLa文件)中使用get_absolute_url()方法。


16 为你的视图函数创建模板templates

我们已经为blog应用程序创建了视图和URL模式。现在是时候添加模板以方便显示帖子内容了。

在blog应用程序目录中创建以下目录和文件:
在这里插入图片描述
这将是我们模板的文件结构。base.html文件将包含网站的主要html结构,并将内容划分为主要内容区域和侧边栏。list.html和detail.html文件将继承自base.html文件,以分别渲染博客文章列表和详细视图。

Django有一个强大的模板语言,允许您指定数据显示。它基于模板标记,模板标签类似于{%tag%};模板变量类似于{{{variable}};还有模板筛选器,可以应用于变量,并且看起来类似{{ variable | filter}。你可以看到所有内置模板标记和筛选器链接.

让我们编辑base.html文件并添加以下代码:

{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
  <title>{% block title %}{% endblock %}</title>
  <link href="{% static "css/blog.css" %}" rel="stylesheet"> </head>
<body>
<div id="content">
    {% block content %}
    {% endblock %}
</div>
<div id="sidebar">
	<h2>My blog</h2>
	<p>This is my blog.</p>
</div>
</body>
</html>

{%load staticfiles%}告诉Django加载Django.contrib.staticfiles应用程序提供的staticfiles模板标记。加载后,您可以在整个模板中使用{%static%}模板筛选器。使用此模板过滤器,您可以在博客应用程序的static/目录下包含静态文件,如blog.css文件,您将在本示例的代码中找到这些文件。将此目录复制到项目的同一位置以使用现有静态文件。

让我们编辑post/list.html文件,并添加以下内容:

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

{% block title %}My Blog{% endblock %}

{% block content %}
<h1>My Blog</h1>
  {% for post in posts %}
  <h2>
  	<a href="{{ post.get_absolute_url }}">
  		{{ post.title }}
  	</a>
  </h2>
  <p class="date">Published {{ post.publish }} by {{ post.author }}</p>
  {{ post.body|truncatewords:30|linebreaks }}
 {% endfor %}
{% endblock %}

使用{%extends%}模板标记,我们告诉Django要继承模板文件blog/base.html。然后我们用内容填充基本模板的标题和内容块。我们遍历这些帖子,并显示它们的标题、日期、作者和正文,包括标题中指向帖子规范URL的链接。在文章的正文中,我们应用了两个模板过滤器:truncatewords将值截断为指定的字数,换行符将输出转换为HTML换行符。您可以根据需要连接任意多个模板过滤器;每一个都将应用于前一个生成的输出。

打开shell并执行命令python manage.py runserver以启动开发服务器。打开http://127.0.0.1:8000/blog/在你的浏览器中,你会看到一切都在运行。请注意,您需要有一些状态为“已发布”的帖子才能在此处看到它们。您应该看到这样的内容:
在这里插入图片描述
然后,让我们编辑post/detail.html文件,添加以下内容:

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

{% block title %}{{ post.title }}{% endblock %}

{% block content %}
  <h1>{{ post.title }}</h1>
  <p class="date">Published {{ post.publish }} by {{ post.author }}</p>
  {{ post.body|linebreaks }}
{% endblock %}  

现在,您可以返回浏览器,单击其中一个帖子标题以查看帖子的链接。您应该看到这样的内容:
在这里插入图片描述


17 为博客内容创建分页器

当你开始向你的博客添加内容时,你很快就会意识到你需要将帖子列表分成几个页面。Django有一个内置的分页类,允许您轻松地管理分页数据。

编辑blog应用程序的views.py文件以导入Django paginator类,并按如下方式修改post_list视图:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

def post_list(request):
	 object_list = Post.published.all()
	 paginator = Paginator(object_list, 3) # 3 posts in each page
	 page = request.GET.get('page')
	 try:
	 	posts = paginator.page(page)
	 except PageNotAnInteger:
	 	# If page is not an integer deliver the first page 
	 	posts = paginator.page(1)
     except EmptyPage:
    	# If page is out of range deliver last page of results 
    	posts = paginator.page(paginator.num_pages)
    	
     return render(
     		request,
     		'blog/post/list.html',
     		{'page': page, 'posts': posts},
     	)

那么Django自带的分页器是如何工作的呢:

  1. 首先要给每个页面中显示的对象实例化Paginator类。
  2. get到当前请求的页码。
  3. 将页码作为参数,获取我们想要的数据。
  4. 分别在对象不足1页和对象为空的时候,捕获异常。
  5. 将页码和检索到的对象传递给模板。

现在,我们必须创建一个模板来显示分页器,以便它可以包含在任何使用分页的HTML中。在blog应用程序的templates文件夹中,创建一个新文件并将其命名为pagination.html。将以下html代码添加到该文件中:

<div class="pagination">
	<span class="step-links">
		{% if page.has_previous %}
			<a href="?page={{ page.previous_page_number }}">Previous</a>
		{% endif %}
		<span class="current">
			Page {{ page.number }} of {{ page.paginator.num_pages }}.
		</span>
		
		{% if page.has_next %}
			<a href="?page={{ page.next_page_number }}">Next</a>
		{% endif %}
	</span>
</div>

分页模板需要一个Page对象,以便呈现上一个和下一个链接,并显示当前页面和结果的总页面。让我们回到blog/post/list.html模板,在{%content%}块的底部包含pagination.html模板,如下所示:

{% block content %}
  ...
  {% include "pagination.html" with page=posts %}
{% endblock %}

由于我们传递给模板的Page对象称为posts,因此我们将分页模板包含到post列表模板中,指定正确呈现该模板的参数。这是可以在不同模型的分页视图中重用分页模板的方法。

现在,打开http://127.0.0.1:8000/blog/在浏览器中。您应该在文章列表的底部看到分页,并且您应该能够浏览页面:
在这里插入图片描述


18 使用python的类作为视图(CBV)

由于视图是接受web请求并返回web响应的可调用对象,因此您也可以将视图定义为类方法。Django提供基础视图类View。为此,所有这些都继承自View类,后者处理HTTP方法分派和其他功能。这是创建视图的另一种方法。

我们将把post_list视图更改为基于类的视图,以使用Django提供的通用ListView。

编辑blog应用程序的views.py文件并添加以下代码:

from django.views.generic import ListView

class PostListView(ListView):
	queryset = Post.published.all()    
	context_object_name = 'posts'
	paginate_by = 3
    template_name = 'blog/post/list.html'

这个基于类的视图,功能和原来的FBV是一样的。这里,我们告诉ListView:

  • 定义要获取的对象,它是一个QuerySet。
  • 查询结果的模板变量上下文变量为posts,如果不指定任何context_object_name,则默认模板变量为object_list。
  • 每页的数量
  • 模板文件

现在,打开blog应用程序的urls.py文件,注释先前的post_list URL模式,并使用PostListView类添加一个新的URL模式,如下所示:

urlpatterns = [
	# post views
    # url(r'^$', views.post_list, name='post_list'),
    url(r'^$', views.PostListView.as_view(), name='post_list'),
    url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/(?P<post>[-\w]+)/$', views.post_detail, name='post_detail'),
]   

为了保证分页器正常工作,传给模板一个分页对象。Django的ListView在一个名为page_obj的变量中传递所选页面,因此您必须相应地编辑post_list.html模板,以使用正确的变量包含paginator,如下所示:

{% include "pagination.html" with page=page_obj %}

打开http://127.0.0.1:8000/blog/在浏览器中,并检查所有内容的工作方式是否与前一个postlist视图相同。这是一个使用Django提供的泛型类的基于类的视图的简单示例。您将在第10章“构建电子学习平台”和后续章节中了解更多关于基于类的视图。


总结

在本章中,您已经通过创建一个基本的blog应用程序学习了Django web框架的基础知识。您已经设计了数据模型并将迁移应用到项目中。您已经为博客创建了视图、模板和URL,包括对象分页。

在下一章中,您将学习如何使用:

  • 评论系统
  • 标记功能
  • 允许用户共享
  • 通过电子邮件发布
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勇敢牛马 不怕困难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值