django-modelcluster Documentation

repository·main·Indexed 19 days ago

https://github.com/wagtail/django-modelcluster

A Django extension (version 6.5) that allows working with clusters of related models as a single unit in memory before they are persisted to the database. It provides ClusterableModel, ParentalKey, and ParentalManyToManyField to handle complex object trees, drafts, and previews independently of the database.

Tokens
1.2K
Snippets
3
Records
3
Agent score
18%

What's inside django-modelcluster

  1. What is django-modelcluster and how does it work?

    main

    django-modelcluster allows you to work with 'clusters' of related objects in memory before they are saved to the database. This is useful for scenarios like rendering data previews of unsaved forms, constructing trees for serialization, or handling draft states and revisions without complex database redesigns.

    It achieves this by extending Django's foreign key relations. By using ClusterableModel on a parent model and ParentalKey on child models, related objects are stored locally to the parent in memory. These objects can be accessed via a subset of the QuerySet API (like .all(), .add(), and .count()) even before the parent is saved to the database.

    from modelcluster.models import ClusterableModel
    from modelcluster.fields import ParentalKey
    
    class Band(ClusterableModel):
        name = models.CharField(max_length=255)
    
    class BandMember(models.Model):
        band = ParentalKey('Band', related_name='members', on_delete=models.CASCADE)
        name = models.CharField(max_length=255)
    
    # Usage:
    beatles = Band(name='The Beatles')
    beatles.members = [
        BandMember(name='John Lennon'),
        BandMember(name='Paul McCartney'),
    ]
    # The members exist in memory and can be queried:
    # [member.name for member in beatles.members.all()] -> ['John Lennon', 'Paul McCartney']
    
    beatles.save()  # Only now are the records written to the database
  2. Introspect child relations and M2M relations

    main

    You can programmatically inspect a model to find its child relations or many-to-many relations. This is useful for tasks like creating deep copies of models and their children.

    • Use get_all_child_relations(model) to find all child relations (including those from superclasses).
    • Use get_all_child_m2m_relations(model) to retrieve a list of all ParentalManyToManyField definitions on a model.
    from modelcluster.models import get_all_child_relations, get_all_child_m2m_relations
    
    # Get all child relations
    relations = get_all_child_relations(Band)
    
    # Get all ParentalManyToManyField relations
    m2m_relations = get_all_child_m2m_relations(Movie)
  3. Use ParentalManyToManyField for in-memory many-to-many relations

    main

    For many-to-many relationships, use ParentalManyToManyField. This allows the relations between the parent and the related objects to be stored in memory without writing to the database until the parent is saved.

    Important constraints:

    1. ParentalManyToManyField must be defined on the parent model.
    2. The related objects themselves (the instances being associated) must already exist in the database before they can be associated with the parent record via the ParentalManyToManyField.
    from modelcluster.models import ClusterableModel
    from modelcluster.fields import ParentalManyToManyField
    
    class Movie(ClusterableModel):
        title = models.CharField(max_length=255)
        actors = ParentalManyToManyField('Actor', related_name='movies')
    
    class Actor(models.Model):
        name = models.CharField(max_length=255)
    
    # Usage:
    harrison_ford = Actor.objects.create(name='Harrison Ford')
    star_wars = Movie(title='Star Wars')
    star_wars.actors = [harrison_ford]
    
    # The relation is held in memory:
    # star_wars.actors.count() -> 1
    
    star_wars.save()  # The Movie and the actor relations are now written to the database