Skip to content

Commit

Permalink
implement fix jazzband#130
Browse files Browse the repository at this point in the history
  • Loading branch information
zeonn committed Apr 24, 2019
1 parent 5c6ee2f commit 6df25c0
Showing 1 changed file with 82 additions and 81 deletions.
163 changes: 82 additions & 81 deletions sortedm2m/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,87 +92,88 @@ def set(self, objs, **kwargs):
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
# *objs - objects to add. Either object instances, or primary keys of object instances.

# If there aren't any objects, there is nothing to do.
from django.db.models import Max, Model
if objs:
# Django uses a set here, we need to use a list to keep the
# correct ordering.
new_ids = []
for obj in objs:
if isinstance(obj, self.model):
if not router.allow_relation(obj, self.instance):
raise ValueError(
'Cannot add "%r": instance is on database "%s", value is on database "%s"' %
(obj, self.instance._state.db, obj._state.db)
)
if hasattr(self, '_get_fk_val'): # Django>=1.5
fk_val = self._get_fk_val(obj, target_field_name)
if fk_val is None:
raise ValueError('Cannot add "%r": the value for field "%s" is None' %
(obj, target_field_name))
new_ids.append(self._get_fk_val(obj, target_field_name))
else: # Django<1.5
new_ids.append(obj.pk)
elif isinstance(obj, Model):
raise TypeError(
"'%s' instance expected, got %r" %
(self.model._meta.object_name, obj)
)
else:
new_ids.append(obj)

db = router.db_for_write(self.through, instance=self.instance)
manager = self.through._default_manager.using(db)
vals = (manager
.values_list(target_field_name, flat=True)
.filter(**{
source_field_name: self._fk_val,
'%s__in' % target_field_name: new_ids,
}))
for val in vals:
if val in new_ids:
new_ids.remove(val)
_new_ids = []
for pk in new_ids:
if pk not in _new_ids:
_new_ids.append(pk)
new_ids = _new_ids
new_ids_set = set(new_ids)

if self.reverse or source_field_name == self.source_field_name:
# Don't send the signal when we are inserting the
# duplicate data row for symmetrical reverse entries.
signals.m2m_changed.send(sender=rel.through, action='pre_add',
instance=self.instance, reverse=self.reverse,
model=self.model, pk_set=new_ids_set, using=db)

# Add the ones that aren't there already
with atomic(using=db):
fk_val = self._fk_val
source_queryset = manager.filter(**{'%s_id' % source_field_name: fk_val})
sort_field_name = self.through._sort_field_name
sort_value_max = source_queryset.aggregate(max=Max(sort_field_name))['max'] or 0

manager.bulk_create([
self.through(**{
'%s_id' % source_field_name: fk_val,
'%s_id' % target_field_name: pk,
sort_field_name: sort_value_max + i + 1,
})
for i, pk in enumerate(new_ids)
])

if self.reverse or source_field_name == self.source_field_name:
# Don't send the signal when we are inserting the
# duplicate data row for symmetrical reverse entries.
signals.m2m_changed.send(sender=rel.through, action='post_add',
instance=self.instance, reverse=self.reverse,
model=self.model, pk_set=new_ids_set, using=db)
def _add_items(self, source_field_name, target_field_name, *objs, through_defaults=None):
# 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
# *objs - objects to add. Either object instances, or primary keys of object instances.
through_defaults = through_defaults or {}

# If there aren't any objects, there is nothing to do.
from django.db.models import Max, Model
if objs:
# Django uses a set here, we need to use a list to keep the
# correct ordering.
new_ids = []
for obj in objs:
if isinstance(obj, self.model):
if not router.allow_relation(obj, self.instance):
raise ValueError(
'Cannot add "%r": instance is on database "%s", value is on database "%s"' %
(obj, self.instance._state.db, obj._state.db)
)
fk_val = self.through._meta.get_field(
target_field_name).get_foreign_related_value(obj)[0]
if fk_val is None:
raise ValueError(
'Cannot add "%r": the value for field "%s" is None' %
(obj, target_field_name)
)
new_ids.append(fk_val)
elif isinstance(obj, Model):
raise TypeError(
"'%s' instance expected, got %r" %
(self.model._meta.object_name, obj)
)
else:
new_ids.append(obj)

db = router.db_for_write(self.through, instance=self.instance)
manager = self.through._default_manager.using(db)
vals = (manager
.values_list(target_field_name, flat=True)
.filter(**{
source_field_name: self._fk_val,
'%s__in' % target_field_name: new_ids,
}))
for val in vals:
if val in new_ids:
new_ids.remove(val)
_new_ids = []
for pk in new_ids:
if pk not in _new_ids:
_new_ids.append(pk)
new_ids = _new_ids
new_ids_set = set(new_ids)

if self.reverse or source_field_name == self.source_field_name:
# Don't send the signal when we are inserting the
# duplicate data row for symmetrical reverse entries.
signals.m2m_changed.send(sender=rel.through, action='pre_add',
instance=self.instance, reverse=self.reverse,
model=self.model, pk_set=new_ids_set, using=db)

# Add the ones that aren't there already
with atomic(using=db):
fk_val = self._fk_val
source_queryset = manager.filter(**{'%s_id' % source_field_name: fk_val})
sort_field_name = self.through._sort_field_name
sort_value_max = source_queryset.aggregate(max=Max(sort_field_name))['max'] or 0

manager.bulk_create([
self.through(**through_defaults, **{
'%s_id' % source_field_name: fk_val,
'%s_id' % target_field_name: pk,
sort_field_name: sort_value_max + i + 1,
})
for i, pk in enumerate(new_ids)
])

if self.reverse or source_field_name == self.source_field_name:
# Don't send the signal when we are inserting the
# duplicate data row for symmetrical reverse entries.
signals.m2m_changed.send(sender=rel.through, action='post_add',
instance=self.instance, reverse=self.reverse,
model=self.model, pk_set=new_ids_set, using=db)

return SortedRelatedManager

Expand Down

0 comments on commit 6df25c0

Please sign in to comment.