def deconstruct(self): """ Deconstruct the object, used with migrations. """ name, path, args, kwargs = super(TaggableManager, self).deconstruct() # Remove forced kwargs. for kwarg in ('serialize', 'null'): del kwargs[kwarg] # Add arguments related to relations. # Ref: https://github.com/alex/django-taggit/issues/206#issuecomment-37578676 rel = _remote_field(self) if isinstance(rel.through, six.string_types): kwargs['through'] = rel.through elif not rel.through._meta.auto_created: kwargs['through'] = "%s.%s" % (rel.through._meta.app_label, rel.through._meta.object_name) related_model = _related_model(rel) if isinstance(related_model, six.string_types): kwargs['to'] = related_model else: kwargs['to'] = '%s.%s' % (related_model._meta.app_label, related_model._meta.object_name) return name, path, args, kwargs
def test_deconstruct_kwargs_kept(self): instance = TaggableManager(through=OfficialThroughModel, to='dummy.To') name, path, args, kwargs = instance.deconstruct() new_instance = TaggableManager(*args, **kwargs) self.assertEqual('tests.OfficialThroughModel', _remote_field(new_instance).through) self.assertEqual('dummy.To', _related_model(_remote_field(new_instance)))
def similar_objects(self): lookup_kwargs = self._lookup_kwargs() lookup_keys = sorted(lookup_kwargs) qs = self.through.objects.values(*six.iterkeys(lookup_kwargs)) qs = qs.annotate(n=models.Count('pk')) qs = qs.exclude(**lookup_kwargs) qs = qs.filter(tag__in=self.all()) qs = qs.order_by('-n') # TODO: This all feels like a bit of a hack. items = {} if len(lookup_keys) == 1: # Can we do this without a second query by using a select_related() # somehow? f = _get_field(self.through, lookup_keys[0]) remote_field = _remote_field(f) rel_model = _related_model(_remote_field(f)) objs = rel_model._default_manager.filter(**{ "%s__in" % remote_field.field_name: [r["content_object"] for r in qs] }) actual_remote_field_name = remote_field.field_name if VERSION > (1, 9): actual_remote_field_name = f.target_field.get_attname() else: actual_remote_field_name = f.related_field.get_attname() for obj in objs: items[(getattr(obj, actual_remote_field_name),)] = obj else: preload = {} for result in qs: preload.setdefault(result['content_type'], set()) preload[result["content_type"]].add(result["object_id"]) for ct, obj_ids in preload.items(): ct = ContentType.objects.get_for_id(ct) for obj in ct.model_class()._default_manager.filter(pk__in=obj_ids): items[(ct.pk, obj.pk)] = obj results = [] for result in qs: obj = items[ tuple(result[k] for k in lookup_keys) ] obj.similar_tags = result["n"] results.append(obj) return results
def similar_objects(self): lookup_kwargs = self._lookup_kwargs() lookup_keys = sorted(lookup_kwargs) qs = self.through.objects.values(*six.iterkeys(lookup_kwargs)) qs = qs.annotate(n=models.Count('pk')) qs = qs.exclude(**lookup_kwargs) qs = qs.filter(tag__in=self.all()) qs = qs.order_by('-n') # TODO: This all feels like a bit of a hack. items = {} if len(lookup_keys) == 1: # Can we do this without a second query by using a select_related() # somehow? f = self.through._meta.get_field(lookup_keys[0]) remote_field = _remote_field(f) rel_model = _related_model(_remote_field(f)) objs = rel_model._default_manager.filter( **{ "%s__in" % remote_field.field_name: [r["content_object"] for r in qs] }) for obj in objs: items[(getattr(obj, remote_field.field_name), )] = obj else: preload = {} for result in qs: preload.setdefault(result['content_type'], set()) preload[result["content_type"]].add(result["object_id"]) for ct, obj_ids in preload.items(): ct = ContentType.objects.get_for_id(ct) for obj in ct.model_class()._default_manager.filter( pk__in=obj_ids): items[(ct.pk, obj.pk)] = obj results = [] for result in qs: obj = items[tuple(result[k] for k in lookup_keys)] obj.similar_tags = result["n"] results.append(obj) return results