将GraphQL整合到Django中的详细教程

686 阅读8分钟

简介

GraphQL是一种API查询语言,是Facebook在2012年开发的,供内部使用。后来,它在2015年被开源。根据Graphql官方网站的介绍:

"GraphQL是一种API的查询语言,也是用现有数据完成这些查询的运行时间。GraphQL为你的API中的数据提供了一个完整的、可理解的描述,让客户有能力准确地要求他们所需要的东西,而不是更多,使API更容易随着时间的推移而发展,并实现强大的开发者工具。"

客户端可以在响应中询问他们需要的数据,而不需要任何额外的数据,这意味着你可以控制你想要的数据的结构。在一次请求中访问许多资源。
谈论GraphQL的力量已经超出了本文的重点,请访问官方网站了解更多关于GraphQL的信息。

在本教程中,我们将专注于如何将GraphQL集成到Django项目中,并有效地使用它来查询API以获得有效的结果。

本教程的前提条件:

  • 你的本地机器上已经安装了python
  • 有python的基本知识
  • Django的基本知识
  • 互联网连接

我们将建立一个简单的产品目录,用于这个整合,我们将通过GraphQL端点公开数据。所以让我们开始工作吧。

项目设置

创建我们的项目文件夹

首先,让我们为我们的新项目创建一个目录,打开你的终端,导航到你想保存项目的文件夹。运行以下命令:

$ mkdir graphql-product-catalog

让我们把现在所在的目录改为新创建的目录:

$ cd graphql-product-catalog

创建并激活我们的项目虚拟环境

在python中,为任何需要安装新包的项目创建一个虚拟环境也是很重要的,这可以确保这些包对项目来说是唯一的,不会导致你在本地机器上更新其他项目的包,从而破坏你的工作项目。

让我们运行下面的命令:

$ python -m venv env

这里我把我的新环境称为env,它在一个目录env中创建了我们的python环境。
接下来,我们需要激活这个环境,让我们在这里工作:

$ . env/bin/activate

在我们的环境中安装Django

现在我们有了环境,我们需要在其中安装Django,现在安装任何版本的Django都很方便,而且不会影响我们本地机器上的其他项目的版本。 让我们通过运行以下命令来安装Django:

$ pip install django

创建项目

现在我们已经安装了Django,现在让我们继续通过运行以下命令来创建我们的项目:

$ django-admin startproject product_catalog

这里我们使用Django命令行工具django-admin来创建我们的项目,该项目将被创建在一个名为product_catalog的目录中。

接下来,我们需要通过运行以下命令导航到 product_catalog 目录中:

$ cd product_catalog

其中我们有一个类似于的目录结构:

+-- _product_catalog
|   +-- __init__.py
|   +-- settings.py
|   +-- urls.py
|   +-- wsgi.py+-- manage.py

创建我们的目录应用程序

Django项目的各个部分被称为应用程序,大规模的Django项目可以有尽可能多的应用程序。我们将需要为我们的产品目录调用目录创建一个应用程序。所以我们将通过运行以下命令来进行:

$ django-admin startapp catalog

该命令创建了一个目录call catalog,结构如下

+-- _catalog
|   +-- _migration
|       +-- __init__.py
|   +-- __init__.py
|   +-- admin.py
|   +-- apps.py
|   +-- models.py
|   +-- tests.py
|   +-- views.py

现在整个项目结构应该是这样的

+-- _product_catalog
|   +-- __init__.py
|   +-- settings.py
|   +-- urls.py
|   +-- wsgi.py
+-- _catalog
|   +-- _migration
|       +-- __init__.py
|   +-- __init__.py
|   +-- admin.py
|   +-- apps.py
|   +-- models.py
|   +-- tests.py
|   +-- views.py
+-- manage.py

接下来让我们构建我们的目录应用程序模型。在catelog文件夹内的models.py文件中,让我们添加以下代码

from django.db import models
# Create your models here.
class Category(models.Model):
    name = models.CharField(max_length=255, unique=True)
    description = models.TextField(max_length=255)

    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.IntegerField()
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="products")
    
    def __str__(self):
        return self.name

在这里我们定义了Category模型来组织产品的分类,然后我们的Product模型也与Category有关系。

我们需要在我们的网站管理中注册这些模型,所以打开目录应用程序中的admin.py文件并添加以下代码:

from django.contrib import admin
from .models import Category, Product
# Register your models here.

admin.site.register(Category)
admin.site.register(Product)

