Wersjonowanie SQLAlchemy dba o kolejność importu klas

| | | | | | | | | |

Podążałem za przewodnikiem tutaj:

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

i napotkaliśmy problem. Zdefiniowałem swoje relacje w następujący sposób:

generic_ticker = relations("MojaKlasa", backref=backref("stuffs")) 

z ciągami, więc tak się nie dzieje Nie dbam o kolejność importowania moich modułów modelu. Wszystko to działa normalnie, ale kiedy używam meta wersjonowania, pojawia się następujący błąd:

sqlalchemy.exc.InvalidRequestError: Podczas inicjowania programu mapującego Mapper|MyClass|stuffs, wyrażenie „Trader” nie zlokalizowało nazwy („nazwa „MyClass” nie jest zdefiniowana”). Jeśli jest to nazwa klasy, rozważ dodanie tej relacji() do klasy po zdefiniowaniu obu klas zależnych .

Znalazłem błąd w:

 Plik "/home/nick/workspace/gm3/gm3/lib/history_meta.py", wiersz 90, w __init__ mapper = class_mapper(cls) File "/home/nick/venv/tg2env/lib/python2.6/site-packages/sqlalchemy/orm/util.py", wiersz 622, w class_mapper mapper = mapper.compile () 

class VersionedMeta(DeclarativeMeta): def __init__(cls, cl assname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) try: mapper = class_mapper(cls) _history_mapper(mapper) z wyjątkiem UnmappedClassError: pass 

Rozwiązałem problem próbując: z wyjątkiem elementów lambda i uruchamiając je wszystkie po zakończeniu wszystkich importów. To działa, ale wydaje się trochę bzdury, czy jakieś pomysły, jak to naprawić, to lepszy sposób?

Dzięki!

Aktualizacja

Właściwie problem nie dotyczy zamówienia importu. Przykład wersjonowania został zaprojektowany w taki sposób, że program mapujący wymaga kompilacji w costructor każdej klasy wersjonowanej. A kompilacja kończy się niepowodzeniem, gdy powiązane klasy nie są jeszcze zdefiniowane. W przypadku relacji kołowych nie ma sposobu, aby działały one poprzez zmianę kolejności definicji mapowanych klas.

Aktualizacja 2

Jak stwierdzono w powyższej aktualizacji ( Nie wiedziałem, że możesz edytować posty innych osób tutaj :)) jest to prawdopodobnie spowodowane cyklicznymi odniesieniami. W takim przypadku może komuś przyda się mój hack (używam go z turboprzekładniami) (Zastąp VersionedMeta i dodaj create_mappers global w history_meta)

create_mappers = [] class VersionedMeta(DeclarativeMeta) : def __init__(cls, classname, bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) #Dodałem ten kod, ponieważ się zawieszał, w przeciwnym razie def make_mapper(): try: mapper = class_mapper(cls) _history_mapper (mapper) z wyjątkiem UnmappedClassError: pass create_mappers.append(lambda: make_mapper()) 

Wtedy możesz zrobić coś takiego w swoich modelach __init__.py

< kod># Importuj swoje moduły modelu tutaj z myproj.lib.history_meta import create_mappers z myproj.model.misc import * z myproj.model.actor import * z myproj.model.stuff1 import * z myproj.model.instrument import * z myproj.model.stuff import * #ustaw historię [func() for func in create_mappers] 

W ten sposób tworzy tylko mapery po zdefiniowaniu wszystkich klas.

Aktualizacja 3 Nieco niezwiązane, ale w niektórych okolicznościach natknąłem się na błąd zduplikowanego klucza podstawowego (za jednym razem 2 zmiany w tym samym obiekcie) . Moje obejście polegało na dodaniu nowego podstawowego klucza automatycznej inkrementacji. Oczywiście nie możesz mieć więcej niż 1 z mysql, więc musiałem usunąć klucz podstawowy z istniejących elementów użytych do stworzenia tabeli historii. Sprawdź mój ogólny kod (w tym hist_id i pozbycie się ograniczenia klucza obcego):

