Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to filter across relationships of action objects #475

Open
valentijnscholten opened this issue Jan 16, 2021 · 2 comments
Open

How to filter across relationships of action objects #475

valentijnscholten opened this issue Jan 16, 2021 · 2 comments
Labels
question general questions, not really an issue

Comments

@valentijnscholten
Copy link

Hi,

I am planning on implementing an activity stream which uses a hierarchy for the action_objects.

For example:

Country
        ->
            City
                 ->
                      Street

The actions will be generated on the street level. For example

street1 = Street.objects.first()
streetN = Street.objects.last()

action.send(user, 'crashed at', street1)
action.send(user, 'crashed at', streetN)

The activity stream for a specific street is easy to render:

street1.action_object_actions.all()

But how to render the streams at the City or Country level?

Ideally I would do something like:

country = Country.objects.first()
action_object_content_type=ContentType.objects.get_for_model(Street)
Action.objects.filter(action_object_content_type=action_object_content_type, action_object__city__country=country)

But this doesn't work because of the GenericForeignKey construct.

django.core.exceptions.FieldError: Field 'action_object' 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 tried adding a GenericRelation to the Street model to point to Action, but that doesn't seem to help (To be honest I can't see how it could help).

I can make it work using subqueries (action_object_object_id__in=...):

ids=Street.objects.filter(city__country=country)
Action.objects.filter(action_object_object_id__in=ids)

But a city or country can have lots of streets, so the subquery performance will be suffering if the result of the subquery is 10k or possibly 100k streets.

I did some research and it looks like it's not so easy to avoid the subqueries without rewriting the whole Action model.

Or am I missing something obvious / simpler? Or can I trust MySQL 5.7 to optimize the subquery into a join internally?

Suggestions welcome!

Valentijn

@valentijnscholten
Copy link
Author

Just noticed there is an issue with a similar question: #428. About filtering on target object fields, but boils down to the same question.

@valentijnscholten
Copy link
Author

valentijnscholten commented Jan 16, 2021

Ha, after some diner I figured it out. As always some things in Django are backwards/counter intuitive. Adding a GenericRelation to Street to point to Action allows you to query for Street from Action :-)
Some fieldnames needed as well to make it work:

    class Street(Model):

    ...

    from django.contrib.contenttypes.fields import GenericRelation
    from actstream.models import Action
    actions = GenericRelation(Action,
        related_query_name='street',
        content_type_field='action_object_content_type',
        object_id_field='action_object_object_id',
    )

And then:

country = Country.objects.first()
Action.objects.filter(street_city__country=country)

@auvipy auvipy added the question general questions, not really an issue label Feb 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question general questions, not really an issue
Projects
None yet
Development

No branches or pull requests

2 participants