这里我们只是做了一个基本的注册,并允许Django用默认设置来处理渲染。

接下来,我们需要通过在settings.py文件的INSTALLED_APPS列表中注册,使我们的应用被项目知道:

INSTALLED_APPS = [
    ...
    'catalog',
]

由于我们所做的项目是一个简单的项目,我们将坚持使用Django的默认数据库配置,使用SQLite数据库。接下来是用于准备通过运行以下命令来迁移我们的目录应用表:

$ python manage.py makemigrations catalog

接下来,我们通过运行下面的迁移命令来迁移我们的数据库

$ python manage.py migrate

这将把我们的目录模型和Django相关的模型一起迁移到数据库中。

接下来,我们要为Django管理仪表盘创建超级用户,使我们能够在仪表盘上创建一些类别和产品。现在运行下面的命令并按照下面的指示操作:

$ python manage.py createsuperuser

接下来,我们通过运行以下命令来启动我们的开发服务器:

$ python manage.py runserver

默认情况下,Django在8000端口启动我们的服务器,但你可以在命令后面加上一个端口号

$ python manage.py runserver 8001

现在在浏览器上访问http://127.0.0.1:8001/,看看我们的应用程序是否正在运行

App home page

应用程序主页

你可以使用我们在上面创建超级用户步骤中设置的凭证,导航到 http://127.0.0.1:8001/admin ,访问管理站点。

Django admin site

Django管理网站你可以继续添加一些类别和产品到应用程序。

集成GraphQL

现在我们将把Graphql集成到项目中,以查询我们的产品。我们将使用一个名为Graphene-Django的软件包。

让我们继续安装Graphene-Django,在终端上运行以下命令:
注意:验证你的环境仍然处于活动状态。

$ pip install graphene-django

接下来,我们需要在 settings.py 的 INSTALLED_APPS 列表中注册新安装的软件包。

INSTALLED_APPS = [
...
"django.contrib.staticfiles", # Required for GraphiQL
"graphene_django"
]

如果你注意到我们还注册了django.contrib.staticfiles,这是Graphene-Django所需要的,用于渲染。

接下来,我们要为我们的图创建一个入口点。所以让我们在urls.py中添加以下几行。

...
from graphene_django.views import GraphQLView
urlpatterns = [
      ...
      path("graphql", GraphQLView.as_view(graphiql=True)),
]

看上面的代码,我们设置了graphiql=True,这将为我们的图形启用浏览器API,但如果你不希望浏览器用于我们的图形,你可以将其设置为False。

接下来是告诉graphene-Django在哪里可以找到我们的模式。架构就像我们的图的结构。 在我们的settings.py中添加以下代码

GRAPHENE = {    "SCHEMA": "catalog.schema.schema"}

这将指向我们将在目录应用中创建的schema.py文件。
接下来,在我们的目录应用文件夹中创建schema.py文件,内容如下:

import graphene
from graphene_django import DjangoObjectType
from .models import Category, Product

class CategoryType(DjangoObjectType):
    class Meta:
        model = Category
        fields = ("id", "name", "description", "products")

class ProductType(DjangoObjectType):
    class Meta:
        model = Product
        fields = ("id", "name", "description", "category", 'price', 'quantity')

class Query(graphene.ObjectType):
    products = graphene.List(
        ProductType,  category=graphene.String(required=False))
    categories = graphene.List(CategoryType)

    def resolve_products(root, info, category=None):
        if category:
            return Product.objects.filter(category__name=category) 
       # We can easily optimize query count in the resolve method
        return Product.objects.select_related("category").all()

    def resolve_categories(root, info):
        return Category.objects.all()

schema = graphene.Schema(query=Query)

在这里,我们通过创建两个继承自graphene_django模块中DjangoObjectType的类,为Category和Product模型创建了我们的模式,提供了我们想要在Graphql中公开的模型和模型域。

接下来,我们创建了一个继承自graphene.ObjectType类的Query类,并为我们的Graphql查询提供了配置。在这里,我们打开了产品和类别,这些产品和类别是由类的resolve_products和resolve_categories方法解决的。这些方法接受两个必要的参数(root和info),而resolve_products接受一个额外的参数category,用于按类别进行简单的产品过滤。

测试我们的应用程序
接下来,如果我们的应用程序已经被关闭,我们再次启动它,运行

$ python manage.py runserver 8001

在你的浏览器上访问http://120.0.0.1:8001/graphql,看看我们的Graphql的交互式屏幕。

