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.
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.