Exemplo n.º 1
0
    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
Exemplo n.º 3
0
 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
     )
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
 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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
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
Exemplo n.º 10
0
            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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
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
Exemplo n.º 13
0
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
Exemplo n.º 14
0
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
Exemplo n.º 15
0
    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()
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
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
Exemplo n.º 18
0
    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
Exemplo n.º 19
0
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
Exemplo n.º 20
0
    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
Exemplo n.º 21
0
    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
Exemplo n.º 22
0
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