I have a mixin that is shared between two models. In it, I have a method that takes a dictionary and creates an SQLAlchemy object, this is raising a warning. The two models are very similar (all the same except for one column that has a relationship to a different table. However, all the column names have the same names.
Here is the mixin:
class ConstructionBase:
id: Mapped[int] = mapped_column(db.Integer, autoincrement=True, primary_key=True)
data: Mapped[bytes] = mapped_column(db.LargeBinary, unique=False, nullable=True)
measurement_date: Mapped[datetime] = mapped_column(db.DateTime, nullable=True)
def from_dict(self, data):
if 'module' in data:
self.module = Module.get_by_sn(data['module'])
if 'component' in data:
self.component = Component.get_by_sn(data['component'])
if 'type' in data:
TypeClass = type(self).type.property.mapper.class_
self.type = TypeClass.get_by_name(data['type'])
And my two models:
class Assembly(db.Model, UtilityMixin, PaginatedAPIMixin, TimeStampMixin, MTDdbSyncMixin, ConstructionBase):
__tablename__ = "assembly"
module_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('module.id', ondelete="CASCADE"), nullable=True)
component_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('component.id', ondelete="CASCADE"), nullable=True)
type_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('assembly_type.id'), nullable=False)
#Relationships
module: Mapped["Module"] = relationship(back_populates="assembly")
component: Mapped[List["Component"]] = relationship(back_populates="assembly")
type: Mapped["AssemblyType"] = relationship(back_populates="assembly")
class Test(db.Model, UtilityMixin, PaginatedAPIMixin, TimeStampMixin, MTDdbSyncMixin, ConstructionBase):
__tablename__ = "test"
module_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('module.id', ondelete="CASCADE"), nullable=True)
component_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('component.id', ondelete="CASCADE"), nullable=True)
type_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('test_type.id'), nullable=False)
#Relationships
module: Mapped["Module"] = relationship(back_populates="test")
component: Mapped["Component"] = relationship(back_populates="test")
type: Mapped["TestType"] = relationship(back_populates="test")
With this mixin I would can do something like
fake_assembly = Assembly()
fake_assembly.from_dict({'module':'mod_sn','component':'comp_sn','type':'the type'})
However when I do that I get this warning:
/home/application/models.py:656: SAWarning: Object of type <Assembly> not in session, add operation along 'Module.assembly' will not proceed (This warning originated from the Session 'autoflush' process, which was invoked automatically in response to a user-initiated operation.)
return Component.query.filter_by(serial_number=serial_number).first()
But the object works, when I print it out it is <Assembly None> which is wrong but when I do fake_assembly.module it gives the correct module object. And I can add it to the session and commit it just fine. Also, it only throws the error for objects that have relationships to other tables.
In regards to the warning I have been unable to find anything useful, so I have come here to try to learn about it. Maybe the way I am currently trying to do things is bad practice. Any insight or help is greatly appreciated!
I think somehow you are flushing the assembly when the second lookup occurs:
Component.get_by_sn(). So you connect a module, and then that module includes the assembly viaback_populates. Then the secondget_by_sntriggers the flush of themoduleand the session doesn't know what to do with the assembly connected to themodulebecause you haven't finished building it yet and it isn't in the session.I guess there are a lot of practices. I would probably consider a better practice to move the
factorypattern outside the class entirely or second best at least put it on a classmethod.Classmethod factory
This would be added via the mixin.
Standalone factory