我创建了一个应用,用户可以在其中发布相关标签:
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
class Post(models.Model):
user = models.ForeignKey(User)
body = models.TextField()
tags = models.ManyToManyField(Tag)
pub_date = models.DateTimeField(default=timezone.now)
activity = GenericRelation(Activity, related_query_name="posts")
class Photo(models.Model):
user = models.ForeignKey(User)
file = models.ImageField()
tags = models.ManyToManyField(Tag)
pub_date = models.DateTimeField(default=timezone.now)
activity = GenericRelation(Activity, related_query_name="photos")
class Activity(models.Model):
actor = models.ForeignKey(User)
verb = models.PositiveIntegerField(choices=VERB_TYPE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
pub_date = models.DateTimeField(default=timezone.now)
我想要做的是获取/过滤最多5个最新/最近的Activity对象,包含用户列表,以及Post对象标签列表中的标签列表,并使用django-rest-framework返回json在客户端查看。
例如活动:
所以说我想用user_list=[UserA, UserC]
和tag_list = [#class, #teacher]
它应该返回:
要使用用户过滤活动,我可以这样查询:
Activity.objects.filter(actor__in=user_list)
但是,如何使用content_object(即Post或Photo)字段(即Post.tags或Photo.tags)过滤Activity?现在我这样做:
Activity.objects.filter(posts__tags__in=tag_l)
Activity.objects.filter(photos__tags__in=tags)
总而言之,如果我需要有用户列表和标签列表的活动,我必须这样做:
activites = Activity.objects.filter(
Q(actor__in=user_list) |
Qposts__tags__in=tag_list) |
Q(photos__tags__in=tag_list)
)
但是假设将有两个以上的ContentType模型类,那么我必须再次添加另一个Q(moreModel__tags__in=tag_list)
。所以,我希望有一种更好的方法来优化这个过程。
答案 0 :(得分:0)
我会从django updown给你一个例子,它有一个类似的模型:https://github.com/weluse/django-updown/blob/master/updown/models.py#L31
>>> from updown.models import Vote
>>> Vote.objects.first()
<Vote: john voted 1 on Conveniently develop impactful e-commerce>
>>> v = Vote.objects.first()
>>> v.content_object
<Thread: Conveniently develop impactful e-commerce>
>>> v.content_object.__class__
<class 'app_forum.models.Thread'>
>>> [ v.content_type for v in Vote.objects.all() if v.content_object.__class__.__name__ == 'Thread' ]
[<Thread: Conveniently develop impactful e-commerce>, <Thread: Quickly evisculate exceptional paradigms>]
>>>
# You can also use with
>>> user_ids = [ u.content_type.id for u in Vote.objects.all() if u.content_object.__class__.__name__ == 'User' ]
>>> user_ids
[1, 52, 3, 4]
>>> from django.contrib.auth.models import User
>>> User.objects.filter(pk__in=user_ids)
[<User: John>, <User: Alex>, <User: Roboto>, <User: Membra>]
>>>
>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.get_for_model(v.content_object)
<ContentType: Detail Thread>
>>>
您也可以使用ContentType.objects.get_for_model(model_instance)
,例如:https://github.com/weluse/django-updown/blob/master/updown/fields.py#L70
在你的问题中,也许可以使用这个..
>>> photo_ids = [ ac.content_type.id for ac in Activity.objects.all() if ac.content_object.__class__.__name__ == 'Photo' ]
>>> Activity.objects.filter(content_type__id__in=photo_ids)
# or
>>> photo_ids = [ ac.content_type.id for ac in Activity.objects.all() if content_type.model_class().__name__ == 'Photo']
>>> Activity.objects.filter(content_type__id__in=photo_ids)
希望它可以帮助..
答案 1 :(得分:0)
对于此方法,将related_query_name设置为小写的模型名称或模型的verbose_name。
您可以先过滤出Activity
模型中的内容类型。
content_types = ContentType.objects.filter(activity__id__isnull=False)
现在使用这些内容类型来构建查找。
q = Q(actor__in=user_list)
for content_type in content_types:
arg = content_type.name + '__tags__in'
kwargs = {arg: tag_list}
q = q | Q(**kwargs)
现在,您可以使用此查找过滤活动。
activities = Activity.objects.filter(q).distinct()
答案 2 :(得分:0)
我说你最好的选择是使用Django filter过滤器(链接到其他框架文档),特别是ModelMultipleChoiceFilter
。我假设你已经有ActivityViewSet
与Activity
模型一起使用。
首先,您要创建一个django_filters.FilterSet
,可能在filters.py
等新文件中,并设置ModelMultipleChoiceFilter
,如下所示:
import django_filters
from .models import Activity, Tag, User
class ActivityFilterSet(django_filters.FilterSet):
tags = django_filters.ModelMultipleChoiceFilter(
name='content_object__tags__name',
to_field_name='name',
lookup_type='in',
queryset=Tag.objects.all()
)
users = django_filters.ModelMultipleChoiceFilter(
name='content_object__user__pk',
to_field_name='pk',
lookup_type='in',
queryset=User.objects.all()
)
class Meta:
model = Activity
fields = (
'tags',
'users',
)
然后,您想要告诉您的视图集使用该过滤器:
from .filters import ActivityFilterSet
# ...
class ActivityViewSet(GenericViewSet):
# all your existing declarations, eg.,
# serializer_class = ActivitySerializer
# ...
filter_class = ActivityFilterSet
# ...
完成后,您将能够使用GET参数过滤结果,例如,
GET /activities?users=1
- 用户1 GET /activities?users=1&users=2
- 由用户1或用户2 GET /activities?users=1&tags=class
- 用户1使用标签#Class GET /activities?users=1&users=2&tags=class&tags=school
- 用户1或用户2使用标签#Class或#School