Управление версиями SQLAlchemy заботится о порядке импорта классов

| | | | | | | | | |

Я следовал руководству здесь:

http ://www.sqlalchemy.org/docs/orm/examples.html?highlight=versioning#versioned-objects

и столкнулись с проблемой. Я определил свои отношения следующим образом:

generic_ticker = ratio("MyClass", backref=backref("stuffs")) 

со строками, поэтому он не "не заботится о порядке импорта модулей моей модели. Все это нормально работает нормально, но когда я использую метаданные управления версиями, я получаю следующую ошибку:

sqlalchemy.exc.InvalidRequestError: При инициализации картографа Mapper|MyClass|stuffs, выражению "Trader" не удалось найти имя ("имя "MyClass" не определено"). Если это имя класса, рассмотрите возможность добавления этого отношения () в класс после определения обоих зависимых классов .

Я отследил ошибку до:

 Файл "/home/nick/workspace/gm3/gm3/lib/history_meta.py", строка 90, в __init__ mapper = class_mapper(cls) File "/home/nick/venv/tg2env/lib/python2.6/site-packages/sqlalchemy/orm/util.py", строка 622, в class_mapper mapper = mapper.compile () 

класс VersionedMeta(DeclarativeMeta): def __init__(cls, cl assname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) try: mapper = class_mapper(cls) _history_mapper(mapper) за исключением UnmappedClassError: pass 

Я исправил проблему поместив try: кроме материала в лямбду и запустив их все после того, как все импорты произошли. Это работает, но кажется немного ерундой, есть идеи, как исправить это лучше?

Спасибо!

Обновить

Проблема на самом деле не в заказе на импорт. Пример управления версиями разработан таким образом, что картограф требует компиляции в конструкторе каждого класса версии. И компиляция завершается ошибкой, когда связанные классы еще не определены. В случае циклических отношений невозможно заставить его работать, изменив порядок определения сопоставленных классов.

Обновление 2

Как указано в приведенном выше обновлении ( Я не знал, что здесь можно редактировать посты других людей :)) скорее всего, это связано с циклическими ссылками. В этом случае может быть кому-то пригодится мой хак (я использую его с турборедуктором) (замените VersionedMeta и добавьте в create_mappers global в history_meta)

create_mappers = [] class VersionedMeta(DeclarativeMeta) : def __init__(cls, classname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) #Я добавил этот код, поскольку он давал сбой, иначе def make_mapper(): try: mapper = class_mapper(cls) _history_mapper (mapper), кроме UnmappedClassError: pass create_mappers.append(lambda: make_mapper()) 

Затем вы можете сделать что-то вроде следующего в своих моделях __init__.py

# Импортируйте модули вашей модели сюда from myproj.lib.history_meta import create_mappers from myproj.model.misc import * from myproj.model.actor import * from myproj.model.stuff1 import * from myproj.model.instrument import * from myproj.model.stuff import * #setup the history [func() for func in create_mappers] 

Таким образом создаются только преобразователи после того, как все классы были определены.

Обновление 3 Немного не связано, но в некоторых случаях я столкнулся с ошибкой дублирования первичного ключа (фиксация 2 изменений в одном и том же объекте за один раз) . Мой обходной путь состоял в том, чтобы добавить новый первичный автоматически увеличивающийся ключ. Конечно, вы не можете иметь более 1 с mysql, поэтому мне пришлось отменить первичный ключ существующего материала, используемого для создания таблицы истории. Проверьте мой общий код (включая hist_id и избавление от ограничения внешнего ключа):

