Модель 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()