跳转至

第6章:博客 API

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

在本章中,我们将构建基本的 API 部分。就像我们的图书馆和 Todo API 一样,我们从传统的 Django 开始,然后添加 Django REST Framework。主要区别是我们将使用自定义用户模型,并从一开始就支持 CRUD 操作,正如我们将看到的,Django REST Framework 使这一切变得非常无缝。

初始设置

我们的设置与以前相同。导航到代码目录,并在其中为此项目创建一个名为 blogapi 的目录。然后在新虚拟环境中安装 Django,并创建一个名为 django_project 的新 Django 项目。

Shell

# Windows
> cd onedrive\desktop\code
> mkdir blogapi
> cd blogapi
> python -m venv .venv
> .venv\Scripts\Activate.ps1
(.venv) > python -m pip install django~=4.0.0
(.venv) > django-admin startproject django_project .

# macOS
% cd desktop/desktop/code
% mkdir blogapi
% cd blogapi
% python3 -m venv .venv
% source .venv/bin/activate
(.venv) % python3 -m pip install django~=4.0.0
(.venv) % django-admin startproject django_project .

运行命令 python manage.py runserver,它应该在 [[http://127.0.0.1:8000/](http://127.0.0.1:8000/](http://127.0.0.1:8000/](http://127.0.0.1:8000/`)) 上显示 Django 欢迎页面。

终端 Shell 可能会显示一条消息抱怨"你有 18 个未应用的迁移"。我们故意不运行 migrate,因为我们将使用自定义用户模型,并希望等到它配置好后再运行我们的第一次 migrate 命令。

.gitignore

在项目上早期和频繁使用 Git 总是一个好主意。它让开发人员跟踪项目随时间的进展,并识别可能出现的任何错误。让我们初始化一个新的 Git 存储库并检查其状态。

Shell

(.venv) > git init
(.venv) > git status

.venv 文件应该出现,我们不希望它进入源代码控制,因此使用您的文本编辑器在项目目录中创建一个 .gitignore 文件,旁边是 manage.py 文件。为 .venv 添加一行,以便它被 Git 忽略。

.gitignore

.venv/

然后再次运行 git status 以确认 .venv 不再出现,添加我们当前的工作,并创建第一个 Git 提交。

Shell

(.venv) > git status
(.venv) > git add -A
(.venv) > git commit -m "initial commit"

自定义用户模型

添加自定义用户模型是一个可选但推荐的下一步。即使您不打算使用它,现在采取几个步骤也为将来在项目上利用它敞开了大门。

首先创建一个名为 accounts 的新应用程序。

Shell

(.venv) > python manage.py startapp accounts

然后将其添加到我们的 INSTALLED_APPS 配置中,以便 Django 知道它的存在。

Code

# django_project/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # Local
    "accounts.apps.AccountsConfig",  # new
]

accounts/models.py 中,通过扩展 AbstractUser 并添加一个字段 name 来定义自定义用户模型 CustomUser。我们还将添加一个 __str__ 方法以在管理员和其他地方返回用户的电子邮件地址。

Code

# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    name = models.CharField(null=True, blank=True, max_length=100)

最后一步是在 settings.py 中更新 AUTH_USER_MODEL 配置,它隐式设置为 auth.User,改为 accounts.CustomUser。这可以添加到文件的底部。

Code

# django_project/settings.py
AUTH_USER_MODEL = "accounts.CustomUser"  # new

现在我们可以为模型更改运行 makemigrations,运行 migrate 以初始化数据库,并创建超级用户以便我们可以查看管理员。确保为您的自定义用户包含电子邮件。

Shell

(.venv) > python manage.py makemigrations
(.venv) > python manage.py migrate
(.venv) > python manage.py createsuperuser

然后使用 runserver 命令启动 Django 的内部 Web 服务器:

Shell

(.venv) > python manage.py runserver

如果我们前往 [[http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/`)) 的管理员并登录,看起来好像缺少了什么,不是吗?

Admin Empty Homepage

只有"Groups"部分出现。我们没有像通常使用默认用户模型时那样有"Users"。缺少的是两件事:我们必须自定义 accounts/admin.py 以显示我们的新自定义用户模型,并创建一个名为 accounts/forms.py 的新文件,该文件设置 CustomUser 在创建或更改用户时使用。我们将从 accounts/forms.py 开始。

Code

# accounts/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ("name",)

class CustomUserChangeForm(UserChangeForm):
    class Meta:
        model = CustomUser
        fields = UserChangeForm.Meta.fields

在顶部,我们导入了用于创建或更新用户的 UserCreationFormUserChangeForm。我们还导入了我们的 CustomUser 模型,以便它可以集成到新的 CustomUserCreationFormCustomUserChangeForm 类中。

完成这一步后,自定义用户设置的最后一步是更新 accounts/admin.py 以正确显示新的自定义用户。

Code

# accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    list_display = [
        "email",
        "username",
        "name",
        "is_staff",
    ]
    fieldsets = UserAdmin.fieldsets + ((None, {"fields": ("name",)}),)
    add_fieldsets = UserAdmin.add_fieldsets + ((None, {"fields": ("name",)}),)

admin.site.register(CustomUser, CustomUserAdmin)

我们完成了。如果您重新加载管理员页面,它现在显示"Users"。

Admin Users

如果您单击"Users",您可以看到我们的超级用户也在那里。

Admin Superuser

Posts 应用程序

是时候为我们的博客创建一个专用的应用程序了。命名总是很棘手,虽然很想添加一个名为 blog 的新应用程序和一个相关的模型 Blog,但很少这样做,因为 Django 中的多个区域会在应用程序和模型名称上添加 s,而"blogs"看起来不太好。

因此,更常见的做法是将博客应用程序称为 posts,相关的数据库模型简单地称为 Post。这就是我们在这里要做的。

键入 Control+c 停止本地服务器,然后使用管理命令 startapp 创建新的 posts 应用程序。

Shell

(.venv) > python manage.py startapp posts

然后立即更新 django_project/settings.py 文件中的 INSTALLED_APPS,以免我们忘记。

Code

# django_project/settings.py
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # Local
    "accounts.apps.AccountsConfig",
    "posts.apps.PostsConfig",  # new
]

Post 模型

我们的博客文章数据库模型将有五个字段:authortitlebodycreated_atupdated_at。我们还将导入 Django 的设置,以便我们可以在 author 字段中引用 AUTH_USER_MODEL。我们还将添加 __str__ 方法作为一般最佳实践。

Code

# posts/models.py
from django.conf import settings
from django.db import models

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

    def __str__(self):
        return self.title

这看起来足够简单。现在通过使用命令 makemigrations posts 首先创建新的迁移文件,然后运行 migrate 使数据库与我们的模型更改同步,来更新我们的数据库。

Shell

(.venv) > python manage.py makemigrations posts
(.venv) > python manage.py migrate

很好!我们想在 Django 的管理应用程序中查看我们的数据,所以我们快速更新 posts/admin.py 如下。

Code

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

admin.site.register(Post)

使用 python manage.py runserver 再次启动本地 Web 服务器,并访问管理员以查看我们的工作成果。

Admin Posts

那里有我们的 Posts 应用程序!单击 Posts 旁边的"+Add"按钮并创建一个新的博客文章。在"Author"旁边将有一个下拉菜单,其中有您的超级用户帐户(我的是 wsv)。确保选择了一个作者,添加标题,添加正文内容,然后单击"Save"按钮。

Admin add blog post

您将被重定向到 Posts 页面,该页面显示所有现有的博客文章。

Admin blog posts

测试

我们写了新代码,所以是时候进行测试了。这些被添加到使用 startapp 命令创建的现有 posts/tests.py 文件中。

在文件顶部导入 get_user_model() 以引用我们的用户以及 TestCasePost 模型。然后创建一个类 BlogTests,包含设置数据和现在的一个测试 test_post_model,它检查 Post 模型上的字段及其 __str__ 方法。

Code

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

class BlogTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user = get_user_model().objects.create_user(
            username="testuser",
            email="test@email.com",
            password="secret",
        )
        cls.post = Post.objects.create(
            author=cls.user,
            title="A good title",
            body="Nice body content",
        )

    def test_post_model(self):
        self.assertEqual(self.post.author.username, "testuser")
        self.assertEqual(self.post.title, "A good title")
        self.assertEqual(self.post.body, "Nice body content")
        self.assertEqual(str(self.post), "A good title")

要确认我们的测试正在工作,请使用 Control+c 退出本地服务器并运行我们的测试。

Shell

(.venv) > python manage.py test