Home > Mobile >  Factory boy field not getting expected value
Factory boy field not getting expected value

Time:01-17

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:

  1. Add a "parameter" to decide which player should win;
  2. 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).

  •  Tags:  
  • Related