【问题标题】:ndb Model - retrieve ordered set of property namesndb 模型 - 检索有序的属性名称集
【发布时间】:2013-12-04 16:47:39
【问题描述】:

我经常被要求将存储在 NDB 模型中的数据导出到 csv。为此,我通常最终会编写这样的模型:

from google.appengine.ext import ndb

class Foo(ndb.Model):
    monty = ndb.StringProperty()
    python = ndb.StringProperty()

    @property
    @classmethod
    def fieldnames(cls):
        return ['monty', 'python']

并且在导出模块中类似于

# pseudocode ...

query = Foo.gql('where monty = :1', 'bunny')
data = [littlefoo._to_dict() for littlefoo in query]
fieldnames = Foo.fieldnames
with open('outfile.csv', 'w') as f:
writer = csv.DictWriter(f, fieldnames, dialect=dialect)
writer.writerows(data)

请注意,需要使用 fieldnames 方法来确定输出 csv 中字段的顺序。这种方法的问题在于,对于任何具有大量属性的模型,添加fieldnames 方法都是丑陋的重复工作。理想情况下,我想在声明它们时简单地对属性进行排序,并以相同的顺序检索它们以获取fieldnames。有没有办法按顺序检索这些属性? Foo._properties 按字母顺序排列。

【问题讨论】:

  • 我也想做同样的事情,并想出了一个比公认答案更简单的解决方案。以防万一你仍然感兴趣,看看我的回答:stackoverflow.com/a/54744631/1965404

标签: python google-app-engine csv app-engine-ndb


【解决方案1】:

据我所知,如果不自己解析源代码,这是不可能的。幸运的是,以 python 的“包括电池”的心态,这并不太难。您可以使用inspect 获取源代码,然后您可以使用ast 解析源并订购东西:

import ast
import inspect

class NodeTagger(ast.NodeVisitor):
    def __init__(self):
        self.class_attribute_names = {}

    def visit_Assign(self, node):
        for target in node.targets:
            self.class_attribute_names[target.id] = target.lineno

    # Don't visit Assign nodes inside Function Definitions.
    def visit_FunctionDef(self, unused_node):
        return None

def order_properties(model):
    properties = model._properties
    source = inspect.getsource(model)
    tree = ast.parse(source)
    visitor = NodeTagger()
    visitor.visit(tree)
    attributes = visitor.class_attribute_names
    model._ordered_property_list = sorted(properties, key=lambda x:attributes[x])
    return model


@order_properties
class Foo(object):
    c = 1
    b = 2
    a = 3

    # Add a _properties member to simulate an `ndb.Model`.
    _properties = {'a': object, 'b': object, 'c': object}

print Foo._ordered_property_list

请注意,这里的方法几乎是通用的。我知道ndb.Models 有一个_properties 属性,但该信息可能是从dirinspect.getmembers 收集到的,因此可以修改order_properties,使其完全正常工作。

【讨论】:

  • 这太棒了。非常感谢!我可能想到了这种方法,但我当然会认为它太复杂而无法实施。很高兴你向我展示了其他方式。
  • @Alice -- 最近我一直在玩ast。尽管文档有些不透明,但它非常有用且易于使用。
  • 这个答案很酷。遗憾的是 ndb 在其原始实现中没有为 _properties 使用 OrderedDict。我将一些表单从架构中删除,并且一直在存储名称(属性)列表,因此我可以明确地了解表单中的顺序,并删除表单中不需要的属性,例如 date_created。
  • @TimHoffman -- 这不会有任何好处。 _properties 几乎肯定是通过ndb.Model__metaclass__ 获取的。由于它通常必须遵守 python 元类的规则,因此类命名空间作为常规 dict 传递。也就是说,这是python中的一个限制,不是ndb的实现。当然,当 guido 对其进行编程时,我认为他基本上可以完成我刚刚在元类中所做的事情——但是增加的代码复杂性可能不值得增加开销和最小的收益。
  • 创意方法+1!但是对于这个特殊问题有一个更简单的解决方案:stackoverflow.com/a/54744631/1965404
【解决方案2】:

我只是在编造这个,它可能充满了错误,但希望它能让你走上正确的道路:

from google.appengine.ext.ndb import Property

def get_field_names(ndbmodel):
    result = []
    all_fields = dir(ndbmodel)
    for field in all_fields:
        if isinstance(getattr(ndbmodel, field), Property):
            result.append(field)
    return result.sort()

【讨论】:

  • dir 按字母顺序返回结果,type(...) == Property 将不起作用。通常您使用的是Property 的子类(例如StringProperty)。
  • 糟糕,误读了这个问题,我以为它要求排序。应该是 isinstance() 而不是 type()。
  • 是的,这个按字母顺序很好用,只是不是我想要的:) 谢谢。
【解决方案3】:

如果使用google.appengine.ext.db

db.Property 实例有一个名为creation_counter 的属性,每次创建新实例时该属性都会递增。因此,要获取按声明顺序排序的属性列表,您可以执行以下操作:

sorted(Foo.properties().items(), key=lambda np: np[1].creation_counter)

(其中Foodb.Model 的一个实例)

如果使用google.appengine.ext.ndb

同样的事情,除了 ndb.Property 属性被称为 _creation_counter,所以等效代码是:

sorted(Foo._properties.items(), key=lambda np: np[1]._creation_counter)

(其中Foondb.Model 的一个实例)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-26
    • 1970-01-01
    • 1970-01-01
    • 2015-02-19
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多