Модель ndb - получить упорядоченный набор имен свойств

Меня часто просят экспортировать данные, хранящиеся в моделях 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 упорядочено по алфавиту.

2 ответа

Решение

Насколько мне известно, это невозможно без разбора источника для себя. К счастью, с менталитетом питона "включенные батареи" это не так уж сложно. Ты можешь использовать 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.Modelс _properties атрибут, но эту информацию, вероятно, можно почерпнуть из dir или же inspect.getmembers так order_properties может быть изменен так, чтобы он работал полностью в целом.

При использовании google.appengine.ext.db:

db.Property экземпляры имеют атрибут с именем creation_counter, который увеличивается каждый раз, когда создается новый экземпляр. Таким образом, чтобы получить список свойств, отсортированных в порядке объявления, вы можете сделать что-то вроде:

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

(где Foo является примером db.Model)

При использовании google.appengine.ext.ndb:

То же самое, кроме ndb.Property атрибут называется _creation_counterтак что эквивалентный код будет:

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

(где Foo является примером ndb.Model)

Я просто придумываю это, возможно, она полна ошибок, но, надеюсь, она направит вас по правильному пути:

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()
Другие вопросы по тегам