Factory boy to select from existing flask_sqlalchemy table with Iterator using parameter

Question:

There are lots of great answers here, but I can’t quite find the one to solve my problem.

Two SQLAlchemy models: Calendar and Transaction. Transactions link to the Calendar model:

class Calendar(Model):
    calendar_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    cal_date = db.Column(db.Date, unique=True, nullable=False)

class Transactions(Model):
    transaction_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    transaction_date = Column(db.Date, nullable=False)
    transaction_calendar_id = reference_col('calendar', pk_name='calendar_id')
    transaction_calendar = relationship('Calendar', backref=db.backref('transaction_dates'))

Then Factory Boy classes to generate SQLAlchemy objects:

class CalendarFactory(BaseFactory):
    calendar_id = Sequence(lambda n: n)
    cal_date = Sequence(lambda n: datetime.date(2021,1,1) + relativedelta(days=n))

    class Meta:
        model = Calendar
        sqlalchemy_get_or_create = ('cal_date', )
        
class TransactionFactory(BaseFactory):
    transaction_date = Sequence(lambda n: datetime.date(2022,1,1) + relativedelta(days=n))
    transaction_calendar = SubFactory(CalendarFactory, cal_date=SelfAttribute('..transaction_date'))

    class Meta:
        model = Transactions

The trouble I’m having is that when my TransactionFactory is generated, it tries to generate a CalendarFactory object even when there is already one for the given date, and I get the following error:

(sqlite3.IntegrityError) UNIQUE constraint failed: calendar.cal_date

I’ve tried a number of strategies including StaticFactory as describe in Avoiding duplicates with factory_boy factories

The solution I’m trying to work on now is described in Choosing from a populated table in factory boy while using flask

This is what I’m trying, but it ain’t working:

def lazy_calendar(cal_date):
  """Turn query into a lazily evaluated generator"""
  yield from Calendar.query.filter_by(cal_date=cal_date).all()

class TransactionFactory(BaseFactory):
    transaction_date = Sequence(lambda n: datetime.date(2022,1,1) + relativedelta(days=n))
    transaction_calendar = LazyAttribute(lambda c: lazy_calendar(c.transaction_date))

Question: can I call a Factory Boy Iterator with a parameter so I can get back the generator with the single date that I want?

Or am I completely barking up the wrong tree here?

Note: this issue has only come to light when I’m running unit tests simulatenously via VS Code Testing feature. When I run my tests sequentially via pytest, it’s fine. But I would like to get the multithreaded version working if poss.

Asked By: Whiz

||

Answers:

So, no responses, but after some time away I’ve been able to fix it myself. As often happens with specific scenarios, I’d followed a path and ended up in an overly complex knot. The solution was to start from the beginning and simply.

The key mistake was to generate the id in the CalendarFactory object. This allowed for the creation of multiple Calendar objects with the same cal_date.

I stopped using the StaticFactory and hey presto! Green ticks across the board.

So working code is:

class CalendarFactory(BaseFactory):
    cal_date = Sequence(lambda n: datetime.date(2021,1,1) + relativedelta(days=n))

    class Meta:
        model = Calendar
        sqlalchemy_get_or_create = ('cal_date', )

Thanks!

You’re welcome.

Answered By: Whiz