发布于 2026-01-06 3 阅读
0

Django 和现代 JS 库 - 后端 (1)

Django 和现代 JS 库 - 后端 (1)

Django 和现代 JS 库 - 后端

本教程讲的是什么?

《Django与现代JS框架》系列教程将整合Django以及React、Svelte等现代前端解决方案。未来还将计划整合其他框架/库。

项目描述

  • 我们将搭建一个 Django 服务器,并创建两个简单的单页应用程序。每个应用程序将使用不同的 JavaScript 库,并且它们都将与 Django 服务器通信。

  • 第二部分将使用 webpack 从零开始编写 React 应用。另请注意,不会使用 create-react-app。

  • 本教程的最新部分,也是第三部分,将是 Svelte 集成。

本系列共有 2 个项目和 3 篇文章:

  • Django 服务器和 GraphQL API 设置

  • 使用 webpack 搭建 React 应用并将其与后端集成。

  • 使用 webpack 设置 Svelte 应用程序并将其与我们的后端集成。

需要遵循哪些要求?

  • 具备 Python 和 Django 框架的基础知识

  • 必须具备Javascript和React的基础知识。

动机和初步信息

Python 是我的第一门编程语言。当时我们在开发一个电影推荐引擎,因为我们希望它是一个单页应用,所以必须将其与 Facebook 的 React 库集成。我对 JavaScript 的了解还处于入门阶段。精通一门陌生的编程语言需要时间。此外,我喜欢 Python 的生态系统,因为它拥有优秀的数据科学库,所以放弃 Python 对我来说从来都不是一个选项。总而言之,将 Django 和 React 集成起来确实花了不少时间。最近我发布了我的开发博客伊斯坦布尔旅行指南并编辑更新了我之前的所有文章。在此期间,另一个前端库 Svelte 发布了,这让我非常兴奋。我还添加了一篇关于 Svelte 和 Django 集成的文章。我希望这个系列文章能够帮助新手解决一些问题。

每个项目在生产环境中只会运行一台服务器。

介绍

单页应用架构

什么是单页应用程序?

在传统的网页设计中,所有 HTML、CSS 和 JS 代码都由服务器整理并以可渲染的形式传输。浏览器收到代码后,会立即在屏幕上渲染元素。如果用户点击链接,浏览器会向服务器发出另一个请求。服务器会进行所有逻辑运算,并返回另一个可渲染的代码。

在现代客户端应用程序中,一些逻辑操作由 JavaScript 代码处理,这些代码在用户的浏览器中执行。因此,服务器会在首次请求中发送所有网站代码。所以,浏览器需要额外的时间进行首次内容绘制。

除了首次加载之外,客户端应用运行速度更快,用户体验更接近原生应用,因为某些操作会在浏览器端立即执行,并且 I/O 操作可以通过 JavaScript 的异步特性完成。因此,用户看到的仍然是您的应用,而不是空白页面。

浏览器功能强大,能够完成许多令人印象深刻的任务。正因如此,在用户浏览器中处理资源密集型操作不失为一个合适的选择。否则,这些操作会占用服务器大量资源,增加账单费用。

任何在网页上贴上“此页面使用浏览器 X 浏览效果最佳”标签的人,似乎都在怀念互联网出现之前的糟糕旧时代,那时你几乎没有机会阅读在其他电脑、其他文字处理软件或其他网络上编写的文档。——蒂姆·伯纳斯-李

使用 Django 创建后端

第一步:从零开始创建一个 Django 项目

设置 Django

让我们创建一个虚拟环境来进行全新安装。

这个虚拟环境将为这三篇文章提供一个活跃的交流环境。

python3 -m venv tutorial-env

# activate
source ./tutorial-env/bin/activate
Enter fullscreen mode Exit fullscreen mode

安装 Django 及其依赖项

# install our dependencies
pip install ipython django django_extensions django-cors-headers "graphene-django>=2.0"

#create a django project
django-admin startproject djangoproject

# change directory
cd djangoproject

# create templates directory
mkdir templates

# create static folder
mkdir static

# create utils folder for initial data
mkdir utils

Enter fullscreen mode Exit fullscreen mode

2- 配置和运行

更新您的djangoproject /djangoproject/ settings.py文件。新增设置标记为“ New ...”

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    "django_extensions",# New! (useful extension)
    'graphene_django',  # New! (for graphql communication)
    'corsheaders',      # New! (for cors request in dev env)
]

# New (for improved interactive shell)
SHELL_PLUS = "ipython"

# New (it allows webpack development server to make cross origin request)
CORS_ORIGIN_WHITELIST = (
    'http://localhost:8080',
)


MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'corsheaders.middleware.CorsMiddleware', # New Add this
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': (os.path.join(BASE_DIR, 'templates'),), # New
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

#New 
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
Enter fullscreen mode Exit fullscreen mode

在开始项目之前,我们应该先进行数据库迁移。之后,我们将运行服务器并检查其是否正常工作。

# create migration for django-orm
python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

如果一切顺利,Django 服务器将会启动。您可以打开浏览器并访问地址 127.0.0.1:8000,您将看到类似这样的屏幕:

Django 运行界面

步骤 3:创建电影应用

我们将创建一个电影模型,其中包含电影应该具备的基本字段。

在此之前,我们应该先介绍一下专业方向的选择情况。

为什么海报的输入框是网址(URL)而不是图片输入框

由于不建议在生产环境中提供静态文件,我们仅使用 URL 字段。从远程获取图片并将其保存到生产存储是另一个话题,我们将在另一篇文章中讨论。因此,我们只会保存海报的 URL,而不是海报本身的图片文件。此外,直接发送静态文件(例如图片)也不是一个好方法。我们会将图片的完整 URL 发送给用户。然后,用户的浏览器会从该 URL 获取图片

*什么是鼻涕虫?为什么鼻涕虫应该是独一无二的? *

让我举个例子来说明:我最初cbsofyalioglu[com]/post/django-and-modern-js-libraries-backend上发表了这篇文章。

URL 的最后一部分django-and-modern-js-libraries-backend是帖子的别名,也是一个标识符,使该 URL 与其他帖子页面区分开来。

在本教程的 GraphQL 部分,您会看到我们将使用此 slug 作为查询参数,这意味着我们将根据 slug 执行数据库查询。因此,它应该是唯一的。

我们还可以选择另一个标识符作为 URL 标识符,但很明显,该 URL 将不是人类可读的地址。

搜索引擎索引和排名对于任何面向新用户的网站都至关重要。易读的URL地址不仅对用户有益,搜索引擎指南也建议使用易读的URL地址。此外,谷歌网站管理员指南也建议使用简洁明了的URL结构。

让我们来创建模型并定义它的属性和方法。下一步,我们将向数据库中填充初始记录。因此,我添加了一个负责数据库填充的类方法。

让我们创建一个 Django 应用。这个应用将包含我们的模型。数据库表将根据这个模型创建。API 请求也将基于这个模型。

# create new Django app
python manage.py startapp items
Enter fullscreen mode Exit fullscreen mode

更新设置.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    "django_extensions",
    'graphene_django', 
    'corsheaders',
    "items"             # New! (make our app will active)
]
Enter fullscreen mode Exit fullscreen mode

打开djangoproject / items/models.py文件,复制以下代码。


# items.models

from django.db import models

class  Movie(models.Model):

    id  = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=100)
    year = models.IntegerField(null=True)
    summary = models.TextField(max_length=5000,null=True)
    poster_url = models.URLField(blank=True, null=True)
    slug = models.SlugField(max_length=50, null=True,blank  =True, unique=True)
    # order items in descending order
    class  Meta:
        ordering = ["-year"]

    # the method which defines string output of class
    def  __str__(self):
        return  self.name

    # the method which loads initial data
    @classmethod
    def  import_records(cls, record_list):
        for record in record_list:
            # create record if id is not exist
            if  not  cls.objects.filter(id=record.get("id")).exists():
                new_movie =  cls.objects.create(**record)
            else:
                print(f"Id:{record.get('id')} is already exist.")
        print("Import operation done successfully")

Enter fullscreen mode Exit fullscreen mode
# make database migrations
python manage.py makemigrations
python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

步骤四:用初始数据填充数据库

目前我们的数据库中没有任何电影记录。我们将提供少量初始数据来创建一些电影记录。所有数据均来自社区构建的电影数据库(TMDb)。我们将在应用程序中使用这些记录。

首先,在djangoproject /utils文件中创建一个名为"initial_data.py" 的文件。之后,您可以将以下数据复制粘贴到这个新文件中。

