【问题标题】:GraphQL + Django: resolve queries using raw PostgreSQL queryGraphQL + Django:使用原始 PostgreSQL 查询解析查询
【发布时间】:2017-01-15 19:48:51
【问题描述】:

在使用外部数据库从多个表中获取数据时,将 GraphQL 与 Django 一起使用的最佳方法是什么(即,创建一个 Django 模型来表示数据不会对应于我的数据库中的单个表)?

我的方法是暂时放弃使用 Django 模型,因为我认为我还没有完全理解它们。 (我对 Django 和 GraphQL 完全陌生。)我已经建立了一个简单的项目,其中包含一个连接了外部 Postgres DB 的应用程序。我按照Graphene Django tutorial 中的所有设置进行操作,然后当我意识到我创建的模型是几个表的组合时遇到了障碍。

我有一个查询发送回映射到模型中字段的正确列,但我不知道如何使它成为动态连接,这样当我的 API 被命中时,它会查询我的数据库并映射行到我在 Django 中定义的模型模式。

此后我的方法一直是避免使用模型并使用 Steven Luscher 的演讲中演示的更简单的方法:Zero to GraphQL in 30 Minutes

TLDR;

目标是能够访问我的 GraphQL 端点,使用我的 django.db.connection 中的游标对象来获取应该解析为 OrderItemTypes 的 GraphQLList 的字典列表(见下文)。

问题是,当我使用查询访问以下端点时,每个值都为空值:

localhost:8000/api?query={orderItems{date,uuid,orderId}}

返回:

{ "data":{ "orderItems":[ {"date":null, "uuid":null, "orderId":null }, ... ] } }

project/main/app/schema.py

import graphene
from django.db import connection


class OrderItemType(graphene.ObjectType):
    date = graphene.core.types.custom_scalars.DateTime()
    order_id = graphene.ID()
    uuid = graphene.String()

class QueryType(graphene.ObjectType):
    name = 'Query'
    order_items = graphene.List(OrderItemType)

    def resolve_order_items(root, args, info):
        data = get_order_items()

        # data prints out properly in my terminal
        print data
        # data does not resolve properly
        return data


def get_db_dicts(sql, args=None):
    cursor = connection.cursor()
    cursor.execute(sql, args)
    columns = [col[0] for col in cursor.description]
    data = [
        dict(zip(columns, row))
        for row in cursor.fetchall() ]

    cursor.close()
    return data

def get_order_items():
    return get_db_dicts("""
        SELECT j.created_dt AS date, j.order_id, j.uuid
        FROM job AS j
        LIMIT 3;
    """)

在我的终端中,我从 QueryType 的解析方法打印,我可以看到数据成功地从我的 Postgres 连接返回。但是,GraphQL 给了我空值,所以它必须在 resolve 方法中,一些映射被搞砸了。

[ { 'uuid': u'7584aac3-ab39-4a56-9c78-e3bb1e02dfc1', 'order_id': 25624320, 'date': datetime.datetime(2016, 1, 30, 16, 39, 40, 573400, tzinfo=<UTC>) }, ... ]

如何正确地将我的数据映射到我在 OrderItemType 中定义的字段?

这里还有一些参考资料:

project/main/schema.py

import graphene

from project.app.schema import QueryType AppQuery

class Query(AppQuery):
    pass

schema = graphene.Schema(
    query=Query, name='Pathfinder Schema'
)

文件树

|-- project
    |-- manage.py
    |-- main
        |-- app
            |-- models.py
            |-- schema.py
        |-- schema.py
        |-- settings.py
        |-- urls.py

