Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django prefetch table that is not directly related

Tags:

django

With the following models is it possible to get Category objects and prefetch OrderLine so that it can be accessed by category.orderlines.all()?

class Category(models.Model):
    name = models.CharField(...)

class Product(models.Model):
    category = models.ForeignKey(Category, related_name='products', ...)
    name = models.CharField(...)
    price = models.DecimalField(...)

class OrderLine(models.Model):
    product = models.ForeignKey(Product, related_name='orderlines', ...)

I know you can do a nested prefetch through products but I'd like to access OrderLine objects directly from a Category rather than go through Product

from django.db.models import Prefetch

categories = Category.objects.prefetch_related(Prefetch(
    'products__orderlines',
    queryset=OrderLine.objects.filter(...)
))

for category in categories:
    for product in category.products.all():
        for line in product.orderlines.all():
            ...

Desired usage:

for category in categories:
    for line in category.orderlines.all():
        ...

Update

Adding to_attr='orderlines' results in:

ValueError: to_attr=orderlines conflicts with a field on the Product model.

Changing the attribute name to_attr='lines' doesn't cause an error but the attribute isn't added to the Category objects. It prefetches Product then adds a lines attribute to each product.

like image 438
bdoubleu Avatar asked Dec 09 '19 11:12

bdoubleu


1 Answers

The closest I've come to this is using an ArraySubquery. The only downside being that lines is a list of dictionaries rather than model instances.

Category.objects.annotate(
    lines=ArraySubquery(
        OrderLine.objects
        .filter(category=OuterRef('id'))
        .values(json=JSONObject(
            id='id',
            product_name='product__name',
            quantity='quantity',
        ))
    )
)
like image 109
bdoubleu Avatar answered Oct 08 '22 22:10

bdoubleu