GraphQL с входным проектом MongoDB

Python

Что такое GraphQL

GraphQL — это язык запросов, ориентированный на API. В первые дни Интернета требования были в основном веб-, а требования к данным и бизнесу в то время не были сложными, поэтому метод RestAPI мог полностью удовлетворить требования. Однако с развитием Интернета объем данных увеличивается, а потребности бизнеса меняются. Существуют также различные клиенты, которым требуется адаптация интерфейса, а подход на основе RestAPI становится все более жестким, поэтому на свет появился GraphQL. Он может обеспечить по крайней мере следующие три преимущества

  1. GraphQL упрощает запросы API

Разным клиентам иногда нужно возвращать данные в разных форматах.Предыдущий способ использования RestAPI требует, чтобы серверная часть предоставляла отдельный интерфейс для каждого клиента. По мере роста потребностей бизнеса стоимость обслуживания случайным образом увеличивается в геометрической прогрессии. А использовать GraphQL веселее, нужно только написать набор интерфейсов

  1. Решите чрезмерную зависимость передней и задней частей

В процессе разработки интерфейсу и серверу необходимо многократно подтверждать каждое поле, чтобы разработка не была на полпути.Поскольку нет правильного поля, необходимо изменять код большими блоками. Теперь удобнее иметь GraphQL, какие поля вам нужны, соответствующий синтаксис запроса вы можете написать сами

  1. Экономьте ресурсы сети и памяти компьютера

Перед написанием интерфейсов через RestAPI есть большая проблема, для определения интерфейсов нужно проделать большую работу на ранней стадии, и прилагаются различные усилия, чтобы интерфейс был разделен, но даже при этом нет способ справиться с внезапными изменениями спроса. Иногда необходимо вернуть только определенный тип данных определенного пользователя, но другая информация пользователя должна быть возвращена вместе, что не только тратит впустую ресурсы сети, но и потребляет производительность компьютера. Очевидно, недостаточно элегантный, GraphQL еще раз доказал свою мощь, он может предоставить данные, необходимые для самостоятельного приобретения, сколько использовать, сколько брать, можно сказать, что он довольно безвреден для окружающей среды.

PS: дополнительные сведения о GraphQL см. в ссылках в конце статьи.

представлять

В этой статье я буду использовать конкретный экземпляр Todo List и работать с вами над пошаговой ручной сборкой экземпляра проекта GraphQL + MongoDB. В нем мы будем использовать следующие библиотеки, которые необходимо установить заранее перед запуском:

  1. graphene_mongo
  2. graphene
  3. mongoengine
  4. flask_graphql
  5. Flask

Прежде чем мы начнем, давайте разберем наши основные требования. Мы хотим создать продукт Todo List. У нас есть только две основные таблицы, одна — таблица пользователей, в которой хранится вся информация о пользователях, а другая — таблица задач, в которой хранятся все пользователи, информация о задачах. Таблица задач связана с соответствующим пользователем по идентификатору пользователя. Структура таблицы соответствует отношению «один ко многим», а основные поля данных следующие:

таблица задач

{ 
    "_id" : ObjectId("5c353fd8771502a411872712"), 
    "_in_time" : "2019-01-09 08:26:53", 
    "_utime" : "2019-01-09 09:26:39", 
    "task" : "read", 
    "start_time" : "2019-01-09 08:26:53", 
    "end_time" : "2019-01-09 08:26:53", 
    "repeat" : [
        "Wed"
    ], 
    "delete_flag" : NumberInt(0), 
    "user" : "1"
}

пользовательская таблица

{ 
    "_id" : "1", 
    "_in_time" : "2019-01-09 08:39:16", 
    "_utime" : "2019-01-09 09:23:25", 
    "nickname" : "xiao hong", 
    "sex" : "female", 
    "photo": "http://xh.jpg",
    "delete_flag" : NumberInt(0)
}

Структура проекта

Изображение стоит тысячи слов.Чтобы более четко понять общую структуру проекта, я напечатаю общую структуру каталогов проекта.Друзья могут обратиться к структуре каталогов и увидеть следующие этапы построения.

----task_graphql\
    |----api.py
    |----database\
    |    |----__init__.py
    |    |----base.py
    |    |----model_task.py
    |    |----model_user.py
    |----requirements.txt
    |----schema.py
    |----schema_task.py
    |----schema_user.py

pic_1.png

  • user_model и task_model определяют модули данных и напрямую подключаются к базе данных mongo.
  • Операции схемы shema_user и schema_task, определенные на верхнем уровне, выполняют операции добавления, удаления, модификации и запроса в модели данных.
  • Наконец, flask создает внешний сервис API для взаимодействия с внешними запросами.

