Django 应用开发:一个简单的博客系统_django

引言

我们的下一个项目是使用全套 Django REST Framework 功能的博客 API。 它将具有用户,权限,并允许完整的 CRUD(创建-读取-更新-删除)功能。 我们还将探索视图集,路由器和文档。

在本章中,我们将构建基本的 API 部分。 与我们的 Library 和 Todo API 一样,我们从传统 Django 开始,然后添加 Django REST Framework。

初始化

我们的设置与以前相同。 导航到我们的代码目录,并在其中为该项目创建一个名为blogapi的目录。 然后在新的虚拟环境中安装Django,创建新的 Django 项目(blog_project)和用于博客条目(posts)的应用。

$ ;cd ~/Desktop && cd code
$ ;mkdir blogapi && cd blogapi
$ ;pipenv install ;django==2.2.6
$ ;pipenv shell
(blogapi) $ ;django-admin startproject blog_project . (blogapi) $ ;python manage.py startapp posts

由于我们添加了新应用,因此我们需要将其告知Django。 因此,请确保在settings.py文件中将帖子添加到我们的INSTALLED_APPS列表中。

# blog_project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

# Local
'posts.apps.PostsConfig', # new
]

现在,第一次运行 migrate,将我们的数据库与 Django 的默认设置和新应用同步。

(blogapi) $ ;python manage.py migrate

模型

我们的数据库模型将包含五个字段:author,title,body,created_at和updated_at。 如果我们在顶部的第二行中导入了Django的内置用户模型,则可以使用该模型。

# posts/models.py
from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return self.title

请注意,我们还定义了模型的__str__表示形式,这是Django的最佳做法。 这样,我们稍后将在Django管理员中看到标题。 现在,通过首先创建一个新的迁移文件,然后运行迁移将数据库与我们的模型更改同步来更新数据库。

(blogapi) $ ;python manage.py makemigrations posts

(blogapi) $ ;python manage.py migrate

好! 我们想在 Django 出色的内置管理应用程序中查看数据,因此,如下所示将其添加到 posts/admin.py 中。

# posts/admin.py
from django.contrib import admin
from .models import Post


admin.site.register(Post)

然后创建一个超级用户帐户,以便我们可以访问管理员。 在下面键入命令,然后输入所有提示。

(blogapi) $ ;python manage.py createsuperuser

现在我们可以启动本地Web服务器。 导航到 http://127.0.0.1:8000/admin/ 并使用您的超级用户凭据登录。 Django 应用开发:一个简单的博客系统_字段_02

单击帖子旁边的“ +Add”按钮,然后创建一个新博客帖子。 “作者”旁边将是一个具有您的超级用户帐户的下拉菜单(我的帐户称为wsv)。 确保选择了作者。 添加标题和正文内容,然后单击“保存”按钮。 Django 应用开发:一个简单的博客系统_python_03

您将被重定向到显示所有现有博客帖子的“帖子”页面。 *Django 应用开发:一个简单的博客系统_django_04

测试


让我们为Post模型编写一个基本测试。 我们希望确保已登录的用户可以创建带有标题和正文的博客文章。

# posts/tests.py
from django.test import TestCase
from django.contrib.auth.models import User
from .models import Post

class BlogTests(TestCase):

@classmethod
def setUpTestData(cls):
# Create a user
testuser1 = User.objects.create_user(username='testuser1', password='abc123')
testuser1.save()

# Create a blog post
test_post = Post.objects.create(
author=testuser1,
title='Blog title',
body='Body content...'
)
test_post.save()

def test_blog_content(self):

post = Post.objects.get(id=1)
author = f'{post.author}'
title = f'{post.title}'
body = f'{post.body}'
self.assertEqual(author, 'testuser1')
self.assertEqual(title, 'Blog title')
self.assertEqual(body, 'Body content...')

为了确认我们的测试正常,请退出本地服务器Control + c。 然后运行我们的测试。

(blogapi) $ ;python manage.py test

您应该看到类似以下的输出,该输出确认一切正常。

(blogapi) $ ;python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
---------------------------------------------------------------------- Ran 1 test in 0.119s

OK
Destroying test database for alias 'default'...

现在,我们已经完成了 API 的常规 Django 部分。 我们真正需要的只是模型和数据库中的一些数据。 现在是时候添加 Django REST 框架,以将我们的模型数据转换为 API 了。

Django REST Framework

如前所述,Django REST Framework 负责将数据库模型转换为 RESTful API 的繁重工作。 此过程包括三个主要步骤:

  • URL 路由的 py 文件
  • 将数据转换为 JSON 的 py
  • 将应用逻辑用于每个 API 端点的 py 文件

在命令行中,使用 Control + c 停止本地服务器,然后使用 pipenv 安装 Django REST Framework 。

(blogapi) $ ;pipenv install ;djangorestframework==3.10.3

然后将其添加到 settings.py 文件的 INSTALLED_APPS 部分。 明确设置我们的权限也是个好主意,默认情况下,Django REST Framework中 的权限已配置为 AllowAny 。 我们将在下一章中进行更新。

# blog_project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

# 3rd-party apps
'rest_framework', # new

# Local
'posts.apps.PostsConfig',
]

# new
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
}

现在,我们需要创建 URL,视图和序列化程序。

URLs

让我们从端点的实际位置的 URL 路由开始。 使用第二行的 include import 和我们的 posts app 的新 api/v1/ 路由更新项目级别的 urls.py 文件。

# blog_project/urls.py
from django.contrib import admin
from django.urls import include, path # new


urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('posts.urls')), # new
]

