Django Dynamic Fixture
Django Dynamic Fixture (DDF) is a full library designed to create dynamic model instances primarily for testing purposes. It allows developers to generate complex Django model objects with sensible default data, reducing boilerplate in tests. As of version 4.0.1, it supports modern Django and Python versions. Releases typically follow major Django version compatibility updates, with minor bug fix releases in between.
Common errors
-
AttributeError: module 'ddf' has no attribute 'Fixture'
cause Attempting to use the old `Fixture` class name after upgrading to ddf v4.0.0 or later.fixChange `Fixture` to `F`. Update import statements (`from ddf import Fixture` to `from ddf import F`) and usage (`Fixture(...)` to `F(...)`). -
TypeError: 'int' object is not callable
cause This often occurs when passing an integer (or other literal) directly to a field that expects a callable or `ddf.F` when using certain DDF features, e.g., in a list comprehension or generator, or when using `ddf.F` incorrectly.fixEnsure you are using `ddf.F(lambda: some_value)` for dynamic values or `ddf.F(some_callable)` when you intend to pass a callable function for a field value that should be evaluated later, especially in list/generator contexts. -
NameError: name 'G' is not defined
cause Forgetting to import the `G` function from the `ddf` module.fixAdd `from ddf import G` at the top of your file. -
django.db.utils.IntegrityError: NOT NULL constraint failed: myapp_model.field_id
cause Occurs when a non-nullable field or a unique field without a default value is not provided by `ddf.G` (or `ddf.G` generates a non-unique value for a unique field).fixExplicitly provide values for all non-nullable fields. For unique fields, ensure DDF can generate unique values (e.g., by using `Faker().unique.email()` or providing `F(lambda: unique_value_generator())`).
Warnings
- breaking The `Fixture` class was renamed to `F` in version 4.0.0. Code using `from ddf import Fixture` or `ddf.Fixture(...)` will break.
- breaking Version 4.0.0 dropped support for older Python and Django versions. Specifically, it requires Python >= 3.8 and Django >= 3.2.
- gotcha When using `ddf.G` for related fields (ForeignKey, ManyToManyField, OneToOneField), passing `F(...)` directly for the related object (e.g., `author=F(...)`) will not create the related object automatically. Instead, define nested attributes using double underscores (e.g., `author__name='...'`) or pass an existing object.
Install
-
pip install django-dynamic-fixture
Imports
- G
from ddf import G
- F
from ddf import F
- N
from ddf import N
- Fixture
from ddf import Fixture
from ddf import F
Quickstart
import os
from django.conf import settings
from django.apps import apps
from django.db import models
from ddf import G, F, N
from faker import Faker
# Minimal Django setup for the quickstart to be runnable without a full project
if not apps.ready:
settings.configure(
DEBUG=True,
INSTALLED_APPS=[
'django.contrib.auth',
'django.contrib.contenttypes',
],
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
},
SECRET_KEY='not-a-secret',
USE_TZ=True,
# Define a dummy app_label for models in this ad-hoc setup
MIGRATION_MODULES={'myapp': None} # Prevent migrations check
)
import django
django.setup()
# Define dummy models for the quickstart within a custom app_label
class Author(models.Model):
name = models.CharField(max_length=255)
email = models.EmailField(unique=True)
bio = models.TextField(blank=True)
class Meta:
app_label = 'myapp' # Required for models in minimal setup
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
pages = models.IntegerField(default=100)
class Meta:
app_label = 'myapp' # Required
def __str__():
return self.title
# --- Actual django-dynamic-fixture usage ---
fake = Faker()
print("--- Creating instances ---")
# 1. Create a single Author instance
author1 = G(Author, name=fake.name(), email=fake.email(), bio=fake.paragraph())
print(f" Created Author: {author1.name} ({author1.email})")
# 2. Create a Book instance referencing an existing author
book1 = G(Book, author=author1, title=fake.sentence(nb_words=4))
print(f" Created Book: '{book1.title}' by {book1.author.name}")
# 3. Create multiple Books with dynamic values using F()
# This will also create a new Author because 'author__name' is specified
books_dynamic = G(Book, n=2, author__name=F(fake.name), author__email=F(fake.email),
title=F(lambda: fake.sentence(nb_words=3)),
pages=F(lambda: fake.random_int(min=50, max=500)))
for book_obj in books_dynamic:
print(f" Created Dynamic Book: '{book_obj.title}' by {book_obj.author.name} ({book_obj.pages} pages)")
# 4. Create a non-persisted instance (not saved to database)
draft_book = N(Book, title="Draft Idea", author=author1, pages=20)
print(f" Created Draft Book (not persisted): '{draft_book.title}' by {draft_book.author.name}")
# You can verify it's not saved (requires django.db.models import and Django setup)
# from django.db import connection
# with connection.cursor() as cursor:
# cursor.execute("SELECT COUNT(*) FROM myapp_book")
# print(f"Total books in DB: {cursor.fetchone()[0]}")