[PYTHON] Kombination von polymorpher Vererbung und vielen zu vielen mit SQLAlchemy

Hintergrund

Es gibt eine Tabelle mit einer ähnlichen Struktur, und ich habe sie durch Verwendung von Polymorphismus = Polymorphismus zu einer DRY-Struktur gemacht. Aus jeder Tabelle wurde es notwendig, eine Viele-zu-Viele-Beziehung zu derselben separaten Tabelle zu haben.

Rückblick 1

SQL Alchemy kann abhängig von der Tabellenstruktur drei Arten von Polymorphismus verwenden. Die Anzahl der Tabellen unter der Annahme, dass es eine Basisklasse und zwei abgeleitete Klassen gibt, wird ebenfalls beschrieben.

Aufgrund der externen Datenbank usw. haben wir diesmal die Vererbung von Betontabellen übernommen.

Rückblick 2

SQL Alchemy kann viele zu viele verarbeiten, ohne die Zwischentabellen zu kennen. Zitiert aus dem Beispiel im offiziellen Handbuch. Ein Muster, in dem ein Blog-Artikel mehrere Schlüsselwörter enthält.

post.keywords.append(Keyword('wendy'))

Also, wie machst du das?

Angenommen, jedes Formular mit ähnlicher Konfiguration verfügt über mehrere Tags. Es gibt vier Tabellen: Tags, jede Form x 2, und eine Zwischentabelle.


# -*- coding: utf-8 -*-
from sqlalchemy import *
from sqlalchemy.ext.declarative import *
from sqlalchemy.orm import *
from sqlalchemy_utils import *

base = declarative_base()

#Die Zwischentabelle muss keine Klasse sein.
assoc = Table('assoc', base.metadata,
  Column('aform_id', Integer, ForeignKey('AForm.id')),
  Column('bform_id', Integer, ForeignKey('BForm.id')),
  Column('tag_id', Integer, ForeignKey('Tag.id'))
)

class Tag(base):
  __tablename__ = 'Tag'
  id = Column(Integer, primary_key = True)
  name = Column(String)
  #Auch wenn Sie es nicht direkt verwenden, tritt ohne diese Definition ein Fehler auf.
  #Durch Angabe von backref das Gegenteil[AB]Die Beziehungsdefinition von Form kann weggelassen werden.
  aform = relationship('AForm', secondary = assoc, backref = 'atag')
  bform = relationship('BForm', secondary = assoc, backref = 'btag')

class Form(AbstractConcreteBase, base):
  id = Column(Integer, primary_key = True)
  amount = Column(Integer)

  @declared_attr
  def __tablename__(cls):
    return cls.__name__
  
  @declared_attr
  def __mapper_args__(cls):
    return {
      'polymorphic_identity': cls.__name__,
      'concrete':True
  }

  
class AForm(Form, base):
  pass

class BForm(Form, base):
  b_only = Column(String(10))


db_uri = 'sqlite:////tmp/m2m.sqlite'
engine = create_engine(db_uri, echo =True)

if database_exists(engine.url):
  drop_database(db_uri)
  create_database(engine.url)
base.metadata.create_all(bind = engine)

Session = sessionmaker(bind = engine)
session = Session()

#Da atag für AForm und btag für BForm definiert ist, müssen sie separat aufgerufen werden.
a = AForm(amount = 100)
atag = Tag(name = 'booked')
a.atag.append(atag)
session.add(a)

f = BForm(amount = 200)
tag = Tag(name = 'canceled')
f.btag.append(tag)
session.add(f)
session.commit()

#Es ist auch möglich, sie unten dynamisch aufzurufen.
getattr(btag, f.__class__.__name__.lower()).append(f)

forms=session.query(AForm).all()
for f in forms:
  print(f)
  print(f.atag[0].name)

Sie können zwar ohne Kenntnis der Zwischentabelle arbeiten, aber da atag für AForm und btag für BForm definiert ist, müssen diese separat aufgerufen werden. Es ist jedoch möglich, mit getattr eine Methodenreferenz aus dem Klassennamen des Objekts zu generieren und diese in die Methodenkette aufzunehmen.

Erneut veröffentlichen


getattr(tag, b.__class__.__name__.lower()).append(b)

Verwirrend! Es kann eine Meinung geben, dass es so geschrieben werden sollte. Dies hängt vom Codierungsstil und der Anzahl der abgeleiteten Klassen ab.

if isinstance(f, AForm):
  tag.aform.append(f)

Recommended Posts

Kombination von polymorpher Vererbung und vielen zu vielen mit SQLAlchemy
Verwenden Sie Enum mit SQLAlchemy
Holen Sie sich die Tabelle dynamisch mit sqlalchemy
Verwenden Sie DATE_FORMAT mit dem SQLAlchemy-Filter
Einführung in RDB mit sqlalchemy Ⅰ
Wie aktualisiere ich mit SQLAlchemy?
Wie mit SQLAlchemy ändern?
Group_by mit sqlalchemy und sum
Unterstützt mehrere Sitzungen mit SQL Alchemy
Wie lösche ich mit SQLAlchemy?