8

我正在尝试在后端使用 Neo4j 和 Django 创建一个 REST API。

问题是,即使我有使用 Neo4Django 的 Django 模型,我也不能使用像 Tastypie 或 Piston 这样通常将模型序列化为 JSON(或 XML)的框架。

抱歉,如果我的问题令人困惑或不清楚,我是网络服务的新手。

谢谢你的帮助


编辑:所以我从 Tastypie 开始,并按照此页面上的教程进行操作http://django-tastypie.readthedocs.org/en/latest/tutorial.html。我正在寻找在浏览器中显示 Neo4j JSON 响应,但是当我尝试访问时,http://127.0.0.1:8000/api/node/?format=json我得到了这个错误:

{"error_message": "'NoneType' object is not callable", "traceback": "Traceback (most recent call last):\n\n  File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 217, in wrapper\n    response = callback(request, *args, **kwargs)\n\n  File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 459, in dispatch_list\n    return self.dispatch('list', request, **kwargs)\n\n  File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 491, in dispatch\n    response = method(request, **kwargs)\n\n  File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 1298, in get_list\n    base_bundle = self.build_bundle(request=request)\n\n  File \"/usr/local/lib/python2.6/dist-packages/tastypie/resources.py\", line 718, in build_bundle\n    obj = self._meta.object_class()\n\nTypeError: 'NoneType' object is not callable\n"}

这是我的代码:

api.py 文件:

class NodeResource (ModelResource): #it doesn't work with Resource neither
    class meta:
        queryset= Node.objects.all()
        resource_name = 'node'

urls.py 文件:

node_resource= NodeResource()

urlpatterns = patterns('',
    url(r'^api/', include(node_resource.urls)),

模型.py 文件:

class Node(models.NodeModel):
    p1 = models.StringProperty()
    p2 = models.StringProperty()
4

4 回答 4

6

我建议不要直接通过您的应用程序传递 Neo4j REST API 响应。随着这些数据格式的演变和弃用(它们确实如此),您不仅无法控制这些数据格式的结构,而且还会暴露数据库层的不必要内部结构。

除了 Neo4Django,您可能还需要考虑其他几个选项。Neomodel是另一个为 Django 设计的模型层,其作用类似于内置的 ORM;您还可以选择 py2neo 提供的原始OGM 层,这可能会有所帮助,但不是特定于 Django 的。

值得记住的是,Django 及其插件是围绕传统的 RDBMS 设计的,而不是图形数据库,因此这些解决方案都不会是完美的。无论您选择什么,您都可能需要进行大量的转换工作来创建应用程序的 API。

于 2013-06-04T16:16:06.227 回答
4

Django-Tastypie 允许使用 NoSQL 数据库创建 REST API,如http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html中所述。

原则是使用tastypie.resources.Resource而不是tastypie.resources.ModelResource特定于 RDBMS,然后必须重新定义主要功能才能提供具有所需参数的 JSON。

因此,我采用了链接中给出的示例,对其进行了修改并使用 Neo4j REST Client for Python 来获取数据库的实例并执行请求,它就像一个魅力。

感谢您的所有回复:)

于 2013-06-05T14:00:25.050 回答
2

感谢最近的 贡献,Neo4django 现在支持 Tastypie 开箱即用!如果您尝试一下,我很想知道您的想法。

编辑:

我刚刚浏览了美味派教程,并发布了一个带有结果示例的要点。我注意到嵌套资源有点有趣,但除此之外效果很好。我很确定为支持此支持提供补丁的绅士们也知道如何处理嵌套资源 - 我会请他们说出来。

编辑:

只要在 中指定了关系ModelResource,它们就可以很好地工作。如果有人想看例子,请告诉我。

于 2013-06-15T20:01:40.790 回答
2

好吧,我的回答有点含糊,所以我将发布如何用一些代码解决问题:

假设我想创建一个具有某些属性的机场资源。我将在 3 个不同的文件中构建它(出于可读性原因)。

  • 第一的 :airport.py

该文件将包含所有资源属性和一个构造函数:

from models import *

class Airport(object):

    def __init__ (self, iata, icao, name, asciiName, geonamesId, wikipedia, id, latitude, longitude):
        self.icao = icao
        self.iata = iata
        self.name = name
        self.geonamesId = geonamesId
        self.wikipedia = wikipedia
        self.id = id
        self.latitude = latitude
        self.longitude = longitude
        self.asciiName = asciiName

