【问题标题】:Create REST API with Neo4J and Django使用 Neo4J 和 Django 创建 REST API
【发布时间】:2013-06-04 14:15:48
【问题描述】:

我正在尝试在后端使用 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)),

models.py 文件:

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

【问题讨论】:

  • “我不能使用像 Tastypie 或 Piston 这样通常将模型序列化为 JSON(或 XML)的框架”。当你说你不能使用这些框架时,为什么不呢?你遇到什么问题? (我不熟悉 Neo4J,但您的问题看起来需要更多细节。)
  • 我创建的模型基于 Neo4Django 框架,该框架依赖于 python 的 neo4j REST 客户端。因此,当对数据库进行查询时,我通常得到的是 JSON ! (见docs.neo4j.org/chunked/milestone/rest-api-transactional.html
  • 为什么不直接调用 Neo4j rest api 呢?在我看来,无论如何您都在尝试充当对该 api 的调用的包装器(如果我错了,请纠正我),因此您应该尝试返回从 neo4j 获得的 JSON。
  • 是的,对 neo4j 的一些查询是通过 REST API 进行的。实际上,我正在考虑这些框架,因为 AFAIK,它们允许创建结构化的 REST API(不是吗?),所以我不必自己做这项工作

标签: django api rest neo4j neo4django


【解决方案1】:

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

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

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

【讨论】:

  • 关于 Neo4j REST API 返回的 JSON,我正在考虑仅检索包含数据的部分。使用框架来创建类似于 Django 的 ORM 中现有的模型也是如此,允许构建应用程序。但是当我们想要执行大查询时(与 Core API 相比,Rest API 的性能),它就成了一个障碍,所以我打算在我的应用程序中同时使用它们(通过服务器插件)。
  • 我的经验是 Neo4django 比 Neomodel 得到更好的支持。首席开发人员非常乐于助人。只是我的 2c。
  • Neomodel 是一个年轻得多的项目,但 Matt (neo4django) 和 Rob (neomodel) 都是很棒的人,根据我的经验,他们都非常乐于助人并致力于 Neo4j 社区。 Python + Neo4j = 赢 :-)
【解决方案2】:

Django-Tastypie 允许使用 NoSQL 数据库创建 REST API,正如http://django-tastypie.readthedocs.org/en/latest/non_orm_data_sources.html 中提到的那样。

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

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

感谢您的所有回复:)

【讨论】:

  • 嗨!你有这方面的例子吗?我和你的情况完全一样:)
  • (注意:我成功使它与 ModelResource 一起工作,但过滤器失败)
  • 嗨,我会尽快发布完整的答案(抱歉回复晚了,这几天有点忙:))
  • @Pirhoo 我在下面发布了完整的答案(抱歉迟到了)
【解决方案3】:

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

编辑:

我刚刚浏览了 sweetpie 教程,posted a gist 给出了示例。我注意到嵌套资源有点有趣,但除此之外效果很好。我很确定为支持此支持提供补丁的绅士们也知道如何处理嵌套资源 - 我会请他们说出来。

编辑:

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

【讨论】:

  • 谢谢,但功劳绝对归功于 Lukas (@lutoma) 和 GitHub 上的 @d9puces :)
  • 它确实非常好,但由于某种原因它对我来说崩溃了。也许它需要一些更新?
【解决方案4】:

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

假设我想创建一个带有一些属性的机场资源。我将在 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_listobj_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

希望这会有所帮助:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-15
    • 2021-07-28
    • 2013-04-28
    • 1970-01-01
    • 2018-08-20
    • 1970-01-01
    • 2014-02-14
    • 1970-01-01
    相关资源
    最近更新 更多