Why isn't SQLAlchemy translating this object generated by a FactoryBoy SubFactory into a foreign key?
Asked Answered
B

1

6

I'm using Flask and SQLAlchemy (via the Flask-SQLAlchemy extension) together with Factory_Boy.

My GearItem model has a foreign key to GearCategory. Factory_Boy handles this through the SubFactory function that creates the object to be used as the foreign key in the original factory.

Here are my model definitions:

class GearCategory(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Text, unique=True, nullable=False)
    gear_items = db.relationship('GearItem', backref='category',
            lazy='dynamic', order_by='GearItem.name')

class GearItem(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Text, nullable=False, index=True)
    category_id = db.Column(db.Integer, db.ForeignKey('gear_category.id'), index=True)

And here are my factory_boy Factory definitions:

class GearCategoryFactory(BaseFactory): # Based on factory.alchemy.SQLAlchemyModelFactory
    class Meta:
        model = gear_models.GearCategory
    name = factory.LazyAttribute(lambda n: faker.word())

class GearItemFactory(BaseFactory):
    class Meta:
        model = gear_models.GearItem
    name = factory.LazyAttribute(lambda n: faker.word())
    category_id = factory.SubFactory(GearCategoryFactory)

I can call GearItemFactory() with no problems and it's clearly generating both a GearItem and a parent GearCategory that's intended to be used as the foreign key.

However, when I call db.session.flush(), SQLAlchemy doesn't translate the object created by the SubFactory into an integer that can be used as the foreign key. Instead, it tries to pass the object itself to the underlying database driver, which then complains that has no idea how to handle an object of type GearCategory.

The error that I'm getting is sqlalchemy.exc.ProgrammingError: (db_driver) can't adapt type 'GearCategory' [SQL: 'INSERT INTO gear_item (...

What am I doing wrong?

Bilbao answered 7/8, 2015 at 7:59 Comment(1)
you need to show how you are actually creating the objects here. But also I don't know what factory boy is and it appears to be some major element here, so perhaps email them for support.Aidoneus
B
9

The problem is that the GearItemFactory definition specifies a Python object reference for the foreign key database ID. sqlalchemy has no problem translating Python objects into database Foreign Key ID. However in my factory I specified an object-to-database column mapping rather than an object-to-object mapping, so SQLAlchemy (rightfully) thinks I want to pass the Python object straight to the database. Just need to change the factory foreign key to an object-to-object mapping and sqlalchemy will handle the actual database FK column behind the scenes.

This is the broken line:

category_id = factory.SubFactory(GearCategoryFactory)

See how the backref on GearCategory is named category not category_id? Updating that line to use category fixes the problem:

category = factory.SubFactory(GearCategoryFactory)
Bilbao answered 2/11, 2015 at 18:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.