[PYTHON] Wovon ich süchtig war, als ich Klassenvererbung und gemeinsame Tabellenvererbung in SQLAlchemy kombinierte

@declared_attr hat Auswirkungen bis zur abgeleiteten Tabelle

Annahme

Ich war süchtig nach

Wenn Sie mit dem Dekorator @declared_attr einen Index für die Klasse Human definieren, wird der Index an die unterste Klasse gesendet.

joint_ng.py


from sqlalchemy.ext.declarative import *
from sqlalchemy import *
from sqlalchemy_utils import database_exists, drop_database, create_database
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class Human(object):
    age = Column(Integer)
    name = Column(String(50))

    @declared_attr
    def __table_args__(cls):
        return(Index('index_name', 'name'),)

class Employee(Human, Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

class Engineer(Employee):
    __tablename__ = 'engineer'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    engineer_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'engineer',
    }
    __table_args__ = {
        
    }

class Manager(Employee):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    manager_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
    }
    __table_args__ = {
        
    }

engine = create_engine('mysql://user:pass@localhost/dummy?charset=utf8')

if database_exists(engine.url):
    drop_database(engine.url)
create_database(engine.url)
Base.metadata.create_all(engine)

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

m = Manager(age=10, name = 'foo', manager_name='hoge')
session.add(m)
session.commit()

Beim Indizieren der Namensspalte in einer Manager-Tabelle, die keine Namensspalte definiert, tritt ein Fehler auf.

Traceback (most recent call last):
  File "joint.py", line 27, in <module>
    class Engineer(Employee):
(Kürzung…)
  File "/home/satosi/.pyenv/versions/3.6.1/lib/python3.5/site-packages/sqlalchemy/util/_collections.py", line 194, in __getitem__
    return self._data[key]
KeyError: 'name'

Apropos, es scheint, dass die Situation nicht aus der Fehlermeldung verschluckt werden kann, und es hat einige Zeit gedauert, die Ursache einzugrenzen, also werde ich sie veröffentlichen.

Gegenmaßnahmen

Definieren Sie einfach table_args in der Manager-Klasse neu.

joint_ok.py


class Engineer(Employee):
    __tablename__ = 'engineer'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    engineer_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'engineer',
    }
    __table_args__ = {
        
    }

class Manager(Employee):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    manager_name = Column(String(30))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
    }
    __table_args__ = {
        
    }

Die Vererbung der polymorphen Identität ist unerwartet

Annahme

Ich möchte die Tabellendefinition und die Modelllogik separat schreiben.

Ich war süchtig nach

Ich habe versucht, in der Reihenfolge deklarative_Base-> Klasse zu erben, die das Spaltenmitglied (Manager) -> Klasse der Anwendungsschicht (SubManager) angibt, aber der Typ, der der Schlüssel zum Polymorphismus ist, ist nicht angegeben.

joint_ng2.py


import sys
from sqlalchemy.ext.declarative import *
from sqlalchemy import *
from sqlalchemy_utils import database_exists, drop_database, create_database
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

class Manager(Employee):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    manager_name = Column(String(30))
    
    __mapper_args__ = {
        'polymorphic_identity':'manager',
    }

    __table_args__ = {        
    }

    def shout(self):
        print('Oh')

class SubManager(Manager):
    def shout(self):
        print('Wah')

engine = create_engine('mysql://user:pass@localhost/dummy?charset=utf8')

if database_exists(engine.url):
    drop_database(engine.url)
create_database(engine.url)
Base.metadata.create_all(engine)

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

m = SubManager(manager_name='hoge')
m.shout()
session.add(m)
session.commit()
query = session.query(Manager)
print(query)
manager = query.first()
assert manager.type is None
manager.shout()

Ausgabe


Wah
SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name 
FROM employee INNER JOIN manager ON employee.id = manager.id
Oh

Versuchen Sie also, @declared_attr anzugeben.

joint_ng2.py


class Manager(Employee):
    (snip...)
    @declared_attr
    def __mapper_args__(cls):
       return {
        'polymorphic_identity':'manager',
       }

Es hat funktioniert, aber ich wurde gewarnt, dass es sich um eine doppelte Definition handelt.

Ausgabe


/home/satosi/.pyenv/versions/3.6.1-mtxweb/lib/python3.5/site-packages/sqlalchemy/orm/mapper.py:1034: SAWarning: Reassigning polymorphic association for identity 'manager' from <Mapper at 0x7fb0202ed978; Manager> to <Mapper at 0x7fb0202edbe0; SubManager>: Check for duplicate use of 'manager' as value for polymorphic_identity.
  self, self.polymorphic_identity)
Wah
SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name 
FROM employee INNER JOIN manager ON employee.id = manager.id
Wah

Gegenmaßnahmen

Ändern Sie einfach die Reihenfolge der Vererbung. Solange zwischen der App-Ebene und dem abgeleiteten Typ der gemeinsamen Tabellenvererbung eine Eins-zu-Eins-Entsprechung besteht, sollte es keinen Unterschied in der Funktionalität geben.

joint_ok.py


from sqlalchemy.ext.declarative import *
from sqlalchemy import *
from sqlalchemy_utils import database_exists, drop_database, create_database
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class _Manager():
    def shout(self):
        print('Oh')

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

class Manager(Employee, _Manager):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
    manager_name = Column(String(30))
    
    __mapper_args__ ={
    'polymorphic_identity':'manager',
    }

    __table_args__ = {        
    }

    def shout(self):
        print('Wah')

engine = create_engine('mysql://user:pass@localhost/dummy?charset=utf8')

