如何使用对象列表过滤多个字段

Aam*_*amu 12 django django-queryset

我想构建一个像QuoraMedium这样的webapp ,用户可以在其中关注用户或某些主题.

例如:userA跟随(userB,userC,tag-Health,tag-Finance).

这些是模型:

class Relationship(models.Model):
    user = AutoOneToOneField('auth.user')
    follows_user = models.ManyToManyField('Relationship', related_name='followed_by')
    follows_tag = models.ManyToManyField(Tag)

class Activity(models.Model):
    actor_type = models.ForeignKey(ContentType, related_name='actor_type_activities')
    actor_id = models.PositiveIntegerField()
    actor = GenericForeignKey('actor_type', 'actor_id')
    verb = models.CharField(max_length=10)
    target_type = models.ForeignKey(ContentType, related_name='target_type_activities')
    target_id = models.PositiveIntegerField()
    target = GenericForeignKey('target_type', 'target_id')
    tags = models.ManyToManyField(Tag)
Run Code Online (Sandbox Code Playgroud)

现在,这将给出以下列表:

following_user = userA.relationship.follows_user.all()
following_user
[<Relationship: userB>, <Relationship: userC>]
following_tag = userA.relationship.follows_tag.all()
following_tag
[<Tag: tag-job>, <Tag: tag-finance>]
Run Code Online (Sandbox Code Playgroud)

要过滤我试过这种方式:

Activity.objects.filter(Q(actor__in=following_user) | Q(tags__in=following_tag))
Run Code Online (Sandbox Code Playgroud)

但由于actor是GenericForeignKey,我收到一个错误:

FieldError:字段'actor'不生成自动反向关系,因此不能用于反向查询.如果是GenericForeignKey,请考虑添加GenericRelation.

如何使用用户列表和用户遵循的标记列表过滤唯一的活动?具体来说,我将如何使用对象列表过滤GenericForeignKey以获取以下用户的活动.

Sar*_*iev 6

你应该只按ID过滤.

首先获取要过滤的对象ID

following_user = userA.relationship.follows_user.all().values_list('id', flat=True)
following_tag = userA.relationship.follows_tag.all()
Run Code Online (Sandbox Code Playgroud)

您还需要过滤actor_type.例如,可以这样做.

actor_type = ContentType.objects.get_for_model(userA.__class__)
Run Code Online (Sandbox Code Playgroud)

或者@Todor在评论中建议.因为get_for_model接受模型类和模型实例

actor_type = ContentType.objects.get_for_model(userA)
Run Code Online (Sandbox Code Playgroud)

而且你可以像这样过滤.

Activity.objects.filter(Q(actor_id__in=following_user, actor_type=actor_type) | Q(tags__in=following_tag))
Run Code Online (Sandbox Code Playgroud)


Tod*_*dor 3

他们docs所建议的并不是一件坏事。

问题是,当您创建时,Activities您正在使用auth.Useras actor,因此您无法添加GenericRelationauth.User (也许您可以通过猴子修补它,但这不是一个好主意)

那么你能做什么呢?

@Sardorbek Imomaliev解决方案非常好,如果将所有这些逻辑放入自定义QuerySet类中,您可以使其变得更好。(这个想法是实现 DRY 性和可重用性)

class ActivityQuerySet(models.QuerySet):
    def for_user(self, user):
        return self.filter(
            models.Q(
                actor_type=ContentType.objects.get_for_model(user),
                actor_id__in=user.relationship.follows_user.values_list('id', flat=True)
            )|models.Q(
                tags__in=user.relationship.follows_tag.all()
            )
        )

class Activity(models.Model):
    #..
    objects = ActivityQuerySet.as_manager()

#usage
user_feed = Activity.objects.for_user(request.user)
Run Code Online (Sandbox Code Playgroud)

但还有什么吗?

1.你真的需要GenericForeignKeyactor?我不知道你的业务逻辑,所以你可能知道,但是只使用FKactor 的常规(就像tags)将可以像 那样做工作人员actor__in=users_following

2.您是否检查过是否有相应的应用程序?已经解决您问题的软件包的一个示例是django-activity-steam检查它。

3.如果您不使用auth.Useras ,actor您可以按照docs建议进行操作 -> 添加GenericRelation字段。事实上,您的Relationship课程适合此目的,但我真的会将其重命名为类似UserProfile或至少UserRelation. 考虑一下我们已经重命名RelationUserProfile,并且我们创建了新的Activities使用userprofile。这个想法是:

class UserProfile(models.Model):
    user = AutoOneToOneField('auth.user')
    follows_user = models.ManyToManyField('UserProfile', related_name='followed_by')
    follows_tag = models.ManyToManyField(Tag)

    activies_as_actor = GenericRelation('Activity',
        content_type_field='actor_type',
        object_id_field='actor_id',
        related_query_name='userprofile'
    )


class ActivityQuerySet(models.QuerySet):
    def for_userprofile(self, userprofile):
        return self.filter(
            models.Q(
                userprofile__in=userprofile.follows_user.all()
            )|models.Q(
                tags__in=userprofile.relationship.follows_tag.all()
            )
        )


class Activity(models.Model):
    #..
    objects = ActivityQuerySet.as_manager()

#usage

#1st when you create activity use UserProfile
Activity.objects.create(actor=request.user.userprofile, ...)

#2nd when you fetch. 
#Check how `for_userprofile` is implemented this time
Activity.objects.for_userprofile(request.user.userprofile)
Run Code Online (Sandbox Code Playgroud)