Filter the contenttype with a list of content object fields

I am creating an application in which users can post tags related to it:

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) 

What I want to do is to get / filter a maximum of 5 last / last Activity objects, a list of users and a list of tags from the list of Post objects tag fields and return json using django-rest-framework for viewing on the client side.

For example, actions:

  • UserA created a new Post obect with tags (#class, #school)
  • UserB created a new Post object with tags (#professor, #teacher)
  • UserC created a new Post object with tags (#school, #university)
  • UserD created a new Post object with tags (#university, #school)

So, say I want to filter Activity with user_list=[UserA, UserC] and tag_list = [#class, #teacher]

He must return:

  • UserA created a new Post obect with tags (#class, #school)
  • UserC created a new Post object with tags (#school, #university)
  • UserB created a new Post object with tags (#professor, #teacher)

To filter activity with users, I can request this method:

 Activity.objects.filter(actor__in=user_list) 

But how do I filter an Activity with a content_object (i.eost or Photo) field (i.e. Post.tags or Photo.tags)? Now I do this:

 Activity.objects.filter(posts__tags__in=tag_l) Activity.objects.filter(photos__tags__in=tags) 

So, to summarize, if I need actions with a list of users and a list of tags, I have to do like this:

 activites = Activity.objects.filter( Q(actor__in=user_list) | Qposts__tags__in=tag_list) | Q(photos__tags__in=tag_list) ) 

But suppose there are more than two classes of the ContentType model, then I will have to add another Q(moreModel__tags__in=tag_list) . Therefore, I hope that there is a better way to optimize the process.

+7
django django-models django-queryset django-rest-framework
source share
3 answers

I will give you an example from django updown , it has a similar model: 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> >>> 

You can also use ContentType.objects.get_for_model(model_instance) , for example: https://github.com/weluse/django-updown/blob/master/updown/fields.py#L70

In your problem, maybe you can use this.

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

Hope this helps.

0
source share

For this method, specify the associated_query_name model name in lowercase or verbose_name of the model.

You can first filter the type of content present in the Activity model.

 content_types = ContentType.objects.filter(activity__id__isnull=False) 

Now use these types of content to create your search.

 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) 

Now you can filter actions using this search.

 activities = Activity.objects.filter(q).distinct() 
0
source share

I would say that it is best to use the filter from the Django filter (link to other documents for the platform), in particular ModelMultipleChoiceFilter . I assume that you already have an ActivityViewSet to go along with the Activity model.

First, you need to create django_filters.FilterSet , possibly in a new file, for example filters.py , and configure ModelMultipleChoiceFilter , for example:

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

Then you want to tell your browser what to use this set of filters:

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

Once you do this, you can filter your results using GET parameters, for example.

  • GET /activities?users=1 - everything created by user 1
  • GET /activities?users=1&users=2 - everything created by user 1 or user 2
  • GET /activities?users=1&tags=class - everything created by user 1 with the #Class tag
  • GET /activities?users=1&users=2&tags=class&tags=school - everything created by user 1 or user 2 with #Class or #School tags
  • etc.
0
source share

All Articles