I am using factory boy to generate data for my django application.
It is a tennis matches app which has player one and two as shown in below class. Either of it will be a winner which will be store in winner_one field.
I am getting some third player name in this field instead of player one or two. That player is also present in table.
Please advise what would be the best way to fix this?
class MatchFactory(factory.django.DjangoModelFactory):
class Meta:
model = Match
player_one = factory.SubFactory(UserFactory)
player_two = factory.SubFactory(UserFactory)
league = factory.Iterator(League.objects.all())
type = fuzzy.FuzzyChoice(Match.MATCH_CHOICES, getter=lambda c: c[0])
winner_one = random.choice([player_one, player_two])
start_date = fuzzy.FuzzyNaiveDateTime(
datetime.today() relativedelta(months=1),
datetime.today() relativedelta(months=3)
)
end_date = start_date
CodePudding user response:
This one seems like a good use case of factory_boy's post generation hook.
class MatchFactory(factory.django.DjangoModelFactory):
class Meta:
model = Match
player_one = factory.SubFactory(UserFactory)
player_two = factory.SubFactory(UserFactory)
league = factory.Iterator(League.objects.all())
type = fuzzy.FuzzyChoice(Match.MATCH_CHOICES, getter=lambda c: c[0])
start_date = fuzzy.FuzzyNaiveDateTime(
datetime.today() relativedelta(months=1),
datetime.today() relativedelta(months=3)
)
end_date = start_date
@factory.post_generation
def winner_one(self, create, extracted, **kwargs):
if extracted:
self.winner_one = extracted
else:
self.winner_one = random.choice([self.player_one, self.player_two])
CodePudding user response:
The issue stems from the random.choice([user_one, user_two]) declaration: that declaration is executed at module import time, not when running the factory.
At that point, both values are factory.SubFactory(UserFactory) — i.e a recipe for creating a new user.
Once random.choice() is called, it will return one of the declarations; your code is thus equivalent to:
class MatchFactory(factory.django.DjangoModelFactory):
...
player_one = factory.SubFactory(UserFactory)
player_two = factory.SubFactory(UserFactory)
winner_one = factory.SubFactory(UserFactory)
It picks 3 different users each time.
I would suggest going for the following pattern:
- Add a "parameter" to decide which player should win;
- Use that value to choose the right attribute:
class MatchFactory(factory.django.DjangoModelFactory):
class Meta:
model = Match
class Params:
# Decide who the winner is; that field won't be passed to the `Match` model.
winner = factory.fuzzy.FuzzyChoice([1, 2])
player_one = factory.SubFactory(UserFactory)
player_two = factory.SubFactory(UserFactory)
winner_one = factory.LazyAttribute(
lambda o: o.player_one if o.winner == 1 else o.player_two
)
With that pattern, you can also "choose" the winner when calling the factory:
MatchFactory(player_one=some_player, player_two=some_player, winner=1).