【问题讨论】:

    标签: python django postgresql graphql graphene-python


    【解决方案1】:

    GraphQL Python / Graphene 上的默认解析器尝试使用 getattr 解析根对象中的给定 field_name。 因此,例如,名为 order_items 的字段的默认解析器将类似于:

    def resolver(root, args, context, info):
        return getattr(root, 'order_items', None)
    

    知道,在 dict 中执行 getattr 时,结果将是 None(要访问 dict 项目,您必须使用 __getitem__ / dict[key])。

    因此,解决您的问题可能就像从 dicts 更改为将内容存储到 namedtuples 一样简单。

    import graphene
    from django.db import connection
    from collections import namedtuple
    
    
    class OrderItemType(graphene.ObjectType):
        date = graphene.core.types.custom_scalars.DateTime()
        order_id = graphene.ID()
        uuid = graphene.String()
    
    class QueryType(graphene.ObjectType):
        class Meta:
            type_name = 'Query' # This will be name in graphene 1.0
    
        order_items = graphene.List(OrderItemType)
    
        def resolve_order_items(root, args, info):
            return get_order_items()    
    
    
    def get_db_rows(sql, args=None):
        cursor = connection.cursor()
        cursor.execute(sql, args)
        columns = [col[0] for col in cursor.description]
        RowType = namedtuple('Row', columns)
        data = [
            RowType(*row) # Edited by John suggestion fix
            for row in cursor.fetchall() ]
    
        cursor.close()
        return data
    
    def get_order_items():
        return get_db_rows("""
            SELECT j.created_dt AS date, j.order_id, j.uuid
            FROM job AS j
            LIMIT 3;
        """)
    

    希望这会有所帮助!

    【讨论】:

    • 这非常有帮助,感谢您抽出宝贵的时间!一个小的编辑是行列表应该在传递给命名元组RowType(*row) 之前展开。一般来说,这是一种好的做法还是有更好的方法来获取外部数据?
    • 如果您看到我的第二次尝试(针对此问题发布的另一个答案),我使用您建议的 dict[key] 方法为每个字段使用了解析器。从我刚刚运行的几个测试来看,这两种方法似乎都在相同的时间内返回了数据。我想知道 GraphQL 的返回时间有点慢是否正常(缓存后 1000 条记录 ~ 3-4 秒;在缓存之前,同一数据集需要 ~6 秒)。
    • 如果使用开发版,查询解析速度至少快10倍(300ms?)。您可以使用pip install graphene-django&gt;=1.0.dev 安装它。希望这会有所帮助!
    【解决方案2】:

    这里是临时的解决方法,虽然我希望有一些更清洁的东西来处理snake_cased 字段名。

    project/main/app/schema.py

    from graphene import (
        ObjectType, ID, String, Int, Float, List
    )
    from graphene.core.types.custom_scalars import DateTime
    from django.db import connection
    
    ''' Generic resolver to get the field_name from self's _root '''
    def rslv(self, args, info):
        return self.get(info.field_name)
    
    
    class OrderItemType(ObjectType):
        date = DateTime(resolver=rslv)
        order_id = ID()
        uuid = String(resolver=rslv)
        place_id = ID()
    
        ''' Special resolvers for camel_cased field_names '''
        def resolve_order_id(self, args, info):
        return self.get('order_id')
    
        def resolve_place_id(self, args, info):
            return self.get('place_id')
    
    class QueryType(ObjectType):
        name = 'Query'
        order_items = List(OrderItemType)
    
        def resolve_order_items(root, args, info):
            return get_order_items()
    

    【讨论】:

    • 应该注意的是,拥有所有这些解析器会使 GraphQL 非常慢。
    【解决方案3】:

    也可以只更改 graphene.object 的默认解析器。

    我相信重组后以下内容会起作用。

    from graphene.types.resolver import dict_resolver
    
    class OrderItemType(ObjectType):
    
        class Meta:
            default_resolver = dict_resolver
    
        date = DateTime()
        order_id = ID()
        uuid = String()
        place_id = ID()
    

    即使这种方法不能直接适用于上述问题,这个问题是我在研究如何做到这一点时发现的。

    dict_resolver

    【讨论】:

      猜你喜欢
      • 2012-12-06
      • 1970-01-01
      • 2018-05-15
      • 2015-11-21
      • 2012-03-16
      • 1970-01-01
      • 2015-05-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多