Skip to content
Advertisement

How can I set model defaults based on a SqlAlchemy relationship?

Let’s say I have the following models:

class Customer(Model):
    __tablename__ = 'customer'
    id = Column(Integer())
    treatments = relationship('Treatment', back_populates='customer')
    shipments = relationship('Shipment', back_populates='customer')

class Treatment(Model):
    __tablename__ = 'treatment'
    customer_id = Column(Integer(), ForeignKey('customer.id'))
    customer = relationship('Customer', back_populates='treatments')
    treatment_date = Column(DateTime(), nullable=False)

class Shipment(Model):
    __tablename__ = 'shipment'
    customer_id = Column(Integer(), ForeignKey('customer.id'))
    customer = relationship('Customer', back_populates='shipments')
    ship_date = Column(DateTime(), nullable=False)

I would like to be able to default the Shipment.ship_date to be the day before the Treatment.treatment_date. In other words, I want to do the following:

customer = Customer()
treatment = Treatment(treatment_date="11/02/2017")
customer.treatments.append(treatment)
shipment = Shipment()
customer.shipments.append(shipment)
shipment.ship_date
# 11/01/2017

How do I set defaults based on relationships when they’re set dynamically by methods like append?


For clarification, this is a question about SqlAlchemy and when relationships are set up. For example, I’ve tried the following:

class Shipment(Model):
    # ...same set up as above
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.ship_date = self.customer.treatments[0].treatment_date - timedelta(1)

But that throws a TypeError because SqlAlchemy hasn’t set up the self.customer field yet.

Advertisement

Answer

It appears as though SQLAlchemy does not configure relationships both ways until after the model has been added and committed to the session.

customer.shipments.append(new_shipment)
customer.shipments # includes the new shipment
new_shipment.customer # None
session.add(customer)
session.commit()
new_shipment.customer # <Customer object>

While this is annoying and I’m not sure why SQLAlchemy doesn’t populate the two-way relationship when one side is created, it can be resolved by manually setting the relationship on both sides. For example:

new_shipment = Shipment(customer=customer)
new_shipment.ship_date # 11/01/2017
customer.shipments.append(new_shipment)
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement