简体   繁体   中英

Django query in One to Many relationship

I have 2 tables, Order and OrderDetails, on OrderDetails I have a field 'product_type'.

From the table Order I want to get all the product_type fields in a list.

Order.objects.filter(pk=1).annotate(type=F('product_type'))

I want the type value to return a list of all product types, not just the first result like 'chair'.

Ex: type = ['chair', 'pencil']

Models:

class Order(models.Model):
    user = models.ForeignKey(User, related_name="orders")

class OrderDetails(models.Model):
    order = models.ForeignKey(Order, related_name="details")
    quantity = models.SmallIntegerField(null=False, blank=False)
    product_type = models.CharField(null=False, blank=False)

This is not something you can or should try to achieve with a queryset annotation. This is because annotations are only usable for aggregation functions like Count , Sum etc.

If I understood your question correctly, you can get this info when iterating over the queryset:

for order in Order.objects.all():
    types = order.details.values_list('product_type', flat=True)

You can make this more efficient by prefetching the related OrderDetail rows for each order:

for order in Order.objects.prefetch_related('details'):
    types = order.details.values_list('product_type', flat=True)

Alternatively, you can retrieve some values from each order using this method:

queryset = Order.objects.values('id', 'user_id', 'details__product_type')

It should do a single db query. However, see the notes here about how this works: https://docs.djangoproject.com/en/1.9/ref/models/querysets/#values

Your queryset will output dicts instead of model instances. And you will not get a nice list of product_type s... instead you will get repeated rows like:

[
    {'id': 1, 'user_id': 1, 'product_type': 'chair'},
    {'id': 1, 'user_id': 1, 'product_type': 'table'},
    {'id': 2, 'user_id': 3, 'product_type': 'chair'},
    ...
]

...so you'll then have to group these rows in python into the data structure you want:

from collections import OrderedDict

grouped = OrderedDict()
for order in Order.objects.values('id', 'user_id', 'details__product_type'):
    if order['id'] not in grouped:
        grouped[order['id']] = {
            'id': order['id'],
            'user_id': order['user_id'],
            'types': set(),
        }
    grouped[order['id']]['types'].add(order['details__product_type'])

Perhaps you can try something like the following (untested):

# first get the order whose product details you want
o = Order.objects.get(pk=1)

# now get a list of the different product_types
# for order details associated with that order
product_types = OrderDetails.objects.filter(order=o).values('product_type')

Let me know if I am misunderstanding your question.

I needed to do something similar, and ended up with two queries like this (extending @brianpck's idea):

# first get the orders whose product details you want
o = Order.objects.values_list('id', flat=True)

# now get a list of the different product_types
# for order details associated with that order
product_types = OrderDetails.objects.filter(order__in=o).values('product_type').distinct()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM