sqlalchemy.exc.InvalidRequestError: Object '<Item at 0x111ff8640>' is already attached to session '5' (this is '4')

Viewed 935

Introduction

I am trying to seed data in the table for a test. For this, I am using factory boy package.

I have set up a factory class that handles seeding.

class OrderItemFactory(factory.alchemy.SQLAlchemyModelFactory):
    id = factory.Sequence(lambda n: n)

    orders = factory.SubFactory(
        'testsuite.database.factories.OrderFactory.OrderFactory')
    items = factory.SubFactory(
        'testsuite.database.factories.ItemFactory.ItemFactory')

    class Meta:
        model = OrderItem
        sqlalchemy_session_persistence = 'commit'

Issue

In the test I run the following code to create an order.

    # create order
    order = OrderFactory.create()
    item = session.query(Item).\
        filter(Item.id == 1).\
        first()

    orderItem = OrderItemFactory.create(
        orders=order,
        items=item
    )

However, if I am to manually create a row everything works fine.

orderItem = OrderItem(
    order_id=order.id,
    item_id=item.id
)

session.add(orderItem)
session.commit()
2 Answers

I think the issue comes from which SQLAlchemy session you're using.

With SQLAlchemy, each object belongs to a "session"; by default, factory_boy will use a new session when creating objects; you must set sqlalchemy_session in your Factory.Meta arguments:


# Import the `session` value from the place you're defining it for your project
from myproject.db import session

class OrderItemFactory(factory.alchemy.SQLAlchemyModelFactory):
    class Meta:
        model = OrderItem
        sqlalchemy_session_persistence = 'commit'
        sqlaclhemy_session = session

Usually, projects will define their factory subclass for these settings:

# project/testing.py
import factory

from . import db

class BaseFactory(factory.alchemy.SQLAlchemyFactory):
    class Meta:
        abstract = True
        sqlalchemy_session = db.session
        sqlalchemy_session_persistence = 'commit'


# project/orders/factories.py
import factory

from . import models
from project import testing

class OrderItemFactory(testing.BaseFactory):
    class Meta:
        model = models.OrderItem
        # sqlalchemy_session_* will be imported through the use of BaseFactory

If you're using flask_sqlalchemy, then you should check if you're inserting into the correct field, and not to a db relationship.

Related