initial_data = [{
    'id': 503919,
    'name': 'The Lighthouse',
    'year': 2019,
    'summary': 'The hypnotic and hallucinatory tale of two lighthouse keepers on a remote and mysterious New England island in the 1890s.',
    'slug': 'the-lighthouse-2019',
    'poster_url': 'https://image.tmdb.org/t/p/w185/3nk9UoepYmv1G9oP18q6JJCeYwN.jpg'
    },{
    'id': 475557,
    'name': 'Joker',
    'year': 2019,
    'summary': 'During the 1980s, a failed stand-up comedian is driven insane and turns to a life of crime and chaos in Gotham City while becoming an infamous psychopathic crime figure.',
    'slug': 'joker-2019',
    'poster_url': 'https://image.tmdb.org/t/p/w185/udDclJoHjfjb8Ekgsd4FDteOkCU.jpg'
    },{
    'id': 530385,
    'name': 'Midsommar',
    'year': 2019,
    'summary': "A couple travels to Sweden to visit a rural hometown's fabled mid-summer festival. What begins as an idyllic retreat quickly devolves into an increasingly violent and bizarre competition at the hands of a pagan cult.",
    'slug': 'midsommar-2019',
    'poster_url': 'https://image.tmdb.org/t/p/w185/rXsh4MI6uyVgZBSSzXCfitJnVPy.jpg'
  },{
    'id': 531428,
    'name': 'Portrait of a Lady on Fire',
    'year': 2019,
    'summary': 'On an isolated island in Bretagne at the end of the eighteenth century, a female painter is obliged to paint a wedding portrait of a young woman.',
    'slug': 'portrait-of-a-lady-on-fire-2019',
    'poster_url': 'https://image.tmdb.org/t/p/w185/3NTEMlG5mQdIAlKDl3AJG0rX29Z.jpg'
  }]
Enter fullscreen mode Exit fullscreen mode

现在,我们将导入并创建新的数据库记录。通常情况下,我们需要打开 Django shell。但是,由django_extensions提供的shell_plus命令功能更强大,因此我们将使用它。它会自动导入我们创建的所有应用。

# open interactive shell
python manage.py shell_plus
Enter fullscreen mode Exit fullscreen mode
# let's check database and verify it's empty
Movie.objects.all()
# prints: <QuerySet []>

# import the records which we took it from github repo
from utils.initial_data import initial_data

# create records in the database
Movie.import_records(initial_data)

# prints 'Import operation done successfully'

# query database and verify it is not empty
Movie.objects.all()
Enter fullscreen mode Exit fullscreen mode

我们的模型和数据库已准备就绪。您可以使用 * quit * 命令关闭 shell

下一节将介绍如何创建 GraphQL API。


GraphQL API

设置 GraphQL API

在本节中,我们将使用 Graphene(Python 的 GraphQL 框架实现)来构建应用程序的 API 部分。

本节内容包括:

  1. 创建另一个 Django 应用:我们将把所有 API 配置都放在那里。

  2. 创建一个包含三个部分的 API 架构:API 模型、解析器和查询。

  3. 创建 URL 端点:客户端应用程序将向此 URL 地址请求所有信息。

步骤 1 - 创建另一个用于 API 配置的 Django 应用

实际上,由于此应用不会创建或更新任何数据库表,因此没有义务开发另一个应用。但是,为了将所有 API 相关的配置集中在一个地方,我选择了这种方式。

让我们创建第二个后端应用程序。应用程序的名称不一定是“gql”,但如果您设置了其他名称,则稍后还应在settings.py 文件中更改 schema 的名称。

在项目根目录下打开终端。

# create app with the name gql
python manage.py startapp gql

# change directory
cd gql


# create schema.py file
touch schema.py
Enter fullscreen mode Exit fullscreen mode

步骤 2 - 创建 API 架构:API 模型、查询和解析器

考虑到本文的范围,API架构将分为三个部分。

具体如下:

  1. * API模型类型: * 一个类,它是电影模型的映射版本。如果响应不是原始类型,您可以基于此类发送响应。

  2. *查询: * 客户端应用程序将使用这些查询来处理不同的请求。

  3. *解析器: * 这些是字段的响应函数。当客户端请求与查询匹配时,解析器会发挥作用,完成所有逻辑运算,然后将信息发送回客户端。

* A ) * * API 模型类型和解析器*

ModelType 类是现有 Django 模型的映射版本。它是 Django 模型(或数据库)与 API 响应之间的中间层。ModelType 的字段与对应模型的字段相同。我们还可以创建不属于对应模型的自定义字段。

比较 Django 模型和相应的 GraphQL 模型

您可以从 Graphene Python 文档中查看其他标量类型。

我们将逐步编写 schema.py 文件。您可以复制粘贴。

import graphene
from items.models import Movie
from graphene_django.types import DjangoObjectType

