ProvenPin Install

Django models with factory_boy and pytest-django db fixtures: working versions (tested 2026-10-15)

Django models built with factory_boy DjangoModelFactory (SubFactory, get_or_create, traits) and tested offline on in-memory SQLite with pytest-django database access rules and on_commit capture.

passed6 passed · 0 failed2026-10-15 14:11 UTC
Status
passed
Last passed
2026-10-15 14:11 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions5
django@6.1.2factory-boy@3.3.3pytest-django@4.14.0pytest@9.1.1faker@37.4.0
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38748988915

The code

Exactly the files that passed. Your agent can pull them with get_recipe("django-pytest-factoryboy") (install), or see them on GitHub.

requirements.txt
django==6.1.2
factory-boy==3.3.3
pytest-django==4.14.0
pytest==9.1.1
faker==37.4.0
settings.py
SECRET_KEY = "test-only-not-secret"
INSTALLED_APPS = ["shop"]
DATABASES = {
    "default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"},
}
USE_TZ = True
pytest.ini
[pytest]
pythonpath = .
DJANGO_SETTINGS_MODULE = settings
shop/__init__.py
shop/models.py
from django.db import models, transaction

class Author(models.Model):
    name = models.CharField(max_length=100, unique=True)

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="books")
    title = models.CharField(max_length=200)
    price_cents = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ["title"]

    def publish(self, notify):
        # runs only after the surrounding transaction commits
        transaction.on_commit(lambda: notify(self.pk))
factories.py
import factory
from factory.django import DjangoModelFactory

from shop.models import Author, Book

class AuthorFactory(DjangoModelFactory):
    class Meta:
        model = Author
        django_get_or_create = ("name",)  # reuse existing row instead of hitting unique constraint

    name = factory.Sequence(lambda n: f"Author {n}")

class BookFactory(DjangoModelFactory):
    class Meta:
        model = Book

    author = factory.SubFactory(AuthorFactory)
    title = factory.Sequence(lambda n: f"Book {n}")
    price_cents = 1000

    class Params:
        cheap = factory.Trait(price_cents=99)
tests/test_models.py
import pytest

from factories import AuthorFactory, BookFactory
from shop.models import Author

@pytest.mark.django_db
def test_subfactory_creates_author():
    book = BookFactory()
    assert Author.objects.count() == 1
    assert book.author.pk is not None

def test_build_strategy_skips_database():
    # no django_db mark needed: build() never writes, SubFactory follows the strategy
    book = BookFactory.build()
    assert book.pk is None and book.author.pk is None

def test_db_access_blocked_without_mark():
    # pytest-django blocks ORM access unless the test opts in with django_db
    with pytest.raises(RuntimeError):
        Author.objects.count()

@pytest.mark.django_db
def test_get_or_create_reuses_row():
    a = AuthorFactory(name="Ann")
    b = AuthorFactory(name="Ann")
    assert a.pk == b.pk and Author.objects.count() == 1

@pytest.mark.django_db
def test_trait_overrides_field():
    assert BookFactory(cheap=True).price_cents == 99

@pytest.mark.django_db
def test_publish_runs_on_commit(django_capture_on_commit_callbacks):
    # on_commit never fires inside the test's rolled-back transaction, so capture it
    book = BookFactory()
    calls = []
    with django_capture_on_commit_callbacks(execute=True):
        book.publish(calls.append)
    assert calls == [book.pk]

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.