Создать модель данных

Структура нашей модели данных очень проста

  • user_model перечисляет всю информацию о пользователе
  • task_model перечисляет всю информацию о задаче, связанную с пользовательской таблицей через поле пользователя, указывая, какому пользователю принадлежит задача.

pic_2.png

base.py
from mongoengine import connect

connect("todo_list", host="127.0.0.1:27017")

Вам нужно только указать соответствующую информацию о ссылке на базу данных и базу данных, вызвав соединение mongoengine, и соединение будет автоматически распознано при импорте непосредственно в модуль Flask.

model_user.py
import sys
sys.path.append("..")

from mongoengine import Document
from mongoengine import (StringField, IntField)
from datetime import datetime


class ModelUser(Document):

    meta = {"collection": "user"}

    id = StringField(primary_key=True)
    _in_time = StringField(required=True, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    _utime = StringField(required=True, default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
    nickname = StringField(required=True)
    sex = StringField(default="unknown", required=True)
    delete_flag = IntField(default=0, required=True)

Документы данных, которые должны быть определены, наследуются через документ mongoengine, который может преобразовывать соответствующие поля в атрибуты класса, чтобы упростить различные операции с данными позже.Поле мета указывает, на какую таблицу mongo вам нужно ссылаться.

model_task.py
import sys
sys.path.append("..")

from mongoengine import Document
from mongoengine import (StringField, ListField, IntField, ReferenceField)

from .model_user import ModelUser
from datetime import datetime


class ModelTask(Document):

    meta = {"collection": "task"}
    
    _in_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
    _utime = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
    task = StringField(default="", required=True)
    start_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
    end_time = StringField(default=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), required=True)
    repeat = ListField(StringField(required=True))
    delete_flag = IntField(default=0, required=True)
    user = ReferenceField(ModelUser, required=True)

Среди них required указывает, что это поле является обязательным, и по умолчанию можно установить значение по умолчанию для этого поля. ReferenceField может указать, с какой моделью ассоциироваться, вот поле ModelUser, а ассоциация по умолчанию соответствует полю _id в соответствующей таблице монго.

Создайте запрос GraphQL

Теперь, когда мы завершили соединение между базой данных и частью модели, мы создадим часть API.В нашем каталоге task_graphql есть два файла, schema_task.py и schema_user.py, которые сопоставляют классы model_task и model_user со схемой Graphene. объекты соответственно

schema_task.py
from database.model_task import ModelTask
from graphene_mongo import MongoengineObjectType

import graphene
import schema_user

from datetime import datetime


class TaskAttribute:
    id = graphene.ID()
    _in_time = graphene.String()
    _utime = graphene.String()
    task = graphene.String()
    start_time = graphene.String()
    end_time = graphene.String()
    repeat = graphene.List(graphene.String)
    delete_flag = graphene.Int()
    user = graphene.String()


class Task(MongoengineObjectType):

    class Meta:
        model = ModelTask


class TaskNode(MongoengineObjectType):
    class Meta:
        model = ModelTask
        interfaces = (graphene.relay.Node, )
schema_user.py
from database.model_task import ModelTask
from graphene_mongo import MongoengineObjectType

import graphene

from datetime import datetime

class TaskAttribute:
    id = graphene.ID()
    _in_time = graphene.String()
    _utime = graphene.String()
    task = graphene.String()
    start_time = graphene.String()
    end_time = graphene.String()
    repeat = graphene.List(graphene.String)
    delete_flag = graphene.Int()
    user = graphene.String()

class Task(MongoengineObjectType):

    class Meta:
        model = ModelTask

class TaskNode(MongoengineObjectType):
    class Meta:
        model = ModelTask
        interfaces = (graphene.relay.Node, )

Теперь мы создаемschema.pyимпортируйте только что определенные файлы schema_task.py и schema_user.py и определите два внешних интерфейса доступа.

  • задачи: запрашивать всю информацию о задаче и возвращать список
  • пользователи: запрашивать всю информацию о пользователе и возвращать список
import schema_user
import schema_task
import graphene
from graphene_mongo.fields import MongoengineConnectionField


class Query(graphene.ObjectType):

    node = graphene.relay.Node.Field()

    tasks = MongoengineConnectionField(schema_task.TaskNode)

    users = MongoengineConnectionField(schema_user.UserNode)

schema = graphene.Schema(query=Query)

Создайте приложение Flask

Создайте его в своем домашнем каталогеapi.pyмы представили соединение с базой данных и схему, которые мы определили ранее, и использовали метод Flask add_url_rule, чтобы связать их. Чтобы облегчить доступ, мы представили метод GraphQLView для flask_graphql для визуализации интерфейса для облегчения отладки.

from flask import Flask
from schema import schema
from flask_graphql import GraphQLView
from database.base import connect
from logger import AppLogger

log = AppLogger("task_graphql.log").get_logger()

app = Flask(__name__)
app.debug = True

app.add_url_rule("/graphql", view_func=GraphQLView.as_view("graphql", schema=schema, graphiql=True))

if __name__ == '__main__':
    app.run()

На данный момент мы успешно создали запрашиваемый интерфейс Todo List с помощью graphql. Мы можем использовать его для тестирования интерфейса запросов. Затем, прежде чем запускать запрос, вам нужно самостоятельно смоделировать данные для монго.

pic_3.png

Получаем доступ к адресу интерфейса (http://127.0.0.1:5000/graphql), чтобы проверить и увидеть эффект

pic_4.png

Добавить метод обновления GraphQL (мутация)

GraphQL официально интегрирует операции обновления и создания при мутации, которые включают в себя функции вставки и обновления данных.Далее мы продолжим вышеуказанные операции и улучшим эту часть функции.

schema_task.py
from database.model_task import ModelTask
from graphene_mongo import MongoengineObjectType

import graphene

from datetime import datetime


class TaskAttribute:
    id = graphene.ID()
    _in_time = graphene.String()
    _utime = graphene.String()
    task = graphene.String()
    start_time = graphene.String()
    end_time = graphene.String()
    repeat = graphene.List(graphene.String)
    delete_flag = graphene.Int()
    user = graphene.String()


class Task(MongoengineObjectType):

    class Meta:
        model = ModelTask


class TaskNode(MongoengineObjectType):
    class Meta:
        model = ModelTask
        interfaces = (graphene.relay.Node, )


class CreateTaskInput(graphene.InputObjectType, TaskAttribute):
    pass


class CreateTask(graphene.Mutation):

    task = graphene.Field(lambda: TaskNode)

    class Arguments:
        input = CreateTaskInput(required=True)

    def mutate(self, info, input):
        task = ModelTask(**input)
        task.save()
        return CreateTask(task=task)


class UpdateTask(graphene.Mutation):

    task = graphene.Field(lambda: TaskNode)

    class Arguments:
        input = CreateTaskInput(required=True)

    def mutate(self, info, input):
        id = input.pop("id")
        task = ModelTask.objects.get(id=id)
        task._utime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        task.update(**input)
        task.save()
        return UpdateTask(task=task)
schema_user.py
from database.model_user import ModelUser
from graphene_mongo.types import MongoengineObjectType
import graphene
from datetime import datetime


class UserAttribute:
    id = graphene.String()
    _in_time = graphene.String()
    _utime = graphene.String()
    nickname = graphene.String()
    photo = graphene.String()
    sex = graphene.String()
    delete_flag = graphene.Int()


class User(MongoengineObjectType):

    class Meta:
        model = ModelUser


class UserNode(MongoengineObjectType):

    class Meta:
        model = ModelUser
        interfaces = (graphene.relay.Node, )


class CreateUserInput(graphene.InputObjectType, UserAttribute):
    pass


class CreateUser(graphene.Mutation):

    user = graphene.Field(lambda: UserNode)

    class Arguments:
        input = CreateUserInput(required=True)

    def mutate(self, info, input):
        user = ModelUser(**input)
        user.save()
        return CreateUser(user=user)


class UpdateUser(graphene.Mutation):

    user = graphene.Field(lambda: UserNode)

    class Arguments:
        input = CreateUserInput(required=True)

    def mutate(self, info, input):
        id = input.pop("id")
        user = ModelUser.objects.get(id=id)
        user._utime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        user.update(**input)
        user.save()
        return UpdateUser(user=user)

Как вы можете видеть с первого взгляда на код, мы передадим информацию, которую нам нужно добавить, через ввод, а затем сопоставим соответствующие параметры. Давайте посмотрим на эффект создания данных на примере

pic_5.png

Давайте еще раз попробуем операцию изменения данных, вот так

pic_6.png

бинго! ! !

На данный момент мы завершили работу GraphQL с MongoDB.

Полный проект смотрите на github:GitHub.com/hackshaman/он…

Вышеизложенное - это методы, к которым я пришел после того, как наступил на множество ям по пути.Если есть какие-то упущения, я надеюсь их исправить.

использованная литература

tail_qrcode.jpg