Skip to content

Commit

Permalink
Update to support Django 1.9
Browse files Browse the repository at this point in the history
Django 1.9 removes ReverseManyRelatedObjectsDescriptor in favor of using ManyRelatedObjectsDescriptor with `reverse` argument passed into it (though the meaning of reverse has been reversed, hah).

It also adds a set() method to RelatedManager that handles setting all the new related objects. It tries to be more efficient by only adding and removing the ones necessary instead of clearing them all and adding them again. The old behavior is maintained by passing the `clear` keyword argument, which is what we now do.
  • Loading branch information
appden committed Aug 6, 2015
1 parent a64be03 commit 3328131
Showing 1 changed file with 38 additions and 14 deletions.
52 changes: 38 additions & 14 deletions sortedm2m/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from django.db import models
from django.db.models import signals
from django.db.models.fields.related import add_lazy_relation, create_many_related_manager
from django.db.models.fields.related import ManyToManyField, ReverseManyRelatedObjectsDescriptor
from django.db.models.fields.related import ManyToManyField
from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
from django.utils import six
from django.utils.functional import curry
from django.utils.functional import cached_property, curry

from .compat import get_foreignkey_field_kwargs
from .compat import get_model_name
Expand All @@ -35,8 +35,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
pass


def create_sorted_many_related_manager(superclass, rel):
RelatedManager = create_many_related_manager(superclass, rel)
def create_sorted_many_related_manager(superclass, rel, *args):
RelatedManager = create_many_related_manager(superclass, rel, *args)

class SortedRelatedManager(RelatedManager):
def get_queryset(self):
Expand Down Expand Up @@ -86,6 +86,12 @@ def get_prefetch_queryset(self, instances, queryset=None):

get_prefetch_query_set = get_prefetch_queryset

def set(self, objs, **kwargs):
# Choosing to clear first will ensure the order is maintained.
kwargs['clear'] = True
super(SortedRelatedManager, self).set(objs, **kwargs)
set.alters_data = True

def _add_items(self, source_field_name, target_field_name, *objs):
# source_field_name: the PK fieldname in join table for the source object
# target_field_name: the PK fieldname in join table for the target object
Expand Down Expand Up @@ -171,13 +177,31 @@ def _add_items(self, source_field_name, target_field_name, *objs):
return SortedRelatedManager


class ReverseSortedManyRelatedObjectsDescriptor(ReverseManyRelatedObjectsDescriptor):
@property
def related_manager_cls(self):
return create_sorted_many_related_manager(
self.field.rel.to._default_manager.__class__,
self.field.rel
)
try:
from django.db.models.fields.related import ReverseManyRelatedObjectsDescriptor

class ReverseSortedManyRelatedObjectsDescriptor(ReverseManyRelatedObjectsDescriptor):
@cached_property
def related_manager_cls(self):
return create_sorted_many_related_manager(
self.field.rel.to._default_manager.__class__,
self.field.rel
)
except ImportError:
from django.db.models.fields.related import ManyRelatedObjectsDescriptor

# ReverseManyRelatedObjectsDescriptor was removed from Django 1.9
class ReverseSortedManyRelatedObjectsDescriptor(ManyRelatedObjectsDescriptor):
def __init__(self, field):
super(ReverseSortedManyRelatedObjectsDescriptor, self).__init__(field.remote_field)

@cached_property
def related_manager_cls(self):
return create_sorted_many_related_manager(
self.rel.model._default_manager.__class__,
self.rel,
False # This is the new `reverse` argument (which ironically should be False)
)


class SortedManyToManyField(ManyToManyField):
Expand Down Expand Up @@ -209,9 +233,9 @@ def deconstruct(self):
kwargs['sorted'] = self.sorted
return name, path, args, kwargs

def contribute_to_class(self, cls, name):
def contribute_to_class(self, cls, name, **kwargs):
if not self.sorted:
return super(SortedManyToManyField, self).contribute_to_class(cls, name)
return super(SortedManyToManyField, self).contribute_to_class(cls, name, **kwargs)

# To support multiple relations to self, it's useful to have a non-None
# related name on symmetrical relations for internal reasons. The
Expand All @@ -222,7 +246,7 @@ def contribute_to_class(self, cls, name):
if self.rel.symmetrical and (self.rel.to == "self" or self.rel.to == cls._meta.object_name):
self.rel.related_name = "%s_rel_+" % name

super(ManyToManyField, self).contribute_to_class(cls, name)
super(ManyToManyField, self).contribute_to_class(cls, name, **kwargs)

# The intermediate m2m model is not auto created if:
# 1) There is a manually specified intermediate, or
Expand Down

0 comments on commit 3328131

Please sign in to comment.