"""Skradzione z oficjalnych receptur sqlalchemy """ z sqlalchemy.ext.declarative import DeclarativeMeta z sqlalchemy.orm import mapper, class_mapper, attribute, object_mapper z sqlalchemy.orm.exc import UnmappedClassError, UnmappedColumnError z sqlalchemy importuj tabelę, kolumnę, ForeignKeyConstraint, Integer z sqlalchemy.orm.interfaces import SessionExtension z sqlalchemy.orm.properties import RelationshipProperty z sqlalchemy.types import DateTime import datetime z sqlalchemy.orm.session(kola_referencyjna tabela def) : for fk in col.foreign_keys: if fk.references(table): return True return False def _history_mapper(local_mapper): cls = local_mapper.class_ # ustaw flagę "active_history" # na kolumnę zmapowaną atrybuty, dzięki czemu zawsze ładowana jest stara wersja # informacji (obecnie ustawia ją na wszystkie atrybuty) dla właściwości w 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 = [] jeśli nie super_mapper lub local_mapper.local_table nie jest super_mapper.local_table: cols = [] dla kolumny w local_mapper.local_table.c: if column.name == "version": continue col = column.copy() col.unique = False #don"t auto inkrementacji rzeczy z normalnej bazy danych if col.autoincrement: col.autoincrement = False #sqllite przewraca się z kluczami auto inkrementacji, jeśli mamy klucz złożony, jeśli col.primary_key: col.primary_key = False, jeśli super_mapper i 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 append(col), jeśli kolumna jest local_mapper.polymorphi c_on: polymorphic_on = col #if super_mapper: # super_fks.append(("wersja", super_history_mapper.base_mapper.local_table.c.version)) cols.append(Column("hist_id", Integer, primary_key=True, autoincrement=True) ) cols.append(Column("wersja", Integer)) cols.append(Column("zmieniony", 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: # dziedziczenie pojedynczej tabeli. weź wszystkie dodatkowe kolumny, które mogły # zostać dodane, i dodaj je do tabeli historii. dla kolumny w local_mapper.local_table.c: jeśli column.key nie ma w super_history_mapper.local_table.c: col = column.copy() super_history_mapper.local_table.append_column(col) table = Brak jeśli super_history_mapper: bases = (super_history_mapper.class_,) else: 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=polimorphic_identity, =local_mapper.polymorphic_identity) cls.__history_mapper__ = m, jeśli nie super_history_mapper: cls.version = Column("wersja", Integer, default=1, nullable=False) create_mappers = [] class VersionedMeta(DeclarativeMeta): def __init__(cls, classname , bases, dict_): DeclarativeMeta.__init__(cls, classname, bases, dict_) #Dodałem ten kod, ponieważ ulegał awarii w przeciwnym razie def make_mapper(): try: mapper = class_mapper(cls) _history_mapper(mapper) z wyjątkiem UnmappedClassError: pass create_mappers.append(lambda: make_mapper()) def versioned_objects(iter): for obj w 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 = atrybuty.instance_state(obj) attr = {} obj_changed = False dla om, hm w zip(obj_mapper.iterate_to_root(), history_mapper.iterate_to_root()): jeśli hm.single: kontynuuj dla hm_col w .local_table.c: if hist_col.key == "version" or hist_col.key == "zmieniony" lub hist_col.key == "hist_id": kontynuuj obj_col = om.local_table.c[hist_col.key] # pobierz wartość atrybutu # na podstawie MapperProperty powiązanej z zamapowaną kolumną #. pozwoli to na użycie MapperProperties #, które mają inną nazwę klucza niż nazwa zmapowanej kolumny. try: prop = obj_mapper.get_property_by_column(obj_col) z wyjątkiem UnmappedColumnError: # w przypadku dziedziczenia pojedynczej tabeli, mogą istnieć # kolumny w mapowanej tabeli przeznaczonej tylko dla podklasy. # status "niezmapowany" kolumny podklasy w # klasie bazowej jest cechą modułu deklaratywnego od sqla 0.5.2. kontynuuj # wygasłe atrybuty obiektu, a także odroczone kolumny mogą nie znajdować się w # dyktaturze. zmusić go do załadowania bez względu na wszystko, używając getattr(). if prop.key nie w 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: # jeśli atrybut nie ma wartości. attr[hist_col.key] = a[0] obj_changed = Prawda, jeśli nie obj_changed: # niezmienione, ale mamy relacje. OK # sprawdź je również pod kątem właściwości w obj_mapper.iterate_properties: if isinstance(prop, RelationshipProperty) i attribute.get_history(obj, prop.key).has_changes(): obj_changed = True break, jeśli nie obj_changed i nie usunięto: return attr[" version"] = obj.version hist = history_cls() dla klucza, wartość w attr.iteritems(): setattr(hist, klucz, wartość) obj.version += 1 session.add(hist) class VersionedListener(SessionExtension): def before_flush(self, session, flush_context, instances): for obj in versioned_objects(session.dirty): create_version(obj, session) for obj in 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