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:
And suppose these are activities:
I can get the user Thor:
thor = User.objects.get(email="[email protected]")
And get the list of users/objects followed by Thor:
thor.follow_set.all()
<QuerySet [<Follow: [email protected] follows [email protected]>, <Follow: [email protected] follows [email protected]>, <Follow: [email protected] 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 [email protected]>, <Activity: [email protected] Created Avengers>]>
But how can I get all the activities Thor is following from the above activities:
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
A GenericForeignKey is in simple words a composition of two fields (a foreign key to ContentType and a field to store the related primary key) to point to a specific object. Looking at your code:
q = (Q(actor__in=thor.follow_set.all())| Q(target__in=thor.follow_set.all())
Activity.objects.filter(q)
This kind of filtering does not work with a GenericForeignKey basically because if it did, in case one tries to access related attributes Django would have to perform tasks like:
This would be quite complex and have several problems like long query times if there are too many joins, etc.
Also your code is somewhat incorrect as it filters Activity objects related to Follow objects and not the followed object.
To solve your problem you can instead filter on actor_content_type, actor_object_id, etc. instead:
follows = thor.follow_set.all()
filters = [Q(actor_content_type=follow.content_type)
& Q(actor_object_id=follow.object_id)
| Q(target_content_type=follow.content_type)
& Q(target_object_id=follow.object_id) for follow in follows]
Activity.objects.filter(Q(*filters, _connector=Q.OR))
If 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