2016-09-08 80 views
5

當使用外部數據庫從多個表中獲取數據時,使用Graphql和Django的最佳方式是什麼(即,創建用於表示數據的Django模型不會對應於我的數據庫中有一張表)?GraphQL + Django:使用原始PostgreSQL查詢來解析查詢

我的方法是暫時放棄使用Django模型,因爲我不認爲我完全理解它們。 (我對Django和GraphQL都是全新的。)我已經用一個連接了外部Postgres數據庫的應用程序設置了一個簡單的項目。我遵循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 }, ... ] } } 

項目/主/ 應用/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給了我空值,所以它必須在解決方法中,一些映射被搞砸了。

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

如何正確地將我的數據映射到我在OrderItemType中定義的字段?

這裏有一些更多的參考資料:

項目/主/ 模式。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 

回答

6

默認解析器嘗試使用GETATTR做一個給定的FIELD_NAME的分辨率根對象。 因此,例如,對於名爲order_items字段的默認解析器會是這樣的:

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

知道的是,在一個dictgetattr時,其結果將是None(你將在訪問字典項使用__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; 
    """) 

希望這會有所幫助!

+0

這非常有幫助,感謝您花時間!一個小的編輯是在傳遞給named_pleple RowType(* row)'之前應該對行列表進行擴展。一般來說,這是一個好的做法還是有更好的方法來獲取外部數據? –

+0

如果您看到我的第二次嘗試(發佈到此問題的其他答案),我使用您建議的dict [key]方法爲每個字段使用了一個解析器。從我剛剛運行的幾個測試中,似乎兩種方法都會在相同的時間內返回數​​據。我不知道從GraphQL返回的時間有點慢是正常的(1000條記錄〜高速緩存後3-4秒;在cachine之前,相同數據集約需6秒)。 –

+0

如果使用開發版本,查詢速度至少快10倍(300ms?)。 你可以用'pip install graphene-django> = 1.0.dev'安裝它。 希望這有助於! –

0

這裏是臨時的解決方法,但我希望有一些清潔處理snake_cased字段名。

項目/主/ 應用程序/ schema.py上GraphQL的Python /石墨烯

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() 
+0

應該指出的是,擁有所有這些解析器會使GraphQL變得非常慢。 –