• Stars
    star
    482
  • Rank 91,212 (Top 2 %)
  • Language
    Python
  • License
    BSD 3-Clause "New...
  • Created almost 11 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Django extension to allow working with 'clusters' of models as a single unit, independently of the database
https://travis-ci.org/wagtail/django-modelcluster.svg?branch=master

django-modelcluster

If you had a data model like this:

class Band(models.Model):
    name = models.CharField(max_length=255)

class BandMember(models.Model):
    band = models.ForeignKey('Band', related_name='members', on_delete=models.CASCADE)
    name = models.CharField(max_length=255)

wouldn't it be nice if you could construct bundles of objects like this, independently of the database:

beatles = Band(name='The Beatles')
beatles.members = [
    BandMember(name='John Lennon'),
    BandMember(name='Paul McCartney'),
]

Unfortunately, you can't. Objects need to exist in the database for foreign key relations to work:

IntegrityError: null value in column "band_id" violates not-null constraint

But what if you could? There are all sorts of scenarios where you might want to work with a 'cluster' of related objects, without necessarily holding them in the database: maybe you want to render a preview of the data the user has just submitted, prior to saving. Maybe you need to construct a tree of things, serialize them and hand them off to some external system. Maybe you have a workflow where your models exist in an incomplete 'draft' state for an extended time, or you need to handle multiple revisions, and you don't want to redesign your database around that requirement.

django-modelcluster extends Django's foreign key relations to make this possible. It introduces a new type of relation, ParentalKey, where the related models are stored locally to the 'parent' model until the parent is explicitly saved. Up to that point, the related models can still be accessed through a subset of the QuerySet API:

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)


>>> beatles = Band(name='The Beatles')
>>> beatles.members = [
...     BandMember(name='John Lennon'),
...     BandMember(name='Paul McCartney'),
... ]
>>> [member.name for member in beatles.members.all()]
['John Lennon', 'Paul McCartney']
>>> beatles.members.add(BandMember(name='George Harrison'))
>>> beatles.members.count()
3
>>> beatles.save()  # only now are the records written to the database

For more examples, see the unit tests.

Many-to-many relations

For many-to-many relations, a corresponding ParentalManyToManyField is available:

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)


>>> harrison_ford = Actor.objects.create(name='Harrison Ford')
>>> carrie_fisher = Actor.objects.create(name='Carrie Fisher')
>>> star_wars = Movie(title='Star Wars')
>>> star_wars.actors = [harrison_ford, carrie_fisher]
>>> blade_runner = Movie(title='Blade Runner')
>>> blade_runner.actors.add(harrison_ford)
>>> star_wars.actors.count()
2
>>> [movie.title for movie in harrison_ford.movies.all()]  # the Movie records are not in the database yet
[]
>>> star_wars.save()  # Star Wars now exists in the database (along with the 'actor' relations)
>>> [movie.title for movie in harrison_ford.movies.all()]
['Star Wars']

Note that ParentalManyToManyField is defined on the parent model rather than the related model, just as a standard ManyToManyField would be. Also note that the related objects - the Actor instances in the above example - must exist in the database before being associated with the parent record. (The ParentalManyToManyField allows the relations between Movies and Actors to be stored in memory without writing to the database, but not the Actor records themselves.)

Introspection

If you need to find out which child relations exist on a parent model - to create a deep copy of the model and all its children, say - use the modelcluster.models.get_all_child_relations function:

>>> from modelcluster.models import get_all_child_relations
>>> get_all_child_relations(Band)
[<RelatedObject: tests:bandmember related to band>, <RelatedObject: tests:album related to band>]

This includes relations that are defined on any superclasses of the parent model.

To retrieve a list of all ParentalManyToManyFields defined on a parent model, use modelcluster.models.get_all_child_m2m_relations:

>>> from modelcluster.models import get_all_child_m2m_relations
>>> get_all_child_m2m_relations(Movie)
[<modelcluster.fields.ParentalManyToManyField: actors>]

More Repositories

1

wagtail

A Django content management system focused on flexibility and user experience
Python
17,837
star
2

bakerydemo

Next generation Wagtail demo, born in Reykjavik
Python
963
star
3

Willow

A wrapper that combines the functionality of multiple Python image libraries into one API
Python
274
star
4

wagtail-localize

Translation plugin for Wagtail CMS
Python
226
star
5

wagtail-bakery

A set of helpers for baking your Django Wagtail site out as flat files.
Python
156
star
6

telepath

A library for exchanging data between Python and JavaScript
Python
139
star
7

wagtail-ai

Get help with your Wagtail content using AI superpowers.
Python
136
star
8

wagtail-autocomplete

An Autocomplete edit handler for selecting Pages, Snippets, and more.
Python
119
star
9

wagtail-personalisation

Rule-based personalisation for Wagtail CMS
Python
117
star
10

wagtail-generic-chooser

A toolkit for custom chooser popups in Wagtail
Python
116
star
11

queryish

A library for constructing queries on arbitrary data sources following Django's QuerySet API
Python
106
star
12

wagtailtrans

A Wagtail add-on for supporting multilingual sites
Python
104
star
13

wagtail-factories

Factory boy classes for wagtail
Python
101
star
14

wagtail-transfer

Content transfer for Wagtail
Python
88
star
15

docker-wagtail-develop

Shell
74
star
16

wagtail.org

Wagtail’s official marketing website
Python
66
star
17

rfcs

Wagtail RFCs
45
star
18

wagtail-review

A Wagtail extension for gathering annotations and feedback on pages before publication
Python
43
star
19

wagtail-airtable

Airtable import and export support for Wagtail pages and Django models.
Python
43
star
20

gsoc

Resources, activity, discussions for Wagtail’s participation to Google Summer of Code
Python
39
star
21

wagtail-live

High speed publishing from messaging apps to a Wagtail live blog. A GSoC 2021 project.
Python
37
star
22

nextjs-loves-wagtail

Tutorial: Next.js ❤️ Wagtail
Python
36
star
23

vagrant-wagtail-develop

A script to painlessly set up a Vagrant environment for development of Wagtail
Shell
36
star
24

wagtail-gitpod

Launch a ready-to-code Wagtail development environment with a single click.
30
star
25

guide

A website to teach Wagtail CMS to content editors, moderators and administrators.
Python
30
star
26

sphinx-wagtail-theme

Sphinx theme for Wagtail
SCSS
29
star
27

wagtail-streamfield-migration-toolkit

Python
25
star
28

wagtail-whoosh

Search backend for Wagtail CMS using Whoosh engine.
Python
24
star
29

outreachy

Resources, activity, discussions for Wagtail’s participation to Outreachy
20
star
30

areweheadlessyet

Are we headless yet?
TypeScript
19
star
31

wagtail-vector-index

Store Wagtail pages & Django models as embeddings in vector databases
Python
18
star
32

cookiecutter-wagtail-package

A cookiecutter template for building Wagtail add-on packages
Python
17
star
33

django-permissionedforms

A Django extension for creating forms that vary according to user permissions
Python
12
star
34

roadmap

Wagtail public roadmap
9
star
35

stylelint-config-wagtail

Shareable stylelint config for CSS and SCSS, following Wagtail’s code style.
JavaScript
9
star
36

wagtail-newsletter

Send email newsletters based on Wagtail content
Python
9
star
37

wagtail-multiple-chooser-panel

An InlinePanel variant allowing multiple items to be quickly selected
JavaScript
7
star
38

wagtail-localize-git

Translate Wagtail content using a git repository and PO files
Python
6
star
39

gitpod-wagtail-develop

Dockerfile
6
star
40

eslint-config-wagtail

Shareable ESLint config for Wagtail, based on airbnb/javascript.
JavaScript
6
star
41

wagtail-review-ui

Frontend UI for adding comments on Wagtail pages
TypeScript
4
star
42

wagtail-editable-help

Make help text editable in the Wagtail admin
Python
3
star
43

wagtail-hallo

Wagtail's legacy Hallo.js richtext editor
Python
3
star
44

accessibility

Documentation relating to accessibility efforts in Wagtail
HTML
3
star
45

workshop

2
star
46

how-to-run-a-wagtail-space

Advice for anyone running a community Wagtail event
2
star
47

gsod

Resources, activity, discussions for Wagtail’s participation to Google Season of Docs
2
star
48

telepath-unpack

JavaScript library for unpacking values that have been packed with telepath
JavaScript
2
star
49

your-first-wagtail-site

The solution for Wagtail’s official getting started tutorial
Python
1
star