Django-fakery
An easy-to-use implementation of Creation Methods (aka Object Factory) for Django, backed by Faker
.
django_fakery
will try to guess the field's value based on the field's name and type.
Installation
Install with:
$ pip install django-fakery
QuickStart
from django_fakery import factory
from myapp.models import MyModel
factory.m(MyModel)(field='value')
If you're having issues with circular imports, you can also reference a model by using the M
utility function:
from django_fakery import factory, M
factory.m(M("myapp.MyModel"))(field="value")
If you really don't want to import things, you could also just reference a model by using the <app_label>.<ModelName>
syntax. This is not encouraged, as it will likely break type-hinting:
from django_fakery import factory
factory.m("myapp.MyModel")(field="value")
If you use pytest
, you can use the fakery
fixture (requires pytest
and pytest-django
):
import pytest
from myapp.models import MyModel
@pytest.mark.django_db
def test_mymodel(fakery):
fakery.m(MyModel)(field='value')
If you'd rather, you can use a more wordy API:
from django_fakery import factory
from myapp.models import MyModel
factory.make(
MyModel,
fields={
'field': 'value',
}
)
We will use the short API thorough the documentation.
The value of a field can be any python object, a callable, or a lambda:
from django.utils import timezone
from django_fakery import factory
from myapp.models import MyModel
factory.m(MyModel)(created=timezone.now)
When using a lambda, it will receive two arguments: n
is the iteration number, and f
is an instance of faker
:
from django.contrib.auth.models import User
user = factory.m(User)(
username=lambda n, f: 'user_{}'.format(n),
)
django-fakery
includes some pre-built lambdas for common needs. See shortcuts for more info.
You can create multiple objects by using the quantity
parameter:
from django_fakery import factory
from django.contrib.auth.models import User
factory.m(User, quantity=4)
For convenience, when the value of a field is a string, it will be interpolated with the iteration number:
from myapp.models import MyModel
user = factory.m(User, quantity=4)(
username='user_{}',
)
Foreign keys
Non-nullable ForeignKey
s create related objects automatically.
If you want to explicitly create a related object, you can pass a factory like any other value:
from django.contrib.auth.models import User
from food.models import Pizza
pizza = factory.m(Pizza)(
chef=factory.m(User)(username='Gusteau'),
)
If you'd rather not create related objects and reuse the same value for a foreign key, you can use the special value django_fakery.rels.SELECT
:
from django_fakery import factory, rels
from food.models import Pizza
pizza = factory.m(Pizza, quantity=5)(
chef=rels.SELECT,
)
django-fakery
will always use the first instance of the related model, creating one if necessary.
ManyToManies
Because ManyToManyField
s are implicitly nullable (ie: they're always allowed to have their .count()
equal to 0
), related objects on those fields are not automatically created for you.
If you want to explicitly create a related objects, you can pass a list as the field's value:
from food.models import Pizza, Topping
pizza = factory.m(Pizza)(
toppings=[
factory.m(Topping)(name='Anchovies')
],
)
You can also pass a factory, to create multiple objects:
from food.models import Pizza, Topping
pizza = factory.m(Pizza)(
toppings=factory.m(Topping, quantity=5),
)
Shortcuts
django-fakery
includes some shortcut functions to generate commonly needed values.
future_datetime(end='+30d')
Returns a datetime
object in the future (that is, 1 second from now) up to the specified end
. end
can be a string, anotther datetime, or a timedelta. If it's a string, it must start with +, followed by and integer and a unit, Eg: '+30d'
. Defaults to '+30d'
Valid units are:
'years'
,'y'
'weeks'
,'w'
'days'
,'d'
'hours'
,'hours'
'minutes'
,'m'
'seconds'
,'s'
Example:
from django_fakery import factory, shortcuts
from myapp.models import MyModel
factory.m(MyModel)(field=shortcuts.future_datetime('+1w'))
future_date(end='+30d')
Returns a date
object in the future (that is, 1 day from now) up to the specified end
. end
can be a string, another date, or a timedelta. If it's a string, it must start with +, followed by and integer and a unit, Eg: '+30d'
. Defaults to '+30d'
past_datetime(start='-30d')
Returns a datetime
object in the past between 1 second ago and the specified start
. start
can be a string, another datetime, or a timedelta. If it's a string, it must start with -, followed by and integer and a unit, Eg: '-30d'
. Defaults to '-30d'
past_date(start='-30d')
Returns a date
object in the past between 1 day ago and the specified start
. start
can be a string, another date, or a timedelta. If it's a string, it must start with -, followed by and integer and a unit, Eg: '-30d'
. Defaults to '-30d'
Lazies
You can refer to the created instance's own attributes or method by using Lazy objects.
For example, if you'd like to create user with email as username, and have them always match, you could do:
from django_fakery import factory, Lazy
from django.contrib.auth.models import User
factory.m(auth.User)(
username=Lazy('email'),
)
If you want to assign a value returned by a method on the instance, you can pass the method's arguments to the Lazy
object:
from django_fakery import factory, Lazy
from myapp.models import MyModel
factory.m(MyModel)(
myfield=Lazy('model_method', 'argument', keyword='keyword value'),
)
Pre-save and Post-save hooks
You can define functions to be called right before the instance is saved or right after:
from django.contrib.auth.models import User
from django_fakery import factory
factory.m(
User,
pre_save=[
lambda u: u.set_password('password')
],
)(username='username')
Since settings a user's password is such a common case, we special-cased that scenario, so you can just pass it as a field:
from django.contrib.auth.models import User
from django_fakery import factory
factory.m(User)(
username='username',
password='password',
)
Get or Make
You can check for existance of a model instance and create it if necessary by using the g_m
(short for get_or_make
) method:
from myapp.models import MyModel
myinstance, created = factory.g_m(
MyModel,
lookup={
'myfield': 'myvalue',
}
)(myotherfield='somevalue')
If you're looking for a more explicit API, you can use the .get_or_make()
method:
from myapp.models import MyModel
myinstance, created = factory.get_or_make(
MyModel,
lookup={
'myfield': 'myvalue',
},
fields={
'myotherfield': 'somevalue',
},
)
Get or Update
You can check for existence of a model instance and update it by using the g_u
(short for get_or_update
) method:
from myapp.models import MyModel
myinstance, created = factory.g_u(
MyModel,
lookup={
'myfield': 'myvalue',
}
)(myotherfield='somevalue')
If you're looking for a more explicit API, you can use the .get_or_update()
method:
from myapp.models import MyModel
myinstance, created = factory.get_or_update(
MyModel,
lookup={
'myfield': 'myvalue',
},
fields={
'myotherfield': 'somevalue',
},
)
Non-persistent instances
You can build instances that are not saved to the database by using the .b()
method, just like you'd use .m()
:
from django_fakery import factory
from myapp.models import MyModel
factory.b(MyModel)(
field='value',
)
Note that since the instance is not saved to the database, .build()
does not support ManyToManies or post-save hooks.
If you're looking for a more explicit API, you can use the .build()
method:
from django_fakery import factory
from myapp.models import MyModel
factory.build(
MyModel,
fields={
'field': 'value',
}
)
Blueprints
Use a blueprint:
from django.contrib.auth.models import User
from django_fakery import factory
user = factory.blueprint(User)
user.make(quantity=10)
Blueprints can refer other blueprints:
from food.models import Pizza
pizza = factory.blueprint(Pizza).fields(
chef=user,
)
)
You can also override the field values you previously specified:
from food.models import Pizza
pizza = factory.blueprint(Pizza).fields(
chef=user,
thickness=1
)
)
pizza.m(quantity=10)(thickness=2)
Or, if you'd rather use the explicit api:
from food.models import Pizza
pizza = factory.blueprint(Pizza).fields(
chef=user,
thickness=1
)
)
thicker_pizza = pizza.fields(thickness=2)
thicker_pizza.make(quantity=10)
Seeding the faker
from django.contrib.auth.models import User
from django_fakery import factory
factory.m(User, seed=1234, quantity=4)(
username='regularuser_{}'
)
Credits
The API is heavily inspired by model_mommy.
License
This software is released under the MIT License.