#StackBounty: #django #django-models #django-queryset Filter GenericForeignKey by list of different model objects

Bounty: 50

I am trying to build activity logs by a User or any other model object, to which a user can follow or subscribe to.

These are the models related with logging the activities:

class Activity(models.Model):
    """
    An activity log : Actor acts on target
    Actor can be a User or a model Object
    """
    actor_content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, related_name="actor_type")
    actor_object_id = models.PositiveIntegerField()
    actor = GenericForeignKey('actor_content_type', 'actor_object_id')
    description = models.TextField()
    target_content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, related_name="target_type")
    target_object_id = models.PositiveIntegerField()
    target = GenericForeignKey('target_content_type', 'target_object_id')


class Follow(models.Model):
    """
    A user can follow any User or model objects.
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    follow_object = GenericForeignKey('content_type', 'object_id')

Suppose:

  • Thor follows user Loki // User follows User
  • Thor follows user Stark // User follows User
  • Thor follows project Avenger // User follows Project

And suppose these are activities:

  • Nick Fury Created a project "Shield"
  • Nick Fury Created a project "Avengers"
  • Stark created a project "Mark II"
  • Avengers added Vision
  • Dr Strange created a project Dormammu

I can get the user Thor:

thor = User.objects.get(email="thor@gmail.com")

And get the list of users/objects followed by Thor:

thor.follow_set.all()
<QuerySet [<Follow: thor@gmail.com follows loki@gmail.com>, <Follow: thor@gmail.com follows Stark@gmail.com>, <Follow: thor@gmail.com follows Avengers>]>

Now to get the list of activities followed by the user Thor, I tried this:

q = (Q(actor__in=thor.follow_set.all())| Q(target__in=thor.follow_set.all())

Activity.objects.filter(q)

But its throwing an error:

FieldError: Field ‘actor’ does not generate an automatic reverse
relation and therefore cannot be used for reverse querying. If it is a
GenericForeignKey, consider adding a GenericRelation.

I can get all the activities for a single object followed by user Thor:

followed_last = thor.follow_set.last().follow_object

q = (Q(actor_content_type=ContentType.objects.get_for_model(followed_last), actor_object_id=followed_last.id)| Q(target_content_type=ContentType.objects.get_for_model(followed_last), target_object_id=followed_last.id))

Activity.objects.filter(q)
<QuerySet [<Activity: Avengers Added vision@gmail.com>, <Activity: nickfury@gmail.om Created Avengers>]>

But how can I get all the activities Thor is following from the above activities:

  • Nick Fury Created a project "Avengers"
  • Stark created a project "Mark II"
  • Avengers added Vision

Update

Added User model as requested:

class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    fullname = models.CharField(max_length=255, validators=[validate_fullname])
    is_active = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)
    date_joined = models.DateTimeField(auto_now_add=True)

    objects = UserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["fullname"]

    def __str__(self):
        return self.email


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.