def __get__(self, instance, instance_type=None): if not instance: return self RelatedManager = create_many_related_manager(AuthorsManager, self.field.rel) manager = RelatedManager( model=self.field.rel.to, instance=instance, symmetrical=self.field.rel.symmetrical, source_field_name=self.field.m2m_field_name(), target_field_name=self.field.m2m_reverse_field_name(), reverse=False, ) # These two attributes are set after the instance is created to # maintain compatibility between Django 1.3.1 and >= 1.4. # ``core_filters`` is no longer a valid kwarg as of Django 1.4, and the # class returned by ``create_many_related_manager`` now expects # ``through`` to be explicitly set. Instead of providing different # kwargs based on the version, we set them here. manager.core_filters = { '%s__pk' % self.field.related_query_name(): instance._get_pk_val(), } manager.through = self.field.rel.through # Set this after the fact because AuthorsManager is the superclass and # RelatedManager doesn't know about these attributes manager.override_field_name = self.field.override_field_name manager.extra_field_name = self.field.extra_field_name return manager
def __get__(self, instance, instance_type=None): if not instance: return self RelatedManager = create_many_related_manager(AuthorsManager, self.field.rel) core_filters = { '%s__pk' % self.field.related_query_name(): instance._get_pk_val(), } manager = RelatedManager( model=self.field.rel.to, core_filters=core_filters, instance=instance, symmetrical=self.field.rel.symmetrical, source_field_name=self.field.m2m_field_name(), target_field_name=self.field.m2m_reverse_field_name(), reverse=False, ) # Set this after the fact because AuthorsManager is the superclass and # RelatedManager doesn't know about these attributes manager.override_field_name = self.field.override_field_name manager.extra_field_name = self.field.extra_field_name return manager
def get_rel_manager(self, instance): rel_model = self.related.model superclass = rel_model._default_manager.__class__ RelatedManager = create_many_related_manager(superclass, self.related.field.rel) return RelatedManager( model=rel_model, core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()}, instance=instance, symmetrical=False, source_field_name=self.related.field.m2m_reverse_field_name(), target_field_name=self.related.field.m2m_field_name(), reverse=True )
def __get__(self, instance, instance_type=None): if instance is None: return self # Dynamically create a class that subclasses the related # model's default manager. rel_model=self.field.rel.to superclass = rel_model._default_manager.__class__ RelatedManager = related.create_many_related_manager(superclass, self.field.rel.through) class VersionsRelatedManager(RelatedManager): def add(self, *args, **kwargs): revision.stage_related_updates(self.related_model_instance, self.related_model_attname, 'add', args) if self.related_model_instance._versions_status == VERSIONS_STATUS_PUBLISHED: super(VersionsRelatedManager, self).add(*args, **kwargs) def remove(self, *args, **kwargs): revision.stage_related_updates(self.related_model_instance, self.related_model_attname, 'remove', args) if self.related_model_instance._versions_status == VERSIONS_STATUS_PUBLISHED: super(VersionsRelatedManager, self).remove(*args, **kwargs) def clear(self, *args, **kwargs): revision.stage_related_updates(self.related_model_instance, self.related_model_attname, 'clear') if self.related_model_instance._versions_status == VERSIONS_STATUS_PUBLISHED: super(VersionsRelatedManager, self).clear(*args, **kwargs) def get_query_set(self, *args, **kwargs): rev = kwargs.get('rev', None) bypass_filter = kwargs.get('bypass_filter', False) if self.related_model_instance is not None: rev = rev and rev or self.related_model_instance._versions_revision if rev is not None and not bypass_filter: self.core_filters = {'pk__in': revision.get_related_object_ids(self.related_model_instance, self.related_model_attname, rev)} return super(VersionsRelatedManager, self).get_query_set(*args, **kwargs) qn = connection.ops.quote_name manager = VersionsRelatedManager( model=rel_model, core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()}, instance=instance, symmetrical=(self.field.rel.symmetrical and isinstance(instance, rel_model)), join_table=qn(self.field.m2m_db_table()), source_col_name=qn(self.field.m2m_column_name()), target_col_name=qn(self.field.m2m_reverse_name()) ) manager.model_attname = self.field.related.get_accessor_name() manager.related_model_instance = instance manager.related_model_attname = self.field.attname return manager
def get_rel_manager(self, instance): rel_model = self.related.model superclass = rel_model._default_manager.__class__ RelatedManager = create_many_related_manager(superclass, self.related.field.rel) return RelatedManager( model=rel_model, core_filters={ '%s__pk' % self.related.field.name: instance._get_pk_val() }, instance=instance, symmetrical=False, source_field_name=self.related.field.m2m_reverse_field_name(), target_field_name=self.related.field.m2m_field_name(), reverse=True)
def __get__(self, instance, instance_type=None): if not instance: return self RelatedManager = create_many_related_manager(AuthorsManager, self.field.rel) kwargs = { 'model': self.field.rel.to, 'instance': instance, 'symmetrical': self.field.rel.symmetrical, 'source_field_name': self.field.m2m_field_name(), 'target_field_name': self.field.m2m_reverse_field_name(), 'reverse': False, 'through': self.field.rel.through } try: manager = RelatedManager(**kwargs) except TypeError: # Django 1.3.x does not have a through kwarg, so try # removing it and instantiating it one more time. del kwargs['through'] manager = RelatedManager(**kwargs) # These two attributes are set after the instance is created to # maintain compatibility between Django 1.3.1 and >= 1.4. # ``core_filters`` is no longer a valid kwarg as of Django 1.4, and the # class returned by ``create_many_related_manager`` now expects # ``through`` to be explicitly set. Instead of providing different # kwargs based on the version, we set them here. manager.core_filters = { '%s__pk' % self.field.related_query_name(): instance._get_pk_val(), } manager.through = self.field.rel.through # Set this after the fact because AuthorsManager is the superclass and # RelatedManager doesn't know about these attributes manager.override_field_name = self.field.override_field_name manager.extra_field_name = self.field.extra_field_name return manager
def __get__(self, instance, instance_type=None): if not instance: return self RelatedManager = create_many_related_manager(AuthorsManager, self.field.rel) kwargs = { 'model': self.field.rel.to, 'instance': instance, 'symmetrical': self.field.rel.symmetrical, 'source_field_name': self.field.m2m_field_name(), 'target_field_name': self.field.m2m_reverse_field_name(), 'reverse': False, 'through': self.field.rel.through } try: manager = RelatedManager(**kwargs) except TypeError: # Django 1.3.x does not have a through kwarg, so try # removing it and instantiating it one more time. del kwargs['through'] manager = RelatedManager(**kwargs) # These two attributes are set after the instance is created to # maintain compatibility between Django 1.3.1 and >= 1.4. # ``core_filters`` is no longer a valid kwarg as of Django 1.4, and the # class returned by ``create_many_related_manager`` now expects # ``through`` to be explicitly set. Instead of providing different # kwargs based on the version, we set them here. manager.core_filters = { '%s__pk' % self.field.related_query_name(): instance._get_pk_val(), } manager.through = self.field.rel.through # Set this after the fact because AuthorsManager is the superclass and # RelatedManager doesn't know about these attributes manager.override_field_name = self.field.override_field_name manager.extra_field_name = self.field.extra_field_name return manager
def create_sorted_many_related_manager(superclass, rel): RelatedManager = create_many_related_manager(superclass, rel) class SortedRelatedManager(RelatedManager): def get_queryset(self): # We use ``extra`` method here because we have no other access to # the extra sorting field of the intermediary model. The fields # are hidden for joins because we set ``auto_created`` on the # intermediary's meta options. try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): return super(SortedRelatedManager, self).\ get_query_set().\ extra(order_by=['%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, )]) if not hasattr(RelatedManager, '_get_fk_val'): @property def _fk_val(self): return self.related_val[0] def get_prefetch_queryset(self, instances): # mostly a copy of get_prefetch_query_set from ManyRelatedManager # but with addition of proper ordering db = self._db or router.db_for_read(instances[0].__class__, instance=instances[0]) query = {'%s__pk__in' % self.query_field_name: set(obj._get_pk_val() for obj in instances)} qs = super(RelatedManager, self).get_query_set().using(db)._next_is_sticky().filter(**query) # M2M: need to annotate the query in order to get the primary model # that the secondary model was actually related to. We know that # there will already be a join on the join table, so we can just add # the select. # For non-autocreated 'through' models, can't assume we are # dealing with PK values. fk = self.through._meta.get_field(self.source_field_name) source_col = fk.column join_table = self.through._meta.db_table connection = connections[db] qn = connection.ops.quote_name qs = qs.extra(select={'_prefetch_related_val': '%s.%s' % (qn(join_table), qn(source_col))}, order_by=['%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, )]) select_attname = fk.rel.get_related_field().get_attname() return (qs, attrgetter('_prefetch_related_val'), attrgetter(select_attname), False, self.prefetch_cache_name) 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 Model if objs: 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) vals = self.through._default_manager.using(db).values_list(target_field_name, flat=True) vals = vals.filter(**{ source_field_name: self.related_val[0], '%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 sort_field_name = self.through._sort_field_name sort_field = self.through._meta.get_field_by_name(sort_field_name)[0] for obj_id in new_ids: self.through._default_manager.using(db).create(**{ '%s_id' % source_field_name: self._fk_val, # Django 1.5 compatibility '%s_id' % target_field_name: obj_id, sort_field_name: sort_field.get_default(), }) 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
def create_sorted_many_related_manager(superclass, rel): RelatedManager = create_many_related_manager(superclass, rel) class SortedRelatedManager(RelatedManager): def get_query_set(self): # We use ``extra`` method here because we have no other access to # the extra sorting field of the intermediary model. The fields # are hidden for joins because we set ``auto_created`` on the # intermediary's meta options. try: return self.instance._prefetched_objects_cache[ self.prefetch_cache_name] except (AttributeError, KeyError): return super(SortedRelatedManager, self).\ get_query_set().\ extra(order_by=['%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, )]) if not hasattr(RelatedManager, '_get_fk_val'): @property def _fk_val(self): return self._pk_val def get_prefetch_query_set(self, instances): # mostly a copy of get_prefetch_query_set from ManyRelatedManager # but with addition of proper ordering db = self._db or router.db_for_read(instances[0].__class__, instance=instances[0]) query = { '%s__pk__in' % self.query_field_name: set(obj._get_pk_val() for obj in instances) } qs = super( RelatedManager, self).get_query_set().using(db)._next_is_sticky().filter( **query) # M2M: need to annotate the query in order to get the primary model # that the secondary model was actually related to. We know that # there will already be a join on the join table, so we can just add # the select. # For non-autocreated 'through' models, can't assume we are # dealing with PK values. fk = self.through._meta.get_field(self.source_field_name) source_col = fk.column join_table = self.through._meta.db_table connection = connections[db] qn = connection.ops.quote_name qs = qs.extra(select={ '_prefetch_related_val': '%s.%s' % (qn(join_table), qn(source_col)) }, order_by=[ '%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, ) ]) select_attname = fk.rel.get_related_field().get_attname() return (qs, attrgetter('_prefetch_related_val'), attrgetter(select_attname), False, self.prefetch_cache_name) 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 Model if objs: 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) vals = self.through._default_manager.using(db).values_list( target_field_name, flat=True) vals = vals.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 sort_field_name = self.through._sort_field_name sort_field = self.through._meta.get_field_by_name( sort_field_name)[0] for obj_id in new_ids: self.through._default_manager.using(db).create( **{ '%s_id' % source_field_name: self._fk_val, # Django 1.5 compatibility '%s_id' % target_field_name: obj_id, sort_field_name: sort_field.get_default(), }) 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
site__pk=settings.SITE_ID) def blocking(self): return self.get_relationships(RelationshipStatus.objects.blocking()) def blockers(self): return self.get_related_to(RelationshipStatus.objects.blocking()) def friends(self): return self.get_relationships(RelationshipStatus.objects.following(), True) if django.VERSION < (1, 2): RelatedManager = create_many_related_manager(RelationshipManager, Relationship) class RelationshipsDescriptor(object): def __get__(self, instance, instance_type=None): qn = connection.ops.quote_name manager = RelatedManager( model=User, core_filters={'related_to__pk': instance._get_pk_val()}, instance=instance, symmetrical=False, join_table=qn('relationships_relationship'), source_col_name=qn('from_user_id'), target_col_name=qn('to_user_id'), ) return manager
def create_sorted_many_related_manager(superclass, rel): RelatedManager = create_many_related_manager(superclass, rel) class SortedRelatedManager(RelatedManager): def get_queryset(self): # We use ``extra`` method here because we have no other access to # the extra sorting field of the intermediary model. The fields # are hidden for joins because we set ``auto_created`` on the # intermediary's meta options. try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): # Django 1.5 support. if not hasattr(RelatedManager, 'get_queryset'): queryset = super(SortedRelatedManager, self).get_query_set() else: queryset = super(SortedRelatedManager, self).get_queryset() return queryset.extra(order_by=['%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, )]) get_query_set = get_queryset if not hasattr(RelatedManager, '_get_fk_val'): @property def _fk_val(self): # Django 1.5 support. if not hasattr(self, 'related_val'): return self._pk_val return self.related_val[0] def get_prefetch_queryset(self, instances, queryset=None): # Django 1.5 support. The method name changed since. if django.VERSION < (1, 6): result = super(SortedRelatedManager, self).get_prefetch_query_set(instances) # Django 1.6 support. The queryset parameter was not supported. elif django.VERSION < (1, 7): result = super(SortedRelatedManager, self).get_prefetch_queryset(instances) else: result = super(SortedRelatedManager, self).get_prefetch_queryset(instances, queryset) queryset = result[0] queryset.query.extra_order_by = [ '%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, )] return (queryset,) + result[1:] get_prefetch_query_set = get_prefetch_queryset 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 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) vals = (self.through._default_manager.using(db) .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) with atomic(using=db, savepoint=False): 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 sort_field_name = self.through._sort_field_name sort_field = self.through._meta.get_field_by_name(sort_field_name)[0] for obj_id in new_ids: self.through._default_manager.using(db).create(**{ '%s_id' % source_field_name: self._fk_val, # Django 1.5 compatibility '%s_id' % target_field_name: obj_id, sort_field_name: sort_field.get_default(), }) 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
def create_sorted_many_related_manager(superclass, rel,rev=False): RelatedManager = create_many_related_manager(superclass, rel) class SortedRelatedManager(RelatedManager): def get_query_set(self): # We use ``extra`` method here because we have no other access to # the extra sorting field of the intermediary model. The fields # are hidden for joins because we set ``auto_created`` on the # intermediary's meta options. return super(SortedRelatedManager, self).\ get_query_set().\ extra(order_by=['%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name if not rev else rel.through._rev_sort_field_name, )]) if not hasattr(RelatedManager, '_get_fk_val'): @property def _fk_val(self): return self._pk_val 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. if objs: 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) vals = self.through._default_manager.using(db).values_list(target_field_name, flat=True) vals = vals.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 sort_field_name = self.through._sort_field_name rev_sort_field_name = self.through._rev_sort_field_name #sort_field = self.through._meta.get_field_by_name(sort_field_name)[0] for obj_id in new_ids: self.through._default_manager.using(db).create(**{ '%s_id' % source_field_name: self._fk_val, # Django 1.5 compatibility '%s_id' % target_field_name: obj_id, sort_field_name: self.get_sort_order_value(self._fk_val,obj_id,rev), rev_sort_field_name: self.get_sort_order_value(self._fk_val,obj_id, not rev), }) 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 get_sort_order_value(self,fk,pk,is_rev): from django.db.models import Max model=self.through return (model.objects.filter(**{ '%s_id' % self.source_field_name if not is_rev else self.target_field_name:fk if not is_rev else pk }).aggregate(max_order=Max(self.through._sort_field_name if rev == is_rev else self.through._rev_sort_field_name))["max_order"] or 0)+1 def insert(self,objOrObjs,index): """ Function doc @param PARAM: objOrObjs=要插入的元素或元素列表或元素Queryset,index=插入的位置 @return RETURN: """ from django.db.models.query import QuerySet if isinstance(objOrObjs,QuerySet): objs=list(objOrObjs) elif isinstance(objOrObjs,list): objs=objOrObjs elif isinstance(objOrObjs,self.model): objs=[objOrObjs] else: raise TypeError("'%s' instance expected, got %r" % (self.model._meta.object_name, objOrObjs)) length=len(objs) model=self.through queryset=model.objects.filter(**{ self.source_field_name+"_id":self._fk_val }) count=queryset.count() assert index<=count and index>=0,"index must between 0 to query count" if index==count: return self.add(*objs) else: trailItem=list(queryset[index:]) model.objects.filter(pk__in=[item.pk for item in trailItem]).delete() self.add(*objs) sort_field=model._sort_field_name if not rev else model._rev_sort_field_name for item in trailItem: item.__setattr__( sort_field,item.__getattribute__(sort_field)+length ) model.objects.bulk_create(trailItem) def move(self,obj,vector): """move element order,vector can be integer,+1 is move one postion forward,-1 is move one position backward""" model=self.through #bridge=model.get(**{ #self.source_field_name+"_id":self._fk_val, #self.target_field_name+"_id":obj.pk #}) all_bridges=model.objects.filter(**{ self.source_field_name+"_id":self._fk_val }) all_bridges_list=list(all_bridges) finded=False for obj_index,bridge in enumerate(all_bridges_list): if bridge.__getattribute__(self.target_field_name+"_id")==obj.pk: finded=True break assert finded,"obj not add in item" #obj_index=all_bridges_list.index(bridge) target_index=obj_index+vector assert 0<=target_index<len(all_bridges_list),"move index must between items" start=min(obj_index,target_index) end=max(obj_index,target_index) if vector>0: start+=1 offset=-1 new_bridge_index=end elif vector<0: end-=1 offset=1 new_bridge_index=start else: return middle_bridge=all_bridges_list[start:end+1] sort_field=model._sort_field_name if not rev else model._rev_sort_field_name model.objects.filter(pk__in=[item.pk for item in middle_bridge]+[bridge.pk]).delete() bridge.__setattr__( sort_field, all_bridges_list[new_bridge_index].__getattribute__(sort_field) ) for item in middle_bridge: item.__setattr__( sort_field,item.__getattribute__(sort_field)+offset ) model.objects.bulk_create(middle_bridge+[bridge]) return return SortedRelatedManager
def create_many_related_history_manager(superclass, rel): baseManagerClass = create_many_related_manager(superclass, rel) class ManyToManyHistoryThroughManager(baseManagerClass): # time of altering transaction time = None @property def db(self): return router.db_for_write(self.through, instance=self.instance) @property def versions(self): return ManyToManyHistoryVersion.objects.filter( content_type=ContentType.objects.get_for_model(self.instance), object_id=self.instance.pk, field_name=self.prefetch_cache_name) def get_time(self): if not self.time: self.time = timezone.now() return self.time def last_update_time(self): # TODO: refactor and optimize this method to one query qs = self.get_queryset_through() try: time_to = qs.exclude(time_to=None).order_by('-time_to')[0].time_to time_from = qs.exclude(time_from=None).order_by('-time_from')[0].time_from return time_to if time_to > time_from else time_from except IndexError: return qs.exclude(time_from=None).order_by('-time_from')[0].time_from def _prepare_queryset(self, qs, only_pk=False, unique=True): qs = qs.values_list(self.target_field_name, flat=True) if not only_pk: if unique is False: raise ValueError("Argument `unique` should be True if argument only_pk is False") method_name = 'get_queryset' if django.VERSION[:2] >= (1, 7) else 'get_query_set' qs = getattr(super(ManyToManyHistoryThroughManager, self), method_name)().using( self.db).filter(pk__in=qs) if unique: qs = qs.distinct() return qs def get_query_set(self, **kwargs): warnings.warn("Backward compatibility. Use get_queryset() instead", DeprecationWarning) return self.get_queryset(**kwargs) def get_query_set_through(self): warnings.warn("Backward compatibility. Use get_queryset_through() instead", DeprecationWarning) return self.get_queryset_through() @property def queryset_through(self): return self.get_queryset_through() def get_queryset_through(self): qs = self.through._default_manager.using(self.db).filter(**{ self.source_field_name: self._fk_val, }) return qs def get_queryset(self, **kwargs): qs = self.get_queryset_through().filter(time_to=None) return self._prepare_queryset(qs, **kwargs) def were_between(self, time_from, time_to, **kwargs): if time_to <= time_from: raise ValueError('Argument time_to should be later, than time_from') qs = self.get_queryset_through().filter( Q(time_from=None, time_to=None) | Q(time_from=None, time_to__gte=time_to) | Q(time_from__lte=time_from, time_to=None) | Q(time_from__gte=time_from, time_to__lte=time_to) | Q(time_from__lte=time_from, time_to__gte=time_to) | Q(time_from__lt=time_to, time_to__gt=time_from)) return self._prepare_queryset(qs, **kwargs) def added_between(self, time_from, time_to, **kwargs): if time_to <= time_from: raise ValueError('Argument time_to should be later, than time_from') qs = self.get_queryset_through().filter(time_from__gte=time_from, time_from__lte=time_to) return self._prepare_queryset(qs, **kwargs) def removed_between(self, time_from, time_to, **kwargs): if time_to <= time_from: raise ValueError('Argument time_to should be later, than time_from') qs = self.get_queryset_through().filter(time_to__gte=time_from, time_to__lte=time_to) return self._prepare_queryset(qs, **kwargs) def were_at(self, time, **kwargs): qs = self.get_queryset_through().filter( Q(time_from=None, time_to=None) | Q(time_from=None, time_to__gt=time) | Q(time_from__lte=time, time_to=None) | Q(time_from__lte=time, time_to__gt=time)) return self._prepare_queryset(qs, **kwargs) def added_at(self, time, **kwargs): qs = self.get_queryset_through().filter(time_from=time) return self._prepare_queryset(qs, **kwargs) def removed_at(self, time, **kwargs): qs = self.get_queryset_through().filter(time_to=time) return self._prepare_queryset(qs, **kwargs) def clear(self, *objs): self._clear_items(self.source_field_name, self.target_field_name, *objs) # If this is a symmetrical m2m relation to self, clear the mirror entry in the m2m table if self.symmetrical: self._clear_items(self.target_field_name, self.source_field_name, *objs) clear.alters_data = True def send_signal(self, source_field_name, action, 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=self.through, action=action, instance=self.instance, reverse=self.reverse, model=self.model, pk_set=ids, using=self.db) m2m_history_changed.send(sender=self.through, action=action, instance=self.instance, reverse=self.reverse, model=self.model, pk_set=ids, using=self.db, field_name=self.prefetch_cache_name, time=self.get_time()) def get_set_of_values(self, objs, target_field_name, check_values=False): values = set() # Check that all the objects are of the right type for obj in objs: if isinstance(obj, self.model): if check_values and 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._get_fk_val(obj, target_field_name) if check_values and fk_val is None: raise ValueError('Cannot add "%r": the value for field "%s" is None' % (obj, target_field_name)) values.add(fk_val) elif isinstance(obj, models.Model): raise TypeError("'%s' instance expected, got %r" % (self.model._meta.object_name, obj)) else: values.add(obj) return values 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. if objs: new_ids = self.get_set_of_values(objs, target_field_name, check_values=True) current_ids = self.through._default_manager.using(self.db) \ .values_list(target_field_name, flat=True) \ .filter(**{ source_field_name: self._fk_val, '%s__in' % target_field_name: new_ids, 'time_to': None, }) # remove current from new, otherwise integrity error while bulk_create new_ids = new_ids.difference(set(current_ids)) self.send_signal(source_field_name, 'pre_add', new_ids) # Add the ones that aren't there already self.through._default_manager.using(self.db).bulk_create([ self.through(**{ '%s_id' % source_field_name: self._fk_val, '%s_id' % target_field_name: obj_id, 'time_from': self.get_time(), }) for obj_id in new_ids ]) self.send_signal(source_field_name, 'post_add', new_ids) def _remove_items(self, source_field_name, target_field_name, *objs): # source_field_name: the PK colname in join table for the source object # target_field_name: the PK colname in join table for the target object # *objs - objects to remove # If there aren't any objects, there is nothing to do. if objs: old_ids = self.get_set_of_values(objs, target_field_name) self.send_signal(source_field_name, 'pre_remove', old_ids) # Remove the specified objects from the join table qs = self.through._default_manager.using(self.db).filter(**{ source_field_name: self._fk_val, 'time_to': None, '%s__in' % target_field_name: old_ids, }) qs.update(time_to=self.get_time()) self.send_signal(source_field_name, 'post_remove', old_ids) def _clear_items(self, source_field_name, target_field_name, *objs): # source_field_name: the PK colname in join table for the source object # target_field_name: the PK colname in join table for the target object # *objs - objects to clear new_ids = self.get_set_of_values(objs, target_field_name, check_values=True) current_ids = self.through._default_manager.using(self.db) \ .values_list(target_field_name, flat=True) \ .filter(**{ source_field_name: self._fk_val, 'time_to': None, }) old_ids = set(current_ids).difference(new_ids) self.send_signal(source_field_name, 'pre_clear', old_ids) qs = self.through._default_manager.using(self.db).filter(**{ source_field_name: self._fk_val, 'time_to': None, '%s__in' % target_field_name: old_ids, }) qs.update(time_to=self.get_time()) self.send_signal(source_field_name, 'post_clear', set(self.removed_at(self.get_time(), only_pk=True))) # compatibility with Django 1.7 if django.VERSION[:2] >= (1, 7): @property def _fk_val(self): return self.related_val[0] def _get_fk_val(self, obj, target_field_name): return self.through._meta.get_field(target_field_name).get_foreign_related_value(obj)[0] return ManyToManyHistoryThroughManager
def create_sorted_many_related_manager(superclass, rel): RelatedManager = create_many_related_manager(superclass, rel) class SortedRelatedManager(RelatedManager): def get_queryset(self): # We use ``extra`` method here because we have no other access to # the extra sorting field of the intermediary model. The fields # are hidden for joins because we set ``auto_created`` on the # intermediary's meta options. try: return self.instance._prefetched_objects_cache[ self.prefetch_cache_name] except (AttributeError, KeyError): # Django 1.5 support. if not hasattr(RelatedManager, 'get_queryset'): queryset = super(SortedRelatedManager, self).get_query_set() else: queryset = super(SortedRelatedManager, self).get_queryset() return queryset.extra(order_by=[ '%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, ) ]) get_query_set = get_queryset if not hasattr(RelatedManager, '_get_fk_val'): @property def _fk_val(self): # Django 1.5 support. if not hasattr(self, 'related_val'): return self._pk_val return self.related_val[0] def get_prefetch_queryset(self, instances, queryset=None): # Django 1.5 support. The method name changed since. if django.VERSION < (1, 6): result = super(SortedRelatedManager, self).get_prefetch_query_set(instances) # Django 1.6 support. The queryset parameter was not supported. elif django.VERSION < (1, 7): result = super(SortedRelatedManager, self).get_prefetch_queryset(instances) else: result = super(SortedRelatedManager, self).get_prefetch_queryset( instances, queryset) queryset = result[0] queryset.query.extra_order_by = [ '%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, ) ] return (queryset, ) + result[1:] get_prefetch_query_set = get_prefetch_queryset 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 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) vals = (self.through._default_manager.using(db).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 sort_field_name = self.through._sort_field_name sort_field = self.through._meta.get_field_by_name( sort_field_name)[0] if django.VERSION < (1, 6): for obj_id in new_ids: self.through._default_manager.using(db).create( **{ '%s_id' % source_field_name: self._fk_val, # Django 1.5 compatibility '%s_id' % target_field_name: obj_id, sort_field_name: sort_field.get_default(), }) else: with transaction.atomic(): sort_field_default = sort_field.get_default() self.through._default_manager.using(db).bulk_create([ self.through( **{ '%s_id' % source_field_name: self._fk_val, '%s_id' % target_field_name: v, sort_field_name: sort_field_default + i, }) for i, v 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
class GenericManyRelatedManager(create_many_related_manager(superclass)): def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None, join_table=None, source_col_name=None, target_col_name=None, content_type=None, content_type_field_name=None, object_id_field_name=None): super(GenericManyRelatedManager, self).__init__(model, core_filters, instance, symmetrical, join_table, source_col_name, target_col_name) self.content_type = content_type self.content_type_field_name = content_type_field_name self.object_id_field_name = object_id_field_name def _add_items(self, source_col_name, target_col_name, *objs): # join_table: name of the m2m link table # source_col_name: the PK colname in join_table for the source object # target_col_name: the PK colname 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. if objs: # Check that all the objects are of the right type new_ids = set() for obj in objs: if isinstance(obj, self.model): new_ids.add(obj._get_pk_val()) else: new_ids.add(obj) # Add the newly created or already existing objects to the join table. # First find out which items are already added, to avoid adding them twice cursor = connection.cursor() cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s = %%s AND %s IN (%s)" % \ (target_col_name, self.join_table, self.content_type_field_name, source_col_name, target_col_name, ",".join(['%s'] * len(new_ids))), [content_type.id, self._pk_val] + list(new_ids)) existing_ids = set([row[0] for row in cursor.fetchall()]) # Add the ones that aren't there already for obj_id in (new_ids - existing_ids): cursor.execute("INSERT INTO %s (%s, %s, %s) VALUES (%%s, %%s)" % \ (self.join_table, source_col_name, target_col_name), [content_type.id, self._pk_val, obj_id]) transaction.commit_unless_managed() def _remove_items(self, source_col_name, target_col_name, *objs): # source_col_name: the PK colname in join_table for the source object # target_col_name: the PK colname in join_table for the target object # *objs - objects to remove # If there aren't any objects, there is nothing to do. if objs: # Check that all the objects are of the right type old_ids = set() for obj in objs: if isinstance(obj, self.model): old_ids.add(obj._get_pk_val()) else: old_ids.add(obj) # Remove the specified objects from the join table cursor = connection.cursor() cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s AND %s IN (%s)" % \ (self.join_table, self.content_type_field_name, source_col_name, target_col_name, ",".join(['%s'] * len(old_ids))), [content_type.id, self._pk_val] + list(old_ids)) transaction.commit_unless_managed() def _clear_items(self, source_col_name): # source_col_name: the PK colname in join_table for the source object cursor = connection.cursor() cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \ (self.join_table, self.content_type_field_name, source_col_name), [content_type.id, self._pk_val]) transaction.commit_unless_managed()
def __get__(self, instance, instance_type=None): if instance is None: return self # Dynamically create a class that subclasses the related # model's default manager. rel_model = self.field.rel.to superclass = rel_model._default_manager.__class__ RelatedManager = related.create_many_related_manager( superclass, self.field.rel.through) class VersionsRelatedManager(RelatedManager): def add(self, *args, **kwargs): revision.stage_related_updates(self.related_model_instance, self.related_model_attname, 'add', args) if self.related_model_instance._versions_status == VERSIONS_STATUS_PUBLISHED: super(VersionsRelatedManager, self).add(*args, **kwargs) def remove(self, *args, **kwargs): revision.stage_related_updates(self.related_model_instance, self.related_model_attname, 'remove', args) if self.related_model_instance._versions_status == VERSIONS_STATUS_PUBLISHED: super(VersionsRelatedManager, self).remove(*args, **kwargs) def clear(self, *args, **kwargs): revision.stage_related_updates(self.related_model_instance, self.related_model_attname, 'clear') if self.related_model_instance._versions_status == VERSIONS_STATUS_PUBLISHED: super(VersionsRelatedManager, self).clear(*args, **kwargs) def get_query_set(self, *args, **kwargs): rev = kwargs.get('rev', None) bypass_filter = kwargs.get('bypass_filter', False) if self.related_model_instance is not None: rev = rev and rev or self.related_model_instance._versions_revision if rev is not None and not bypass_filter: self.core_filters = { 'pk__in': revision.get_related_object_ids( self.related_model_instance, self.related_model_attname, rev) } return super(VersionsRelatedManager, self).get_query_set(*args, **kwargs) qn = connection.ops.quote_name manager = VersionsRelatedManager( model=rel_model, core_filters={ '%s__pk' % self.field.related_query_name(): instance._get_pk_val() }, instance=instance, symmetrical=(self.field.rel.symmetrical and isinstance(instance, rel_model)), join_table=qn(self.field.m2m_db_table()), source_col_name=qn(self.field.m2m_column_name()), target_col_name=qn(self.field.m2m_reverse_name())) manager.model_attname = self.field.related.get_accessor_name() manager.related_model_instance = instance manager.related_model_attname = self.field.attname return manager
def create_sorted_many_related_manager(superclass, rel): RelatedManager = create_many_related_manager(superclass, rel) class SortedRelatedManager(RelatedManager): def get_query_set(self): # We use ``extra`` method here because we have no other access to # the extra sorting field of the intermediary model. The fields # are hidden for joins because we set ``auto_created`` on the # intermediary's meta options. return super(SortedRelatedManager, self).\ get_query_set().\ extra(order_by=['%s.%s' % ( rel.through._meta.db_table, rel.through._sort_field_name, )]) def _add_items(self, source_field_name, target_field_name, *objs): # join_table: name of the m2m link table # 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 Model if objs: 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)) new_ids.append(obj.pk) elif isinstance(obj, Model): raise TypeError("'%s' instance expected" % self.model._meta.object_name) else: new_ids.append(obj) db = router.db_for_write(self.through.__class__, instance=self.instance) vals = self.through._default_manager.using(db).values_list(target_field_name, flat=True) vals = vals.filter(**{ source_field_name: self._pk_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) # Add the ones that aren't there already sort_field_name = self.through._sort_field_name sort_field = self.through._meta.get_field_by_name(sort_field_name)[0] for obj_id in new_ids: self.through._default_manager.using(db).create(**{ '%s_id' % source_field_name: self._pk_val, '%s_id' % target_field_name: obj_id, sort_field_name: sort_field.get_default(), }) 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) return SortedRelatedManager
def get_relation(self, user): """ Return relation between users if there is not relation returns false """ try: following = Followers.objects.get( from_user = self.instance, to_user = user, ) return following except Followers.DoesNotExist: return False if django.VERSION < (1, 2): RelatedManager = create_many_related_manager(FollowingManager, Followers) class FollowersDescriptor(object): def __get__(self, instance, instance_type=None): qn = connection.ops.quote_name manager = RelatedManager( model=User, core_filters={'related_to__pk': instance._get_pk_val()}, instance=instance, symmetrical=False, join_table=qn('followers_followers'), source_col_name=qn('from_user_id'), target_col_name=qn('to_user_id'), ) return manager
def create_versioned_many_related_manager(superclass, rel): """ The "casting" which is done in this method is needed, since otherwise, the methods introduced by Versionable are not taken into account. :param superclass: This is usually a models.Manager :param rel: Contains the ManyToMany relation :return: A subclass of ManyRelatedManager and Versionable """ many_related_manager_klass = create_many_related_manager(superclass, rel) class VersionedManyRelatedManager(many_related_manager_klass): def __init__(self, *args, **kwargs): super(VersionedManyRelatedManager, self).__init__(*args, **kwargs) # Additional core filters are: version_start_date <= t & (version_end_date > t | version_end_date IS NULL) # but we cannot work with the Django core filters, since they don't support ORing filters, which # is a thing we need to consider the "version_end_date IS NULL" case; # So, we define our own set of core filters being applied when versioning try: version_start_date_field = self.through._meta.get_field('version_start_date') version_end_date_field = self.through._meta.get_field('version_end_date') except (FieldDoesNotExist) as e: print str(e) + "; available fields are " + ", ".join(self.through._meta.get_all_field_names()) raise e # FIXME: this probably does not work when auto-referencing def get_queryset(self): """ Add a filter to the queryset, limiting the results to be pointed by relationship that are valid for the given timestamp (which is taken at the current instance, or set to now, if not available). Long story short, apply the temporal validity filter also to the intermediary model. """ queryset = super(VersionedManyRelatedManager, self).get_queryset() if self.instance.as_of is not None: queryset = queryset.as_of(self.instance.as_of) return queryset.propagate_querytime(self.through._meta.db_table) def _remove_items(self, source_field_name, target_field_name, *objs): """ Instead of removing items, we simply set the version_end_date of the current item to the current timestamp --> t[now]. Like that, there is no more current entry having that identity - which is equal to not existing for timestamps greater than t[now]. """ return self._remove_items_at(None, source_field_name, target_field_name, *objs) def _remove_items_at(self, timestamp, source_field_name, target_field_name, *objs): if objs: if timestamp is None: timestamp = get_utc_now() old_ids = set() for obj in objs: if isinstance(obj, self.model): # The Django 1.7-way is preferred if hasattr(self, 'target_field'): fk_val = self.target_field.get_foreign_related_value(obj)[0] # But the Django 1.6.x -way is supported for backward compatibility elif hasattr(self, '_get_fk_val'): fk_val = self._get_fk_val(obj, target_field_name) else: raise TypeError("We couldn't find the value of the foreign key, this might be due to the " "use of an unsupported version of Django") old_ids.add(fk_val) else: old_ids.add(obj) db = router.db_for_write(self.through, instance=self.instance) qs = self.through._default_manager.using(db).filter(**{ source_field_name: self.instance.id, '%s__in' % target_field_name: old_ids }).as_of(timestamp) for relation in qs: relation._delete_at(timestamp) # FIXME: There could potentially be a problem when trying to remove and re-add an item from/to a relationship; see django/db/models/fields/related.py:654-658 if 'add' in dir(many_related_manager_klass): def add_at(self, timestamp, *objs): """ This function adds an object at a certain point in time (timestamp) """ # First off, define the new constructor def _through_init(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.version_birth_date = timestamp self.version_start_date = timestamp # Through-classes have an empty constructor, so it can easily be overwritten when needed; # This is not the default case, so the overwrite only takes place when we "modify the past" self.through.__init_backup__ = self.through.__init__ self.through.__init__ = _through_init # Do the add operation self.add(*objs) # Remove the constructor again (by replacing it with the original empty constructor) self.through.__init__ = self.through.__init_backup__ del self.through.__init_backup__ add_at.alters_data = True if 'remove' in dir(many_related_manager_klass): def remove_at(self, timestamp, *objs): """ Performs the act of removing specified relationships at a specified time (timestamp); So, not the objects at a given time are removed, but their relationship! """ self._remove_items_at(timestamp, self.source_field_name, self.target_field_name, *objs) # For consistency, also handle the symmetrical case if self.symmetrical: self._remove_items_at(timestamp, self.target_field_name, self.source_field_name, *objs) remove_at.alters_data = True return VersionedManyRelatedManager
def get_relation(self, user): """ Return relation between users if there is not relation returns false """ try: following = Followers.objects.get( from_user = self.instance, to_user = user, ) return following except Followers.DoesNotExist: return False if django.VERSION < (1, 2): RelatedManager = create_many_related_manager(FollowingManager, Followers) class FollowersDescriptor(object): def __get__(self, instance, instance_type=None): qn = connection.ops.quote_name manager = RelatedManager( model=User, core_filters={'related_to__pk': instance._get_pk_val()}, instance=instance, symmetrical=False, join_table=qn('followers_followers'), source_col_name=qn('from_user_id'), target_col_name=qn('to_user_id'), ) return manager
def followers(self): return self.get_related_to(RelationshipStatus.objects.following()) def blocking(self): return self.get_relationships(RelationshipStatus.objects.blocking()) def blockers(self): return self.get_related_to(RelationshipStatus.objects.blocking()) def friends(self): return self.get_symmetrical(RelationshipStatus.objects.following()) if django.VERSION < (1, 2): RelatedManager = create_many_related_manager(RelationshipManager, Relationship) class RelationshipsDescriptor(object): def __get__(self, instance, instance_type=None): qn = connection.ops.quote_name manager = RelatedManager( model=User, core_filters={'related_to__pk': instance._get_pk_val()}, instance=instance, symmetrical=False, join_table=qn('relationships_relationship'), source_col_name=qn('from_user_id'), target_col_name=qn('to_user_id'), ) return manager
def create_versioned_many_related_manager(superclass, rel): """ The "casting" which is done in this method is needed, since otherwise, the methods introduced by Versionable are not taken into account. :param superclass: This is usually a models.Manager :param rel: Contains the ManyToMany relation :return: A subclass of ManyRelatedManager and Versionable """ many_related_manager_klass = create_many_related_manager(superclass, rel) class VersionedManyRelatedManager(many_related_manager_klass): def __init__(self, *args, **kwargs): super(VersionedManyRelatedManager, self).__init__(*args, **kwargs) # Additional core filters are: version_start_date <= t & (version_end_date > t | version_end_date IS NULL) # but we cannot work with the Django core filters, since they don't support ORing filters, which # is a thing we need to consider the "version_end_date IS NULL" case; # So, we define our own set of core filters being applied when versioning try: version_start_date_field = self.through._meta.get_field( 'version_start_date') version_end_date_field = self.through._meta.get_field( 'version_end_date') except (FieldDoesNotExist) as e: print( str(e) + "; available fields are " + ", ".join(self.through._meta.get_all_field_names())) raise e # FIXME: this probably does not work when auto-referencing def get_queryset(self): """ Add a filter to the queryset, limiting the results to be pointed by relationship that are valid for the given timestamp (which is taken at the current instance, or set to now, if not available). Long story short, apply the temporal validity filter also to the intermediary model. """ queryset = super(VersionedManyRelatedManager, self).get_queryset() if self.instance.as_of is not None: queryset = queryset.as_of(self.instance.as_of) return queryset.propagate_querytime(self.through._meta.db_table) def _remove_items(self, source_field_name, target_field_name, *objs): """ Instead of removing items, we simply set the version_end_date of the current item to the current timestamp --> t[now]. Like that, there is no more current entry having that identity - which is equal to not existing for timestamps greater than t[now]. """ return self._remove_items_at(None, source_field_name, target_field_name, *objs) def _remove_items_at(self, timestamp, source_field_name, target_field_name, *objs): if objs: if timestamp is None: timestamp = get_utc_now() old_ids = set() for obj in objs: if isinstance(obj, self.model): # The Django 1.7-way is preferred if hasattr(self, 'target_field'): fk_val = self.target_field.get_foreign_related_value( obj)[0] # But the Django 1.6.x -way is supported for backward compatibility elif hasattr(self, '_get_fk_val'): fk_val = self._get_fk_val(obj, target_field_name) else: raise TypeError( "We couldn't find the value of the foreign key, this might be due to the " "use of an unsupported version of Django") old_ids.add(fk_val) else: old_ids.add(obj) db = router.db_for_write(self.through, instance=self.instance) qs = self.through._default_manager.using(db).filter( **{ source_field_name: self.instance.id, '%s__in' % target_field_name: old_ids }).as_of(timestamp) for relation in qs: relation._delete_at(timestamp) # FIXME: There could potentially be a problem when trying to remove and re-add an item from/to a relationship; see django/db/models/fields/related.py:654-658 if 'add' in dir(many_related_manager_klass): def add_at(self, timestamp, *objs): """ This function adds an object at a certain point in time (timestamp) """ # First off, define the new constructor def _through_init(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) self.version_birth_date = timestamp self.version_start_date = timestamp # Through-classes have an empty constructor, so it can easily be overwritten when needed; # This is not the default case, so the overwrite only takes place when we "modify the past" self.through.__init_backup__ = self.through.__init__ self.through.__init__ = _through_init # Do the add operation self.add(*objs) # Remove the constructor again (by replacing it with the original empty constructor) self.through.__init__ = self.through.__init_backup__ del self.through.__init_backup__ add_at.alters_data = True if 'remove' in dir(many_related_manager_klass): def remove_at(self, timestamp, *objs): """ Performs the act of removing specified relationships at a specified time (timestamp); So, not the objects at a given time are removed, but their relationship! """ self._remove_items_at(timestamp, self.source_field_name, self.target_field_name, *objs) # For consistency, also handle the symmetrical case if self.symmetrical: self._remove_items_at(timestamp, self.target_field_name, self.source_field_name, *objs) remove_at.alters_data = True return VersionedManyRelatedManager