问题
I have a class that looks like this:
class Foo(Base):
pk = Column(Integer, Sequence('foo'), primary_key=True)
I want to make another field, ref, that defaults to being equal to pk for newly-created objects. Unfortunately, I'm using Postgres, which has a non-standard nextval() function, so I can't simply make both of the columns default to the Sequence object.
Here's my current solution:
class Foo(Base):
pk = Column(Integer, Sequence('foo'), primary_key=True)
ref = Column(Integer, index=True, nullable=False, default=func.currval('foo'))
However this depends on two things: SQLAlchemy generating an INSERT statement with pk before ref, and Postgres evaluating expressions from left to right (so the nextval() call generated by pk always happens before the currval call generated by ref).
I would greatly appreciate any suggestions for how to do this better!
EDIT: There's also the more solution of setting default=-1 or something and forcing the caller to update the ref field. That's safer and more robust to changes in SQLA/Postgres semantics but pretty inconvenient for clients of the class.
回答1:
It is possible to utilize SQLAlchemy's ORM after_insert mapper event:
from sqlalchemy import event
# Model from OP
class Foo(Base):
pk = Column(Integer, Sequence('foo'), primary_key=True)
ref = Column(Integer, index=True, nullable=False, default=-1)
@staticmethod
def fill_ref(mapper, connection, target):
# Note: You cannot use ORM in the event callbacks
connection.execute(mapper.mapped_table.update()
.values(ref=target.pk))
.where(table.c.pk==target.pk))
event.listen(Foo, 'after_insert', Foo.fill_ref)
回答2:
I recommend using a trigger to set that value. Though I haven't tested this myself, you could have a trigger activate AFTER INSERT that reads the new row's PK value and update the ref column with that value. It seems somewhat similar to Postgresql insert trigger to set value except that you want the trigger to be after the insert, instead of before.
来源:https://stackoverflow.com/questions/32277403/how-can-i-create-a-sqlalchemy-column-whose-default-is-to-be-equal-to-the-primary