freshcrate
Home > Databases > django-modelcluster

django-modelcluster

Django extension to allow working with 'clusters' of models as a single unit, independently of the database

Description

django-modelcluster =================== If you had a data model like this: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python >>> 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``: .. code-block:: python >>> from modelcluster.models import get_all_child_m2m_relations >>> get_all_child_m2m_relations(Movie) [<modelcluster.fields.ParentalManyTo

Release History

VersionChangesUrgencyDate
6.4.1Imported from PyPI (6.4.1)Low4/21/2026
v6.4.1* Handle `get_prefetch_queryset` deprecation, for Django 6.0 support (Sage Abdullah) Low12/4/2025
v6.4* Add `UniqueConstraint` support for uniqueness validation (Sage Abdullah) * Remove `pytz` dependency (Sage Abdullah) * Added Django 5.1 and Python 3.13 support * Removed Django 3.2 and Python 3.8 support Low12/18/2024
v6.3* Support filtering with Q objects (Shohan Dutta Roy) * Support random ordering with `.order_by("?")` (Shohan Dutta Roy) * Support `distinct()` on querysets (Shohan Dutta Roy) * Support `iso_weekday` and `iso_year` field lookups (Andy Babic) * Support datetime transform expressions on `values` and `values_list` (Andy Babic) * Fix: Correctly handle filtering on fields on related models when those fields have names that match a lookup type (Andy Babic) * Fix: Correctly handle null foreign keLow2/26/2024
v6.2.1* Fix: Prevent failure on pre-3.1.0 versions of django-taggit without `_remove_prefetched_objects` Low1/4/2024
v6.2* Added Django 5.0 support * Removed Django 4.1 support * Implement prefetching for ClusterTaggableManager (Andy Chosak) Low1/3/2024
v6.1* Removed Django 2.2, 3.0, 3.1 & 4.0 support * Added Django 4.2 support (Irtaza Akram) * Fixed deprecation warning for removal of `django.utils.timezone.utc` (John-Scott Atlakson) * Fix: Avoid unnecessary call to localtime for timestamps already in UTC (Stefan Hammer) * Removed Python 3.7 support * Add Python 3.11 and 3.12 support Low10/4/2023
v6.0* BREAKING: ClusterForm now builds no child formsets when neither `formsets` nor `exclude_formsets` is specified in the Meta class, rather than building a formset for every child relation (Matt Westcott) * Removed Python 3.5 and 3.6 support * Removed Django 2.0 and 2.1 support * Support explicit definitions for nested formsets within ClusterForm, via a `formsets` option on the outer formset's definition (Matt Westcott) * Add `inherit_kwargs` attribute to ClusterForm child formsets (Matt WestLow3/14/2022
v5.3* Avoid accessing live queryset on unsaved instances, for preliminary Django 4.1 compatibility (Matt Westcott) * Support traversing one-to-one and many-to-one relations in `filter` / `order_by` lookups (Andy Babic) * Implement `values()` method on FakeQuerySet (Andy Babic) * Allow `values()` and `values_list()` to be chained with other queryset modifiers (Andy Babic) * Fix: Fix HTML escaping behaviour on `ClusterForm.as_p()` (Matt Westcott) * Fix: Match standard behaviour queryset of returnLow3/10/2022
v5.2* Implement `copy_cluster` method on ClusterableModel (Karl Hobley) * Add `formset_name` option on ClusterableModel formsets to allow the formset name to differ from the relation name (Matt Westcott) * Fix: Fix tests for Django 3.2 (Alex Tomkins, Matt Westcott, María Fernanda Magallanes) * Fix: Ensure ptr_id fields are correctly populated when deserialising models with multi-level inheritance (Alex Tomkins) Low10/13/2021
v5.1* Allow child form class to be overridden in the `formsets` Meta property of ClusterForm (Helder Correia) * Add prefetch_related support to ParentalManyToManyField (Andy Chosak) * Implement `copy_child_relation` and `copy_all_child_relations` methods on ClusterableModel (Karl Hobley) * Fix: Fix behavior of ParentalKeys and prefetch_related() supplied with a lookup queryset (Juha Yrjölä) Low9/10/2020
v5.0.2* Fix: Fix compatibility with django-taggit 1.3.0 (Martin Sandström) Low5/26/2020
v5.0.1* Fix: ClusterForm without an explicit `formsets` kwarg now allows formsets to be omitted from form submissions, to fix regression with nested relations * Fix: ParentalManyToManyField data is now loaded correctly by `manage.py loaddata` (Andy Babic) Low1/6/2020
v4.4.1* Fix: ClusterForm without an explicit `formsets` kwarg now allows formsets to be omitted from form submissions, to fix regression with nested relations Low1/6/2020
v5.0* Removed Python 2 and 3.4 support * Removed Django 1.10 and 1.11 support * Added django-taggit 1.x compatibility (Gassan Gousseinov, Matt Westcott) Low8/6/2019
v4.4* Django 2.2 compatibility * Support nested child relationships in ClusterForm (Sam Costigan) Low4/2/2019
4.3* Added support for filter lookup expressions such as `__lt`Low11/22/2018
v4.2* Django 2.1 compatibility * Python 3.7 compatibility * Implemented prefetch_related on FakeQuerySet (Haydn Greatnews) * Fix: Saving a ClusterableModel with a primary key of 0 no longer throws an IntegrityError (A Lee) * Fix: Serialization now respects `serialize=False` on ParentalManyToManyFields (Tadas Dailyda)Low8/8/2018
v3.1* Django 1.11 compatibility * Python 3.6 compatibility * Added the ability to install the optional dependency `django-taggit` using `pip install django-modelcluster[taggit]` * Fix: ClusterForm.save(commit=True) now correctly writes ParentalManyToManyField relations back to the database rather than requiring a separate model.save() step * Fix: ClusterForm.is_multipart() now returns True when a child form requires multipart submission * Fix: ClusterForm.media now includes media defined on Low2/27/2018
v4.1* `on_delete` on ParentalKey now defaults to CASCADE if not specified Low2/12/2018
v4.0* Django 2.0 compatibility * Removed Django 1.8 and 1.9 support * Child formsets now validate uniqueness constraints * Fix: Many-to-many relations inside inline formsets are now saved correctlyLow12/13/2017
v3.0.1- Fix: Added _result_cache property on FakeQuerySet (necessary for model forms with ParentalManyToManyFields to work correctly on Django 1.8-1.9) Low2/2/2017
v3.0- Added support for many-to-many relations (Thejaswi Puthraya, Matt Westcott) - Added compatibility with django-taggit 0.20 and dropped support for earlier versions - Deprecated the Model._meta.child_relations property (get_all_child_relations should be used instead) - Implemented the `set()` method on related managers (introduced in Django 1.9) Low2/2/2017
v2.0- Removed Django 1.7 and Python 3.2 support - Added system check to disallow related_name='+' on ParentalKey - Added support for TAGGIT_CASE_INSENSITIVE on ClusterTaggableManager - Field values for serialization are now fetched via pre_save (which, in particular, ensures that file fields are committed to storage) - Fix: System checks now correctly report a model name that cannot be resolved to a model - Fix: prefetch_related on a ClusterTaggableManager no longer fails (but doesn't prefetch eitheLow4/22/2016
v1.1- Django 1.9 compatibility - Added exclude() method to FakeQuerySet - Removed dependency on the 'six' package, in favour of Django's built-in version Low12/17/2015
v1.1b1- Django 1.9 compatibility - Added exclude() method to FakeQuerySet - Removed dependency on the 'six' package, in favour of Django's built-in version Low12/4/2015
v1.0- Removed Django 1.6 and Python 2.6 support - Added system check to ensure that ParentalKey points to a ClusterableModel - Added validate_max, min_num and validate_min parameters to childformset_factory Low10/9/2015
v0.6.2Fix: Updated add_ignored_fields declaration so that South / Django 1.6 correctly ignores modelcluster.contrib.taggit.ClusterTaggableManager again Low4/13/2015
v0.6.1re-release of 0.6 (withdrawn due to packaging issues) Low4/9/2015
v0.6- Django 1.8 compatibility - 'modelcluster.tags' module has been moved to 'modelcluster.contrib.taggit' Low4/9/2015
v0.5- ClusterForm.Meta formsets can now be specified as a dict to allow extra properties to be set on the underlying form. - Added order_by() method to FakeQuerySet - Fix: Child object ordering is now applied without needing to save to the database Low2/3/2015
v0.4- Django 1.7 compatibility - Fix: Datetimes are converted to UTC on serialisation and to local time on deserialisation, to match Django's behaviour when accessing the database - Fix: ParentalKey relations to a model's superclass are now picked up correctly by that model - Fix: Custom Media classes on ClusterForm now behave correctly Low9/4/2014
v0.3- Added exists(), first() and last() methods on FakeQuerySet - Fix: Model ordering is applied when adding items to DeferringRelatedManager Low6/17/2014

Dependencies & License Audit

Loading dependencies...

Similar Packages

azure-storage-blobMicrosoft Azure Blob Storage Client Library for Pythonazure-template_0.1.0b6187637
azure-storage-file-shareMicrosoft Azure Azure File Share Storage Client Library for Pythonazure-template_0.1.0b6187637
mirakuruProcess executor (not only) for tests.3.0.2
opentelemetry-instrumentation-qdrantOpenTelemetry Qdrant instrumentation0.60.0
modelsearchA library for indexing Django models with Elasicsearch, OpenSearch or database and searching them with the Django ORM.1.3