Próbuję wymyślić, jak rozłożyć klasy SQLAlchemy na kilka plików i przez całe życie nie wiem, jak to zrobić. Jestem całkiem nowy w SQLAlchemy, więc wybacz mi, jeśli to pytanie jest trywialne.
Rozważ te 3 klasy w każdym osobnym pliku :
A.py:
from sqlalchemy import *
from main import Base
class A(Base):
__tablename__ = "A"
id = Column(Integer, primary_key=True)
Bs = relationship("B", backref="A.id")
Cs = relationship("C", backref="A.id")
B.py:
from sqlalchemy import *
from main import Base
class B(Base):
__tablename__ = "B"
id = Column(Integer, primary_key=True)
A_id = Column(Integer, ForeignKey("A.id"))
C.py:
from sqlalchemy import *
from main import Base
class C(Base):
__tablename__ = "C"
id = Column(Integer, primary_key=True)
A_id = Column(Integer, ForeignKey("A.id"))
A potem powiedz, że mamy main.py coś takiego:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref, sessionmaker
Base = declarative_base()
import A
import B
import C
engine = create_engine("sqlite:///test.db")
Base.metadata.create_all(engine, checkfirst=True)
Session = sessionmaker(bind=engine)
session = Session()
a = A.A()
b1 = B.B()
b2 = B.B()
c1 = C.C()
c2 = C.C()
a.Bs.append(b1)
a.Bs.append(b2)
a.Cs.append(c1)
a.Cs.append(c2)
session.add(a)
session.commit()
Powyższe podaje błąd:
sqlalchemy.exc.NoReferencedTableError: Foreign key assocated with column 'C.A_id' could not find table 'A' with which to generate a foreign key to target column 'id'
Jak udostępnić deklaratywną podstawę w tych plikach?
Jaki jest „właściwy” sposób osiągnięcia tego celu, biorąc pod uwagę, że mógłbym rzucić na to coś takiego jak Pylony lub Turbogears ?
edytuj 10-03-2011
Znalazłem ten opis we frameworku Pyramids, który opisuje problem i, co ważniejsze, weryfikuje, że jest to rzeczywisty problem, a nie (tylko) moja zdezorientowana jaźń to jest problem. Mam nadzieję, że może pomóc innym, którzy odważą się podążać tą niebezpieczną drogą :)
źródło
Odpowiedzi:
Najprostszym rozwiązaniem problemu będzie wziąć
Base
z modułu, który importujeA
,B
aC
; Przerwij cykliczny import.base.py
from sqlalchemy.ext.declarative import declarative_base Base = declarative_base()
a.py
from sqlalchemy import * from base import Base from sqlalchemy.orm import relationship class A(Base): __tablename__ = "A" id = Column(Integer, primary_key=True) Bs = relationship("B", backref="A.id") Cs = relationship("C", backref="A.id")
b.py
from sqlalchemy import * from base import Base class B(Base): __tablename__ = "B" id = Column(Integer, primary_key=True) A_id = Column(Integer, ForeignKey("A.id"))
c.py
from sqlalchemy import * from base import Base class C(Base): __tablename__ = "C" id = Column(Integer, primary_key=True) A_id = Column(Integer, ForeignKey("A.id"))
main.py
from sqlalchemy import create_engine from sqlalchemy.orm import relationship, backref, sessionmaker import base import a import b import c engine = create_engine("sqlite:///:memory:") base.Base.metadata.create_all(engine, checkfirst=True) Session = sessionmaker(bind=engine) session = Session() a1 = a.A() b1 = b.B() b2 = b.B() c1 = c.C() c2 = c.C() a1.Bs.append(b1) a1.Bs.append(b2) a1.Cs.append(c1) a1.Cs.append(c2) session.add(a1) session.commit()
Działa na moim komputerze:
$ python main.py ; echo $? 0
źródło
scoped_session
.scoped_session
, jeśli nie wiesz, dlaczego potrzebujemy cię pamięć lokalna wątku; Problem z używaniemscoped_session
polega na tym, że sprawia, że łatwo jest zakończyć z wyciekami transakcji i nieaktualnymi danymi, bez wyraźnego linku do punktu w kodzie, kiedy to mogło się wydarzyć.Jeśli mogę dodać też trochę rozsądku, ponieważ miałem ten sam problem. Musisz zaimportować klasy w pliku, w którym tworzysz
Base = declarative_base()
PO utworzeniuBase
iTables
. Krótki przykład konfiguracji mojego projektu:model / user.py
from sqlalchemy import * from sqlalchemy.orm import relationship from model import Base class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) budgets = relationship('Budget')
model / budget.py
from sqlalchemy import * from model import Base class Budget(Base): __tablename__ = 'budget' id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey('user.id'))
model / __ init__.py
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker _DB_URI = 'sqlite:///:memory:' engine = create_engine(_DB_URI) Base = declarative_base() Base.metadata.create_all(engine) DBSession = sessionmaker(bind=engine) session = DBSession() from .user import User from .budget import Budget
źródło
Używam Pythona 2.7 + Flask 0.10 + SQLAlchemy 1.0.8 + Postgres 9.4.4.1
Ten standardowy szablon jest skonfigurowany z modelami User i UserDetail przechowywanymi w tym samym pliku „models.py” w module „user”. Te klasy dziedziczą z klasy bazowej SQLAlchemy.
Wszystkie dodatkowe klasy, które dodałem do mojego projektu, również wywodzą się z tej klasy bazowej, a gdy plik models.py urósł, postanowiłem podzielić plik models.py na jeden plik na klasę i natknąłem się na opisany problem tutaj.
Rozwiązaniem, które znalazłem, podobnie jak w poście @ computermacgyver z 23 października 2013 r., Było dołączenie wszystkich moich klas do pliku init .py nowego modułu, który utworzyłem, aby przechowywać wszystkie nowo utworzone pliki klas. Wygląda tak:
/project/models/ __init__.py contains from project.models.a import A from project.models.b import B etc...
źródło
Dla mnie dodanie
import app.tool.tool_entity
wnętrzaapp.py
ifrom app.tool.tool_entity import Tool
wnętrzatool/__init__.py
wystarczyło, aby powstał stół. Jednak nie próbowałem jeszcze dodawać relacji.Struktura folderów:
# app/tool/tool_entity.py from app.base import Base from sqlalchemy import Column, Integer, String class Tool(Base): __tablename__ = 'tool' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) fullname = Column(String) fullname2 = Column(String) nickname = Column(String) def __repr__(self): return "<User(name='%s', fullname='%s', nickname='%s')>" % ( self.name, self.fullname, self.nickname)
# app/tool/__init__.py from app.tool.tool_entity import Tool
# app/app.py from flask import Flask from sqlalchemy import create_engine from app.tool.tool_routes import tool_blueprint from app.base import Base db_dialect = 'postgresql' db_user = 'postgres' db_pwd = 'postgrespwd' db_host = 'db' db_name = 'db_name' engine = create_engine(f'{db_dialect}://{db_user}:{db_pwd}@{db_host}/{db_name}', echo=True) Base.metadata.create_all(engine) app = Flask(__name__) @app.route('/') def hello_world(): return 'hello world' app.register_blueprint(tool_blueprint, url_prefix='/tool') if __name__ == '__main__': # you can add this import here, or anywhere else in the file, as debug (watch mode) is on, # the table should be created as soon as you save this file. import app.tool.tool_entity app.run(host='0.0.0.0', port=5000, debug=True)
źródło