7

я создавать приложения, где пользователи могут оставлять свои Относящиеся теги:фильтр в ContentType со списком поле объекта контента в

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 последних/последних объектов активность, со списком пользователей и списком тегов из списка полей тегов Post objects и return json, используя django-rest-framework для просмотра на стороне клиента.

Eg деятельности:

  • UserA создан новый пост obect с тегами (#class, #school)
  • UserB создан новый пост объект с тегами (#professor, #teacher)
  • UserC создал новый объект Post с тегами (#school, #university)
  • UserD создал новый объект Post с тегами (#university, #school)

Так, я хочу, чтобы фильтровать активность с user_list=[UserA, UserC] и tag_list = [#class, #teacher]

Она должна возвращать:

  • UserA создано новое сообщение obect с тегами (#class, #school)
  • UserC создали новый объект Post с теги (#school, #university)
  • UserB создал новый объект Post с тегами (#professor, #teacher)

Чтобы отфильтровать активность с пользователями, я могу запросить т его путь:

Activity.objects.filter(actor__in=user_list) 

Но, как я фильтровать активность с (i.e.Post или Фото) поле content_object в (т.е. Post.tags или Photo.tags)? Теперь я делаю так:

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

Я дам вам пример из 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) 

Надеется, что это может помочь ..

+0

Ok я погляжу. – Robin

+0

@Robin hello robin, теперь, в моем проекте в настоящее время получилось аналогичное условие, такое как ваша проблема ... Я создаю форум, на котором есть система уведомлений ... так, чтобы обрабатывать его с созданием поля 'receiver' и' sender'. . надеюсь, что мои фрагменты полезны для вас: https://gist.github.com/agusmakmun/6607cbd13b7e6b06a0da97cd18b2ef22 –

0

Для этого метода задайте родственное_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() 
+0

Привет, не удастся ли вам удвоить два раза? Также я хочу получить только максимум из 5 последних объектов активности, но в вашем первом фильтре кажется, что он запрашивает все объекты ContentType? Пожалуйста, поправьте меня, если я ошибаюсь здесь? – Robin

+0

Привет, да, вы правы на обеих точках. Я использовал первый фильтр для запроса всех типов содержимого. Если вы уже знаете имя related_query_имя разных моделей, на которое хотите запросить, вы можете просто передать имена в списке и построить поиск по этому методу. Это пропустит первый запрос. Напр. 'content_types = ['posts', 'photos']'. Вы можете создать собственный тип модели менеджера и передать этот список в качестве аргумента, который будет определять обратные отношения для запроса. – maverick

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', 
     ) 

Тогда вы хотите сказать своему Viewset использовать что filterset:

from .filters import ActivityFilterSet 
# ... 
class ActivityViewSet(GenericViewSet): 
    # all your existing declarations, eg., 
    # serializer_class = ActivitySerializer 
    # ... 
    filter_class = ActivityFilterSet 
    # ... 

После того, как вы получили, что сделано, вы будете иметь возможность фильтровать результаты, используя параметры GET, например,

    012.
  • 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
  • и так далее