Graphql preview

Graphql预览你可以玩玩下面的一些命令,查询通过管理网站添加的数据。

{
  products {
    id
    name,
    price,
    quantity,
    category {
      id
      name
    }
  }
}

Graphql response preview

Graphql响应预览其他可以尝试的命令包括:

{
categories {
   name,
   products
  }
}
{
products(category:'category name from previous query ') {
  id,
  name,
  description,
  price
 }
}

在这里,我们在查询中添加了一个过滤器,以查询某个特定类别的产品。

创建和更新

现在我们只能查询,但可以创建或更新。我们可以通过更新我们的schema.py代码,在ProductType类之后添加创建和更新功能到我们的集成:

...

class UpdateCategory(graphene.Mutation):
    class Arguments:
        # The input arguments for this mutation
        name = graphene.String(required=True)
        id = graphene.ID()

    # The class attributes define the response of the mutation
    category = graphene.Field(CategoryType)

    @classmethod
    def mutate(cls, root, info, name, id):
        category = Category.objects.get(pk=id)
        category.name = name
        category.save()
        # Notice we return an instance of this mutation
        return UpdateCategory(category=category)


class CreateCategory(graphene.Mutation):
    class Arguments:
        # The input arguments for this mutation
        name = graphene.String(required=True)

    # The class attributes define the response of the mutation
    category = graphene.Field(CategoryType)

    @classmethod
    def mutate(cls, root, info, name):
        category = Category()
        category.name = name
        category.save()
        # Notice we return an instance of this mutation
        return CreateCategory(category=category)

class ProductInput(graphene.InputObjectType):
    name = graphene.String()
    description = graphene.String()
    price = graphene.Float()
    quantity = graphene.Int()
    category = graphene.Int()


class CreateProduct(graphene.Mutation):
    class Arguments:
        input = ProductInput(required=True)

    product = graphene.Field(ProductType)
    
    @classmethod
    def mutate(cls, root, info, input):
        product = Product()
        product.name = input.name
        product.description = input.description
        product.price = decimal.Decimal(input.price)
        product.quantity = input.quantity
        product.category_id = input.category
        product.save()
        return CreateProduct(product=product)


class UpdateProduct(graphene.Mutation):
    class Arguments:
        input = ProductInput(required=True)
        id = graphene.ID()

    product = graphene.Field(ProductType)
    
    @classmethod
    def mutate(cls, root, info, input, id):
        product = Product.objects.get(pk=id)
        product.name = input.name
        product.description = input.description
        product.price = decimal.Decimal(input.price)
        product.quantity = input.quantity
        product.category_id = input.category
        product.save()
        return UpdateProduct(product=product)

class Mutation(graphene.ObjectType):
    update_category = UpdateCategory.Field()
    create_category = CreateCategory.Field()
    create_product = CreateProduct.Field()
    update_product = UpdateProduct.Field()

...


schema = graphene.Schema(query=Query, mutation=Mutation)

这里我们引入六个新的类来处理我们的突变。类的名字是不言自明的,它将做什么。最后一个类Mutation是注册突变动作的地方,然后我们继续在我们的Schema构造函数中注册我们的突变:

schema = graphene.Schema(query=Query, mutation=Mutation)

我们可以通过这些查询来测试出我们的突变。创建和更新类别:

mutation {
 create_category:createCategory(name: "Books") {
  category {
   id,
   name,
  }
 }
}
mutation {
 update_category: updateCategory(name: "Electronic", id: 1) {
  category {
  id,
  name
  }
 }
}

创建和更新产品

mutation {

create_product: createProduct(input: {name:"New product",description:"a brief description", price: 300.00, quantity:3, category:1 }){
product {
id,
name,
description,
price,
quantity

}
}
}
mutation {
update_product: updateProduct(input: {name:"New product",description:"a brief description", price: 300.00, quantity:3, category:1 }, id:1){
product {
id,
name,
description,
price,
quantity
}
}
}

总结

在这个简短的教程结束时,我们已经能够使用Graphene-Django包将Graphql集成到Django中。这个包有很多我们在本教程中无法介绍的功能,删除信息和Graphql提供的许多其他功能。为了让你的项目能更好地利用这个包,你可以在这里查看这个包的官方文档。如果你不熟悉Graphql的丰富特性和功能,你也可以阅读更多关于Graphql的内容。 本教程的源代码可以在Github上获取。谢谢你的阅读和练习。