Save all objects in QuerySet and related objects to a fixture

I've written a function to save a QuerySet to a fixture JSON file:

def save_as_fixture(query_set: QuerySet, fixture_name: str, app_label: str='mainapp'):
    app_config = apps.get_app_config(app_label)
    fixture_dir = os.path.join(app_config.path, "fixtures")
    os.makedirs(fixture_dir, exist_ok=True)
    fixture_path = os.path.join(fixture_dir, fixture_name)
    data = serializers.serialize("json", query_set, indent=2, use_natural_foreign_keys=True, use_natural_primary_keys=True)
    with open(fixture_path, 'w') as file:
        file.write(data)

But it doesn't save related objects.

I would like it to also save the objects that reference one of the objects of the QuerySet via a ForeignKey, OneToOneField, etc. How can I do that?

Your function correctly serializes a QuerySet into a JSON fixture while preserving foreign and primary keys using their natural values. However, it does not automatically include related objects, which can be a problem if you need a complete dataset.

Enhancing Your Function to Include Related Objects You need to ensure that all related objects (e.g., foreign keys, many-to-many relationships) are included in the fixture. You can do this by recursively fetching related objects.

Improved Version

import os
from django.apps import apps
from django.core import serializers
from django.db.models import QuerySet

def get_related_objects(queryset):
    """Recursively collect all related objects for the given queryset."""
    collected_objects = set(queryset)

    for obj in queryset:
        for field in obj._meta.get_fields():
            if field.is_relation and not field.auto_created:
                related_objects = getattr(obj, field.name)
                
                if related_objects is None:
                    continue
                
                if field.many_to_many or field.one_to_many:
                    related_queryset = related_objects.all()
                else:
                    related_queryset = [related_objects]

                collected_objects.update(related_queryset)
                collected_objects.update(get_related_objects(related_queryset))

    return collected_objects

def save_as_fixture(queryset: QuerySet, fixture_name: str, app_label: str = 'mainapp'):
    """Save a queryset and its related objects to a JSON fixture file."""
    app_config = apps.get_app_config(app_label)
    fixture_dir = os.path.join(app_config.path, "fixtures")
    os.makedirs(fixture_dir, exist_ok=True)
    fixture_path = os.path.join(fixture_dir, fixture_name)

    # Collect all related objects
    full_queryset = get_related_objects(queryset)

    # Serialize to JSON
    data = serializers.serialize("json", full_queryset, indent=2, 
                                 use_natural_foreign_keys=True, 
                                 use_natural_primary_keys=True)
    
    # Write to file
    with open(fixture_path, 'w') as file:
        file.write(data)

How It Works get_related_objects(queryset): Recursively finds all related objects (foreign keys, many-to-many, one-to-many). save_as_fixture(): Collects the complete dataset, including related objects. Serializes it into a JSON fixture. Saves it to the fixtures directory inside the specified Django app. Why This is Better ✅ Includes all related objects automatically. ✅ Ensures complete data integrity when loading the fixture. ✅ Prevents missing dependencies when restoring the database.

Now, when you call:

save_as_fixture(MyModel.objects.filter(some_condition), "my_fixture.json")

It will save MyModel instances along with all their related objects. 🚀

Вернуться на верх