Django: Can I use objects.filter() for generic foreignkey?
Asked Answered
I

2

9

symbol.py

class Symbol(BaseModel):
    name = models.CharField(max_length=30,)

    class Meta:
        abstract = True

class StockSymbol(Symbol):
    market = models.CharField(max_length=10,)
    my_daily_price = GenericRelation(MyDailyPrice)

daily_price.py

class DailyPrice(BaseModel):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        abstract = True

class MyDailyPrice(DailyPrice):
    open = models.DecimalField(
        max_digits=15,
        decimal_places=2,
    )

What I want to do is,

symbol = StockSymbol.objects.first()
MyDailyPrice.objects.filter(content_object=symbol)

But it occured errors:

FieldError: Field 'content_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.

StockSymbol already has GenericRelation. What's wrong with it?

Or do I have to override ojbect manager?

Indomitability answered 4/2, 2018 at 6:53 Comment(0)
S
12

You can filter with content_type and object_id, instead of content_object.

from django.contrib.admin.options import get_content_type_for_model
symbol = StockSymbol.objects.first()
MyDailyPrice.objects.filter(content_type=get_content_type_for_model(symbol), object_id=symbol.pk)
Selective answered 3/4, 2019 at 14:46 Comment(1)
Does anyone know why Django doesn't just offer to do exactly that for you? I don't get it...Evocation
E
1

I wrapped up @Akash's answer in a method to be added to a custom Manager or QuerySet:

def gfks(self, **kwargs):
    filters = {}
    for field, obj in kwargs.items():
        gfk = self.model._meta.get_field(field)
        filters[gfk.ct_field] = ContentType.objects.get_for_model( obj )
        filters[gfk.fk_field] = obj.pk
    return self.filter(**filters)

For example, if you had a model called Comparison with two GFKs called product1 and product2, and added this method, usage would look like:

comp = Comparison.objects.gfks(product1=foo, product2=bar)

Would be nice if Django's contenttypes app provided some similar sugar automatically, but I'll settle for adding this to my BaseQuerySet class in the meantime.

Evocation answered 30/10, 2022 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.