该文件将用于创建资源。


  • 然后是第二个文件::AirportResource.py这个文件将包含资源属性和一些基本方法,具体取决于我们希望资源处理的请求。

    class AirportResource(Resource):
    
        iata = fields.CharField(attribute='iata')
        icao = fields.CharField(attribute='icao')
        name = fields.CharField(attribute='name')
        asciiName = fields.CharField(attribute='asciiName')
        latitude = fields.FloatField(attribute='latitude')
        longitude = fields.FloatField(attribute='longitude')
        wikipedia= fields.CharField(attribute='wikipedia')
        geonamesId= fields.IntegerField(attribute='geonamesId')
    
        class Meta:
            resource_name = 'airport'
            object_class = Airport 
            allowed_methods=['get', 'put'] 
            collection_name = 'airports'
            detail_uri_name = 'id'
    
        def detail_uri_kwargs(self, bundle_or_obj):
            kwargs = {}
            if isinstance(bundle_or_obj, Bundle):
                kwargs['id'] = bundle_or_obj.obj.id
            else:
                kwargs['id'] = bundle_or_obj.id
    
            return kwargs
    

如文档中所述,如果我们想创建一个处理 CREATE、GET、PUT、POST 和 DELETE 请求的 API,我们必须覆盖/实现以下方法:

def obj_get_list(self, bundle, **kwargs): 获取对象列表

def obj_get(self, bundle, **kwargs): 获取单个对象

def obj_create(self, bundle, **kwargs)创建对象(CREATE 方法)

def obj_update(self, bundle, **kwargs)更新对象(PUT 方法)

def obj_delete(self, bundle, **kwargs)删除对象(DELETE 方法)

(见http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html

通常,在ModelResource所有这些方法中都已定义和实现,因此可以毫无困难地直接使用它们。但是在这种情况下,它们应该根据我们想要做的来定制。

让我们看一个实现obj_get_listand的例子obj_get

对于 obj_get_list:

ModelResource中,数据首先从数据库中获取,然后可以根据 META 类中声明的过滤器进行过滤(参见http://django-tastypie.readthedocs.org/en/latest/interacting.html)。但我不希望实现这样的行为(获取所有内容然后过滤),所以我根据查询字符串参数对 Neo4j 进行了查询:

def obj_get_list(self,bundle, **kwargs):
    data=[]
    params= []
    for key in bundle.request.GET.iterkeys():
        params.append(key)
        if "search" in params :
            query= bundle.request.GET['search']
            try:
                results = manager.searchAirport(query) 
                data = createAirportResources(results)
            except Exception as e:
                raise NotFound(e)
            else:
                raise BadRequest("Non valid URL")
    return data

对于 obj_get:

def obj_get(self, bundle, **kwargs):
    id= kwargs['id'] 
    try :
        airportNode = manager.getAirportNode(id)
        airport = createAirportResources([airportNode])
        return airport[0]
    except Exception as e : 
        raise NotFound(e)

最后是一个通用函数,它以节点列表为参数并返回 Airport 对象列表:

def createAirportResources(nodes):
    data= []
    for node in nodes:
        iata = node.properties['iata']
        icao = node.properties['icao']
        name = node.properties['name']       
        asciiName = node.properties['asciiName']
        geonamesId = node.properties['geonamesId']
        wikipedia = node.properties['wikipedia']
        id = node.id
        latitude = node.properties['latitude']
        longitude = node.properties['longitude']
        airport = Airport(iata, icao, name, asciiName, geonamesId, wikipedia, id, latitude, longitude)
        data.append(airport)
    return data

  • 现在是第三个manager.py:负责对数据库进行查询并返回结果:

首先,我使用neo4j rest client框架获得了一个数据库实例:

from neo4jrestclient.client import *
gdb= GraphDatabase("http://localhost:7474/db/data/")

然后是获取机场节点的函数:

def getAirportNode(id):
    if(getNodeType(id) == type):
        n= gdb.nodes.get(id)
        return n
    else:
        raise Exception("This airport doesn't exist in the database")

以及执行搜索的那个(我正在使用服务器插件,有关更多详细信息,请参阅 Neo4j 文档):

def searchAirport(query):
    airports= gdb.extensions.Search.search(query=query.strip(), searchType='airports', max=6)
    if len(airports) == 0:
        raise Exception('No airports match your query')
    else:
        return results

希望这会有所帮助:)

于 2013-08-07T15:45:29.450 回答