if database_exists(engine.url):
    drop_database(engine.url)
create_database(engine.url)
Base.metadata.create_all(engine)

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

m = Manager(manager_name='hoge')
m.shout()
session.add(m)
session.commit()
query = session.query(Manager)
print(query)
manager = query.first()
assert manager.type is not None
manager.shout()

Ausgabe


Wah
SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name 
FROM employee INNER JOIN manager ON employee.id = manager.id
Wah

Es war arm. Es wäre eine unerwartete Freude, wenn es jemandem helfen würde.

Recommended Posts

Wovon ich süchtig war, als ich Klassenvererbung und gemeinsame Tabellenvererbung in SQLAlchemy kombinierte
Ein Hinweis, dem ich beim Erstellen einer Tabelle mit SQL Alchemy verfallen war
Ich war süchtig nach falschen Klassenvariablen und Instanzvariablen in Python
Wovon ich süchtig war, als ich Python Tornado benutzte
Wovon ich beim Erstellen von Webanwendungen in einer Windows-Umgebung abhängig war
Wovon ich süchtig war, als der Processing-Benutzer zu Python wechselte
Als ich versuchte, PIL und matplotlib in einer virtuellen Umgebung zu installieren, war ich süchtig danach.
Wovon ich süchtig war, als ich mit riesigen Dateien in einer Linux 32-Bit-Umgebung umging
Wovon ich süchtig war, als ich ALE in Vim für Python einführte
Was ich süchtig nach Python Autorun war
Der Dateiname war in Python schlecht und ich war süchtig nach Import
Drei Dinge, von denen ich süchtig war, als ich Python und MySQL mit Docker verwendete
Ich war süchtig danach, 2020 mit Selen (+ Python) zu kratzen
Ich war süchtig danach, logging.getLogger mit Flask 1.1.x zu versuchen
Was ich getan habe, als ich wütend war, es mit der Option enable-shared einzufügen
Was wurde gefragt, wenn Random Forest in der Praxis verwendet wurde?
Eine Geschichte, nach der ich süchtig war, als ich in Go nil als Funktionsargument angab
Als ich versuchte, mithilfe von Anforderungen in Python zu kratzen, war ich süchtig nach SSLError, also einem Workaround-Memo
Die Platte, von der ich süchtig war, als ich MeCab in Heroku einsetzte
Ein Hinweis, von dem ich süchtig war, als ich unter Linux einen Piepton machte
Wovon ich süchtig war, als ich mein eigenes neuronales Netzwerk mit den Gewichten und Vorurteilen aufbaute, die ich mit dem MLP-Klassifikator von scikit-learn bekam.
Ich war süchtig nach Multiprocessing + Psycopg2
Als ich Django in mein Home-Verzeichnis legte, wurde ich mit einem Berechtigungsfehler in eine statische Datei eingebunden
Numpys Intelligenz (Eingabevervollständigung) ist in VS Code unvollständig und ich war leicht süchtig nach der Lösung
Worauf ich mich beim Studium von tkinter bezog
Ich war süchtig danach, mysqlclient zu installieren
Ich war auf dotCloud süchtig nach Flask
Docker x Visualization hat nicht funktioniert und ich war süchtig danach, also habe ich es zusammengefasst!
Ein Hinweis, dem ich beim Ausführen von Python mit Visual Studio Code verfallen war
Eine Geschichte, der ich nach der SFTP-Kommunikation mit Python verfallen war
Beachten Sie, dass ich süchtig danach war, TensowFlow einzurichten
[Einführung in json] Nein, ich war süchtig danach. .. .. ♬
Ich habe eine Klasse in Python3 und Java geschrieben
Was ich beim Update von Python 2.6 auf 2.7 gemacht habe
[Frage] Was passiert, wenn Sie% in Python verwenden?
Beachten Sie, dass ich süchtig nach dem npm-Skript war, das in der Überprüfungsumgebung nicht übergeben wurde
Was tun, wenn nur das Fenster angezeigt wird und im Pygame nichts angezeigt wird?
Nachdem ich die Watson IoT Platform-Anwendung mit Flask implementiert hatte, war ich süchtig nach MQTT-Verbindungen
Hinweise zum Hinzufügen von Setter und Deleter in einer abgeleiteten Klasse zur getter-Eigenschaft der Basisklasse
Praktische Linux-Tastaturbedienung, die ich mir selbst beibringen möchte, als ich in der Schule war
[Go language] Seien Sie vorsichtig, wenn Sie einen Server mit mux + cors + alice erstellen. Besonders darüber, wovon ich in Bezug auf CORS süchtig war.
Was beim Nachahmen zu tun ist, wird in Python eingebettet
[openpyxl] Was tun, wenn IllegalCharacterError in pandas.DataFrame.to_excel angezeigt wird?
pickle Um zu lesen, was in 2 Serien mit 3 Serien gemacht wurde
[Spark] Ich bin süchtig nach "", null und [] in DataFrame
Eine Geschichte, von der ich bei np.where süchtig war
Python: Kann in Lambda wiederholt werden
Ich möchte am Ende etwas mit Python machen
Ich war überrascht, eine nette Rezension zu erhalten, als ich Python an CheckIO und seine Erklärung schrieb
Memo (März 2020), dem ich bei der Installation von Arch Linux auf dem MacBook Air 11'Early 2015 verfallen war
Wovon ich in Kapitel 3 der kollektiven Intelligenz abhängig war. Es ist kein Tippfehler, daher denke ich, dass etwas mit meinem Code nicht stimmt.