Класи SQLAlchemy у файлах


82

Я намагаюся зрозуміти, як розподілити класи SQLAlchemy по кількох файлах, і я за все життя не можу зрозуміти, як це зробити. Я дуже новачок у SQLAlchemy, тому пробачте мене, якщо це питання є тривіальним

Розглянемо ці 3 класи в кожному з них власним файлом :

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"))

А потім скажіть, що у нас main.py приблизно такий:

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()

Вищезазначене дає помилку:

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'

Як поділитися декларативною базою між цими файлами?

Що таке «правильний» спосіб зробити це, з огляду на , що я міг би кинути що - щось на зразок Пілони або Turbogears на вершині цього?

редагувати 10-03-2011

Я знайшов цей опис із фреймворку Pyramids, який описує проблему і, що ще важливіше, підтверджує, що це справжня проблема, а не (лише) лише моя розгублена суть, в якій проблема. Сподіваюся, це може допомогти іншим, хто наважиться на цю небезпечну дорогу :)


7
@ S.Lott Вищезазначене працює, якщо всі класи в одному файлі, тож ти скажи мені :)
joveha

Ваш код не видає цієї помилки, надішліть код, який має фактичну помилку. Виправте імпорт, запустіть його, щоб хтось насправді міг побачити вашу помилку.
knitti

@ S.Lott Моя плутанина, очевидно, була зосереджена навколо того, як уникнути циклічного імпорту. Я походжу з Сі, де це не проблема. Приношу свої вибачення за те, що зайняли Ваш час
joveha

@joveha: Що? Які ці циклічні проблеми імпорту у вас виникають. Будь ласка, опублікуйте код із циклічним імпортом, щоб ми могли пояснити, як їх розкласти та уникнути циклів. У цих коментарях занадто багато розмитих гіпотетик. Яка проблема у вас? Будь ласка, будьте конкретні.
S.Lott,

Відповіді:


88

Найпростішим рішенням вашої проблеми буде Baseвилучення модуля, що імпортує A, Bта C; Порушити циклічний імпорт.

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()

Працює на моїй машині:

$ python main.py ; echo $?
0

1
Використовуйте scoped_session.
користувач

3
@user: обробка сеансу не пов'язана з питанням у цій публікації, що насправді є простим старим питанням python (як мені імпортувати матеріали?); але оскільки я привернув вашу увагу, я радив би категорично не використовувати scoped_session, якщо ви не знаєте, навіщо вам потрібне локальне сховище потоків; Проблема використання scoped_sessionполягає в тому, що це полегшує завершення витікаючих транзакцій та застарілих даних, без явного посилання на точку у коді, коли це могло статися.
SingleNegationElimination

Здається, цей шаблон дизайну не працює для python3. Чи існує якесь просте виправлення, сумісне з python3?
computermacgyver

@computermacgyver: цей шаблон повинен працювати коректно в версіях python. Задайте нове запитання, щоб ви могли включити весь свій код та помилки, які ви бачите.
SingleNegationElimination

Дякую @dequestarmappartialsetattr. Я виявив, що помилка трапляється лише тоді, коли я намагався помістити a.py, b.py, c.py та model.py в окремий модуль. Я знайшов рішення в такому випадку включити код base.py у файл __init__.py модуля. Я розмістив тут код і додаткові пояснення . Дякую за відповідь.
computermacgyver

13

Якщо я можу додати свою частинку розуму, оскільки у мене була та сама проблема. Вам потрібно імпортувати класи у файлі, де ви створюєте Base = declarative_base()ПІСЛЯ того, як ви створили Baseі Tables. Короткий приклад створення мого проекту:

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

8

Я використовую Python 2.7 + Flask 0.10 + SQLAlchemy 1.0.8 + Postgres 9.4.4.1

Цей шаблонний шаблон поставляється з моделями User і UserDetail, що зберігаються в одному файлі "models.py" в модулі "user". Обидва класи успадковуються від базового класу SQLAlchemy.

Усі додаткові класи, які я додав до свого проекту, також походять з цього базового класу, і, оскільки файл models.py збільшився, я вирішив розділити файл models.py на один файл на клас і зіткнувся з описаною проблемою тут.

Рішення, яке я знайшов, за тим самим напрямком, що й пост @ computermacgyver від 23 жовтня 2013 р., Полягало у включенні всіх моїх класів у файл init .py нового модуля, який я створив для зберігання всіх нещодавно створених файлів класів. Виглядає так:

/project/models/

__init__.py contains

from project.models.a import A 
from project.models.b import B
etc...

2
чому ви вважаєте, що вам потрібно використовувати Flask?
ночі

0

Для мене додавання import app.tool.tool_entityвсередині app.pyта from app.tool.tool_entity import Toolвсередині tool/__init__.pyбуло достатньо, щоб таблиця була створена. Однак я ще не намагався додати стосунки.

Структура папки:

app/
  app.py
  tool/
    __init__.py
    tool_entity.py
    tool_routes.py
# 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.