fixturefactory

Factory to generate Django model objects. Easier than factoryboy and factorygirl


Keywords
django, factory, fixture
License
Other
Install
pip install fixturefactory==0.2b

Documentation

About:

fixturefactory is a great library for creating Django fixtures.

Within fixturefactory.py, you will find a BaseFactory and DjangoMixin.

  • The BaseFactory class was written to make developing with factories for other purposes or frameworks very simple.
  • The DjangoMixin class provides some methods to make fixture creation easy.

Please fork, give feedback, or add an issue to the tracker!

Use Cases:

Creating instances of a model

>>> ChildFactory()
>>> ChildFactory(save_to_db=False)

Create and retrieve an instance

>>> child1 = ChildFactory().last_obj_created
>>> child2 = ChildFactory()() # same as above

Dynamically passing params to your factories to override factory defaults

>>> BrotherFactory(pk1=child1.pk, pk2=1) # (params become available as class vars)

Defining your Factories:

All factories you create have these basic characteristics:

  • Must inherit from BaseFactory (should also inherit from DjangoMixin to simplify working with Django)
  • Must have a class variable, 'model', which accepts parameters and has a save() method
  • Must have a method, 'getparams', which returns a dict containing the params necessary to instantiate the model
  • Optionally, can have a method, 'lastly', which can be executed after model instantiation (useful for m2m)

The basic template looks like this:

class ChildFactory(BaseFactory, DjangoMixin):
    model = myapp.models.SomeModel

    def getparams(self):
        return {}

    #optional method
    def lastly(self):
        pass

Example Implementations:

The following factory generates generic Django users. A more advanced implementation may make use of randomly generated text, etc. Note that in this example, getparams returns locals(), which is a dict of the local environment. If you have defined temporary variables in the getparams() method, this approach can cause django to raise an exception, but it also brings up the point that getparams() should not do anything complicated or temporary. The purpose of getparams() is to define parameters that will eventually be used to instantiate the factory's model

class UserFactory(BaseFactory, DjangoMixin):
    model = django.contrib.auth.models.User

    def getparams(self):
        pk = self.getUnusedPk() # Utilize the methods in DjangoMixin
        username = 'markov_%s' % pk
        password = 'sha1$86d38$73c3ea4bbe34f27d06b53115a8af1cd66ff263b3' # using lastly(), you can avoid encryption stuff
        return locals()

    #def lastly(self): pass # optional

Lets say you'd rather let django handle password encryption. The lastly() method will execute code after the object has been created. An interesting point here is that all variables defined in getparams() become available to lastly() as class variables

    def lastly(self):
        a = self.last_obj_created
        a.set_password(self.username) # sets the password by default the defined username

Also, you can choose not to execute lastly at time of instantiation:

>>> UserProfileFactory(lastly=False)
UserProfileFactory: last_obj_created <Betsy>

This next example shows how to implement Foreign Keys, where the UserProfile has a Foreign Key on the above example's User model. Note that the 'user' variable in getparams() is an instance of the UserFactory's model

class UserProfileFactory(BaseFactory, DjangoMixin):
    model = myapp.models.UserProfile

    def getparams(self):
        """An example of a foreign key"""
        user = UserFactory().last_obj_created
        pk = user.pk #this User and UserProfile share the same primary key
        return locals()

Implementing Many to Many Relationships are also very easy, and there are a couple different ways to do this. This example utilizes fixturefactory's 'lastly' method to execute some code after the model has been instantiated. In this case, a many to many connection is made using the instantiated model.

For this example, lets assume the UserProfile model has a many to many relationship with a model called 'Group'

class UserProfileFactory(BaseFactory, DjangoMixin):
    model = myapp.models.UserProfile

    def getparams(self):
        return {} # lets assume you have already coded this part

    def lastly(self):
        """Creating a many to many relationship after model instantiation"""
        inst = self.last_obj_created
        inst.groups.add(Group.objects.get(pk=1))

Here's another many to many example. If you didn't want to define a method, you can add m2m relationship after instantiation

>>> userprofile_instance = UserProfileFactory(lastly=False)()

>>> group = Group.objects.get(pk=1)
>>> userprofile_instance.groups.add(group)

If you wanted to require a parameter at runtime, your factory might look like this:

class RelatedUserFactory(BaseFactory, DjangoMixin):
    model = myapp.models.RelatedUser

    def getparams(self):
        pk1 = self.pk1 #NOTE the class var (self.pk1) and local var (pk1) must have the same name 
        pk2 = self.pk2
        return locals()

>>> RelatedUserFactory(pk1=3, pk2=5) #keywords 'pk1' and 'pk2' required in this case.

Development:

To use BaseFactory for a purpose other than Django fixtures, you'd have to (probably) override the BaseFactory.create() method. You will also probably want to create a mixin to simplify development (see DjangoMixin for an example).

Any parameters passed in to the Factory at time of instantiation are available as class variables. This is useful for mixin development,when you may need to know what parameters are overriding defaults defined by getparams().

I hope all this encourages you to use fixturefactory!