"""Украдено из официальных рецептов sqlalchemy """ из sqlalchemy.ext.declarative import DeclarativeMeta из sqlalchemy.orm import mapper, class_mapper, attribute, object_mapper из sqlalchemy.orm.exc import UnmappedClassError, UnmappedColumnError из sqlalchemy. : for fk in col.foreign_keys: if fk.references(table): return True return False def _history_mapper(local_mapper): cls = local_mapper.class_ # установить флаг "active_history" # на сопоставлении столбцов a ttributes, чтобы старая версия # информации всегда загружалась (в настоящее время устанавливает ее для всех атрибутов) для prop в local_mapper.iterate_properties: getattr(local_mapper.class_, prop.key).impl.active_history = True super_mapper = local_mapper.inherits super_history_mapper = getattr(cls, "__history_mapper__", None) polymorphic_on = None super_fks = [] если не super_mapper или local_mapper.local_table не является super_mapper.local_table: cols = [] для столбца в local_mapper.local_table.c: if column.name == «версия»: continue col = column.copy() col.unique = False # не следует автоматически увеличивать материал из обычной базы данных, если col.autoincrement: col.autoincrement = False #sqllite падает с ключами автоматического увеличения, если у нас есть составной ключ, если col.primary_key: col.primary_key = False, если super_mapper и col_references_table(column, super_mapper.local_table): super_fks.append((col.key, list(super_history_mapper.base_mapper.local_table.primary_key)[0])) cols. добавить (столбец), если столбец local_mapper.polymorphi c_on: polymorphic_on = col #if super_mapper: # super_fks.append(("версия", super_history_mapper.base_mapper.local_table.c.version)) cols.append(Column("hist_id", Integer, primary_key=True, autoincrement=True) ) cols.append(Column("version", Integer)) cols.append(Column("changed", DateTime, default=datetime.datetime.now)) if super_fks: cols.append(ForeignKeyConstraint(*zip(*super_fks) )) table = Table(local_mapper.local_table.name + "_history", local_mapper.local_table.metadata, *cols, mysql_engine="InnoDB") else: # наследование одной таблицы. возьмите любые дополнительные столбцы, которые могли # быть добавлены, и добавьте их в таблицу истории. для столбца в local_mapper.local_table.c: если column.key отсутствует в super_history_mapper.local_table.c: col = column.copy() super_history_mapper.local_table.append_column(col) table = None, если super_history_mapper: bases = (super_history_mapper.class_,) еще: bases = local_mapper.base_mapper.class_.__bases__ versioned_cls = type.__new__(type, "%sHistory" % cls.__name__, bases, {}) m = mapper( versioned_cls, table, inherits=super_history_mapper, polymorphic_on=polymorphic_on, polymorphic_identity =local_mapper.polymorphic_identity ) cls.__history_mapper__ = m, если не super_history_mapper: cls.version = Column("version", Integer, default=1, nullable=False) create_mappers = [] class VersionedMeta(DeclarativeMeta): def __init__(cls, classname , bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) #Я добавил этот код, так как он давал сбой, иначе def make_mapper(): try: mapper = class_mapper(cls) _history_mapper(mapper) кроме UnmappedClassError: pass create_mappers.append (лямбда: make_mapper()) def versioned_objects(iter): для объектов в iter: if hasattr(obj, "__history_mapper__"): yield obj def create_version(obj, session, delete = False): obj_mapper = object_mapper(obj) history_mapper = obj.__history_mapper__ history_cls = history_mapper.class_ obj_state = attribute.instance_state (obj) attr = {} obj_changed = False для om, hm в zip (obj_mapper.iterate_to_root(), history_mapper.iterate_to_root()): если hm.single: продолжить для hist_col в hm .local_table.c: если hist_col.key == "версия" или hist_col.key == "изменено" или hist_col.key == "hist_id": continue obj_col = om.local_table.c[hist_col.key] # получить значение атрибута # на основе свойства MapperProperty, связанного с сопоставленным столбцом #. это позволит использовать MapperProperties # с именем ключа, отличным от имени сопоставленного столбца. try: prop = obj_mapper.get_property_by_column(obj_col) кроме UnmappedColumnError: # в случае наследования одной таблицы в сопоставленной таблице могут быть # столбцы, предназначенные только для подкласса. # Статус "несопоставленный" столбца подкласса в # базовом классе является функцией декларативного модуля, начиная с sqla 0.5.2. continue # Атрибуты объектов с истекшим сроком действия, а также отложенные столбцы могут отсутствовать # в словаре. заставить его загружаться, несмотря ни на что, используя getattr(). если prop.key отсутствует в obj_state.dict: getattr(obj, prop.key) a, u, d = attribute.get_history(obj, prop.key) if d: attr[hist_col.key] = d[0] obj_changed = True elif u: attr[hist_col.key] = u[0] else: # если атрибут не имеет значения. attr[hist_col.key] = a[0] obj_changed = True, если не obj_changed: # не изменено, но отношения есть. ОК # проверьте и их на наличие реквизита в obj_mapper.iterate_properties: if isinstance(prop, RelationshipProperty) and attribute.get_history(obj, prop.key).has_changes(): obj_changed = True break, если не obj_changed и не удален: return attr[" версия"] = obj.version hist = history_cls() для ключа, значение в attr.iteritems(): setattr(hist, key, value) obj.version += 1 класс session.add(hist) VersionedListener(SessionExtension): def before_flush(self, session, flush_context, instances): для obj в versioned_objects(session.dirty): create_version(obj, session) для obj в versioned_objects(session.deleted): create_version(obj, session, delete = True) 



                   

Shop

Learn programming in R: courses

$

Best Python online courses for 2022

$

Best laptop for Fortnite

$

Best laptop for Excel

$

Best laptop for Solidworks

$

Best laptop for Roblox

$

Best computer for crypto mining

$

Best laptop for Sims 4

$

Latest questions

NUMPYNUMPY

psycopg2: insert multiple rows with one query

12 answers

NUMPYNUMPY

How to convert Nonetype to int or string?

12 answers

NUMPYNUMPY

How to specify multiple return types using type-hints

12 answers

NUMPYNUMPY

Javascript Error: IPython is not defined in JupyterLab

12 answers

News


Wiki

Python OpenCV | cv2.putText () method

numpy.arctan2 () in Python

Python | os.path.realpath () method

Python OpenCV | cv2.circle () method

Python OpenCV cv2.cvtColor () method

Python - Move item to the end of the list

time.perf_counter () function in Python

Check if one list is a subset of another in Python

Python os.path.join () method