I creating an app where users can post with its related tags:
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 get/filter maximum of 5 latest/recent Activity objects, with the a list of users, and a list of tags from the list of Post objects tags field and return json using django-rest-framework to view in the client side.
Eg activities:
So say I want to filter Activity with user_list=[UserA, UserC]
and tag_list = [#class, #teacher]
It should return:
To filter the Activity with users, I can query this way:
Activity.objects.filter(actor__in=user_list)
But, how do I filter Activity with the content_object's (i.e.Post or Photo) field (i.e. Post.tags or Photo.tags)? Now I am doing this way:
Activity.objects.filter(posts__tags__in=tag_l)
Activity.objects.filter(photos__tags__in=tags)
So to sum up, If I have need activities with list of users and 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 will be more than two ContentType model classes then I'd have to again add another Q(moreModel__tags__in=tag_list)
. So, I hope there's a better way to optimize the process.
Content types are Django's way of identifying database tables. Every Database table is represented as a row in the content type table which is created and maintained by Django.
Django-filter is a generic, reusable application to alleviate writing some of the more mundane bits of view code. Specifically, it allows users to filter down a queryset based on a model's fields, displaying the form to let them do this.
Instances of ContentType have methods for returning the model classes they represent and for querying objects from those models. ContentType also has a custom manager that adds methods for working with ContentType and for obtaining instances of ContentType for a particular model.
Basically it's a built in app that keeps track of models from the installed apps of your Django application. And one of the use cases of the ContentTypes is to create generic relationships between models.
I'd say your best bet would be to use a filter from Django filter (link to the rest framework docs), specifically a ModelMultipleChoiceFilter
. I'm going to assume you already have an ActivityViewSet
to go along with the Activity
model.
Firstly you'll want to create a django_filters.FilterSet
, probably in a new file such as filters.py
, and set up the ModelMultipleChoiceFilter
, like so:
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'll want to tell your viewset to use that filterset:
from .filters import ActivityFilterSet
# ...
class ActivityViewSet(GenericViewSet):
# all your existing declarations, eg.,
# serializer_class = ActivitySerializer
# ...
filter_class = ActivityFilterSet
# ...
Once you've got that done, you'll be able to filter your results using GET parameters, eg.,
GET /activities?users=1
– everything created by User 1GET /activities?users=1&users=2
– everything created by User 1 or User 2GET /activities?users=1&tags=class
– everything created by User 1 with the tag #ClassGET /activities?users=1&users=2&tags=class&tags=school
– everything created by User 1 or User 2 with the tags #Class or #SchoolIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With