最好始终对API(v1 /,v2 /等)进行版本控制,因为进行较大的更改时,可能需要一段时间才能使API的各个使用者进行更新。 这样,您可以在一段时间内支持 API 的 v1 ,同时还可以启动新的更新的 v2,并避免破坏依赖于 API 后端的其他应用程序。 请注意,由于目前我们唯一的应用是 posts,因此我们可以直接在此处添加博客。 如果我们在一个项目中有多个应用程序,那么最好创建一个专用的 api 应用程序,然后将所有其他 API url 路由包含到其中。 但是对于像这样的基础项目,我宁愿避免使用仅用于路由的 api 应用。 如有需要,我们随时可以添加一个。 接下来,创建我们的帖子应用 urls.py 文件。

(blogapi) $ ;touch posts/urls.py

然后包含以下代码。

# posts/urls.py
from django.urls import path
from .views import PostList, PostDetail

urlpatterns = [
path('<int:pk>/', PostDetail.as_view()),
path('', PostList.as_view()),
]

所有博客路由都将位于 api/v1/ 上,因此我们的PostList视图(我们将很快写出)的 '' 将位于 api/v1/ 上,而 PostDetail 视图(也将被写入)位于 api/v1/# 其中 # 表示条目的主键。 例如,第一篇博文的主要 ID 为1,因此它将位于路由 api/v1/1处,第二篇博文的 API 为 api/v1/2,依此类推。

Serializers

现在为我们的序列化函数。 在我们的*posts*应用中创建一个新的serializers.py文件。

(blogapi) $ ;touch posts/serializers.py

序列化器不仅可以将数据转换为JSON,还可以指定要包括或排除的字段。 在我们的例子中,我们将包括Django自动添加到数据库模型的id字段,但由于不将update_at字段包含在我们的字段中,因此我们将排除它。 在我们的API中轻松包含/排除字段的功能是一项了不起的功能。 通常,基础数据库模型具有的字段远远多于需要公开的字段。 Django REST Framework强大的序列化程序类使控制它非常简单。

# posts/serializers.py
from rest_framework import serializers
from .models import Post


class PostSerializer(serializers.ModelSerializer):

class Meta:
fields = ('id', 'author', 'title', 'body', 'created_at',)
model = Post

在文件的顶部,我们导入了 Django REST Framework 的 serializers 类和我们自己的模型。 然后,我们创建了一个 PostSerializer,并添加了一个 Meta 类,在其中我们指定要包括的字段并显式设置要使用的模型。 自定义序列化器有很多方法,但是对于常见的用例(例如,基本的博客),这就是我们所需要的。

Views

最后一步是创建我们的视图。 Django REST Framework具有几个有用的通用视图。 我们已经在Library API和Todos API中都使用ListAPIView来创建一个只读端点集合,实质上是所有模型实例的列表。 在Todos API中,我们还将RetrieveAPIView用作只读的单个端点,这类似于传统Django中的详细信息视图。 对于我们的Blog API,我们希望将所有可用的博客文章列出为读写端点,这意味着使用ListCreateAPIView,它类似于我们之前使用的ListAPIView,但允许写操作。 我们还希望使各个博客帖子可供阅读,更新或删除。 可以肯定的是,有一个内置的通用Django REST Framework视图正用于此目的:RetrieveUpdateDestroyAPIView。 这就是我们在这里使用的。 更新view.py文件,如下:

# posts/views.py
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer

class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer

class PostDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer

在文件的顶部,我们从 Django REST Framework 以及模型和序列化器文件导入泛型。 然后,我们创建两个视图。 PostList 使用通用的 ListCreateAPIView ,而 PostDetail 使用 RetrieveUpdateDestroyAPIView。 我们要做的就是更新通用视图以从根本上改变给定 API 端点的行为,这非常令人惊讶。 这是使用诸如 Django REST Framework 之类的功能齐全的框架的优势:所有这些功能都是可用的,经过测试的并且可以正常使用。 作为开发人员,我们不必在这里重新发明轮子。 至此,我们的 API 现在已经完成,我们真的不必自己编写太多代码。 在接下来的章节中,我们将对 API 进行其他改进,但是值得一提的是,它已经执行了我们想要的基本列表和 CRUD 功能。 是时候使用 Django Rest Framework 的可浏览 API 进行测试了。

Browsable API

启动本地服务器以与我们的API进行交互。

(blogapi) $ ;python manage.py runserver

然后转到 http://127.0.0.1:8000/api/v1/ 查看博客列表终端。

Django 应用开发:一个简单的博客系统_字段_05

该页面以 JSON 格式显示了我们的博客文章列表(目前只有一个)。 注意,GET 和 POST 方法都被允许。 现在,让我们确认是否存在我们的模型实例终结点,该终结点与单个帖子而不是所有帖子的列表有关。 转到 http://127.0.0.1:8000/api/v1/1/. Django 应用开发:一个简单的博客系统_字段_06

您可以在标头中看到支持 GET,PUT,PATCH 和 DELETE,但不支持 POST。 实际上,您可以使用下面的 HTML 表单进行更改,甚至可以使用红色的“ DELETE”按钮删除实例。 让我们尝试一下。 最后用其他文字(已编辑)更新标题。 然后点击“ PUT”按钮。 Django 应用开发:一个简单的博客系统_django_07

通过单击页面顶部的链接返回到“帖子列表”视图,或直接导航到 http://127.0.0.1:8000/api/v1/,您也可以在那里查看更新的文本。 Django 应用开发:一个简单的博客系统_字段_08

总结

此时,我们的 Blog API 完全可用。 但是,有一个大问题:任何人都可以更新或删除现有博客文章! 换句话说,我们没有任何权限。 在下一章中,我们将学习如何应用权限来保护我们的 API。