# api-movie-model
class MovieType(DjangoObjectType):
    id = graphene.Int()
    name = graphene.String()
    year = graphene.Int()
    summary = graphene.String()
    poster_url = graphene.String()
    slug = graphene.String()

    # define which model will be the base
    class Meta:
        model = Movie

    # 'self' corresponds to the item of Django model 
    # like The Lighthouse or Joker
    def resolve_id(self, info):
        return self.id

    def resolve_name(self, info):
        return self.name

    def resolve_year(self, info):
        return self.year

    def resolve_summary(self, info):
        return self.summary

    def resolve_poster_url(self, info):
        return self.poster_url

    def resolve_slug(self, info):
        return self.slug
Enter fullscreen mode Exit fullscreen mode

让我来解释一下上面的代码。

“MovieType”类是Movie模型的映射版本。您可能会注意到所有字段都相同。我们在Meta类中定义了基础模型,因此Movie模型将作为基础模型。

需要说明的是,解析器名称采用蛇形命名法,例如“resolve_poster_url”。但是,当我们编写客户端查询时,它们将采用帕斯卡命名法,例如“posterUrl”。稍后您会看到这一点。

* B)查询和解析器*

客户端应用程序将使用这些查询来处理不同的请求。我们也会在其代码部分编写客户端查询。客户端查询应与服务器端查询相匹配。因此,这部分也定义了前端允许的请求

为了简单起见,我们只定义两个查询。

  • movie_list 查询(* resolve_movie_list *)返回数据库中的所有电影。

  • 电影查询(* resolve_movie)仅在参数(slug *)匹配时才返回特定电影。

让我们在 MovieType 类下方添加这段代码。


class Query(graphene.ObjectType):
    movie_list = graphene.List(MovieType)
    movie = graphene.Field(MovieType, slug=graphene.String())

    def resolve_movie_list(self, info, *_):
        # for large lists only query what you need
        return Movie.objects.all().only("name", "poster_url", "slug")

    def resolve_movie(self, info, slug):
        movie_queryset = Movie.objects.filter(slug=slug)
        if movie_queryset.exists():
            return movie_queryset.first()

schema = graphene.Schema(query=Query)
Enter fullscreen mode Exit fullscreen mode

在最后一行,您会看到一个 schema 对象。这是 API 的根节点。我们需要告诉 Django 服务器使用此对象作为我们的 API schema。为此,请更新 * settings.py * 文件。

# djangoproject/djangoproject/settings.py

# New - Add this part
GRAPHENE= {'SCHEMA': 'gql.schema.schema'}

# MIDDLEWARE = [..]

Enter fullscreen mode Exit fullscreen mode

步骤 3 - 创建 URL 端点

在 REST API 中,我们会为不同的请求定义不同的 URL 端点。GraphQL 的一个优点是,我们只需要定义一个端点,所有请求都将通过该端点进行。

复制以下代码并将其粘贴到djangoproject/djangoproject/ urls.py**** 文件中。

from django.contrib import admin
from django.urls import path
from graphene_django.views import GraphQLView
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),

    # apiclient on client-side will request this adress later
    path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),


    # index.html file will be our root template. When a user opens our webste,
    # this file will be sent by server at first. After then, api requests
    # will directed above address.

    # (it points to ~/Blog/djr/templates/index.html)
    # (currently there is no file, webpack production bundle will come here )
    path("", TemplateView.as_view(template_name="index.html")),
    ]
Enter fullscreen mode Exit fullscreen mode

您注意到我们设置了 ` graphiql=True`。这是 GraphQL 交互式面板。我们可以通过此面板像客户端应用程序一样进行查询。您还可以看到所有查询的详细信息。

现在,请在根文件夹“djangoproject/”下运行服务器。


python manage.py runserver

Enter fullscreen mode Exit fullscreen mode

在浏览器中打开 * 127.0.0.1:8000/graphql * 地址。我们将使用特定的标识符(slug)查询电影。在左侧面板中粘贴此标识符,然后点击 *执行查询* 按钮。

请注意,我们要求字段采用 PascalCase 命名法。(* posterUrl *)


query {

movie(slug:"the-lighthouse-2019"){

id, name, posterUrl

}

}

Enter fullscreen mode Exit fullscreen mode

响应将采用如下所示的 JSON 格式。


{

"data": {

"movie": {

"id": 503919,

"name": "The Lighthouse",

"posterUrl": "https://image.tmdb.org/t/p/w185/3nk9UoepYmv1G9oP18q6JJCeYwN.jpg"

}

}

}

Enter fullscreen mode Exit fullscreen mode

我们的API已准备就绪,可以响应请求。本部分教程到此结束。

现在,我们将制作两个不同的客户端应用程序。请选择其中一个继续。

文章来源:https://dev.to/canburaks/django-and-modern-js-libraries-backend-1-2p9p