Когда у меня есть два объекта, связанных с «отношением» в SQLAlchemy, я понял, что простого присвоения этого отношения недостаточно для распространения значений на другой объект. Например (см. Ниже), если у меня есть таблица «пользователь» и таблица «контакт» (обе они очень надуманные, но хорошо демонстрируют проблему), и у «пользователя» может быть несколько «контактов». В этом случае у меня будет внешний ключ между пользователями и контактами. Если я создам оба экземпляра User
и Contact
и позже назначу пользователю на контакт, я бы ожидал, что атрибуты FK будут обновлены (даже без DB flu sh) но это не так. Почему? И как я могу сказать SA делать это автоматически?
Я бы хотел, чтобы это работало, но, как вы можете видеть в полном примере ниже, это не так:
user = User(name='a', lname='b')
contact(type='email', value='foo@bar.com')
contact.user = user
assert contact.username == 'a' # <-- Fails because the attribute is still `None`
Пример полного запуска:
"""
This code example shows two tables related to each other by a composite key,
using an SQLAlchemy "relation" to allow easy access to related items.
However, as the last few lines show, simply assigning an object A to the
relation of object B does not update the attributes of object B until at least
a "flush" is called.
"""
from sqlalchemy import Column, ForeignKeyConstraint, Unicode, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation, sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = "user"
name = Column(Unicode, primary_key=True)
lname = Column(Unicode, primary_key=True)
class Contact(Base):
__tablename__ = "contact"
__table_args__ = (
ForeignKeyConstraint(
['username', 'userlname'],
['user.name', 'user.lname']
),
)
username = Column(Unicode, primary_key=True)
userlname = Column(Unicode, primary_key=True)
type = Column(Unicode)
value = Column(Unicode)
user = relation(User)
engine = create_engine('sqlite://')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
user = User(name="John", lname="Doe")
contact = Contact(type='email', value='john.doe@example.com')
contact.user = user # <-- How can I tell SA to set the FKs on *contact* here?
session.add(contact)
print('Before flush: contact.username user=%r' % contact.username)
session.flush()
print('After flush : contact.username user=%r' % contact.username)