factory boy - how to create the required data of a factory (pre-generation hooks)
Asked Answered
R

1

7

note: I will try to explain the use case with a simplified scenario (which might look really odd to you).

I am having 2 models (which are related but do not have a foreign key):

# models.py
class User(models.Model):
    name = models.CharField()
    age = models.IntegerField()
    weight = models.IntegerField()
    # there are a lot more properties ... 

class Group(models.Model):
    name = models.CharField()
    summary = JSONField()

    def save(self, *args, **kwargs):
        self.summary = _update_summary()
        super().save(*args, **kwargs)
    def _update_summary(self):
        return calculate_group_summary(self.summary)

# this helper function is located in a helper file
def calculate_group_summary(group_summary):
   """calculates group.summary data based on group.users"""
   # retrieve users, iterate over them, calculate data and store results in group_summary object
   return group_summary

for above models I have this factories:

# factories.py
class UserFactory(factory.DjangoModelFactory):
    class Meta:
        model = User

    name = factory.Sequence(lambda n: "user name %d" % n)
    age = randint(10, 90))
    weight = randint(30, 110))

class GroupFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Group

    name = factory.Sequence(lambda n: "group name %d" % n)
    summary = {
        "users": [34, 66, 76],
        "age": {
            "mean": 0,
            "max": 0,
            "min": 0,
        },
        "weight": {
            "mean": 0,
            "max": 0,
            "min": 0,
        } 
    }

the particularity is that I update the JSON data in field group.summary on group.save().
note: I do not like to move this to a post_save signal because I want to avoid the "double" save (I have creation/revision fields in place).

so when I use GroupFactory(), I have to have the "users" in place.

I was looking at post-generation hooks https://factoryboy.readthedocs.io/en/latest/reference.html#post-generation-hooks, but I need "pre-generation hooks".

is there a "best practice" to generate the User data before GroupFactory is created (without creating them manually in the test case)? something like a "post-generation hook" but "pre"? :|

Runin answered 5/2, 2019 at 8:24 Comment(0)
F
7

Try using the _create() hook. Something like this:

class GroupFactory():
    ...


    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        # Create the prerequisite data here. 
        # Use `UserFactory.create_batch(n)` if multiple instances are needed.
        user = UserFactory()

        group = model_class(*args, **kwargs)

        # Update other fields here if needed.
        group.foo = bar

        # Required operation.
        group.save()

        return group

Reference: https://factoryboy.readthedocs.io/en/latest/reference.html#factory.Factory._create

Fireplug answered 16/5, 2020 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.