djangodjango-querysetmultivalue

Django, get objects by multiple values


There is a model

class Fabric(models.Model):
    vendor_code = models.CharField(max_length=50)
    color = models.CharField(max_length=50)
    lot = models.CharField(max_length=50)

I've a list of objects

values = [
   {'vendor_code': '123', 'color': 'aodfe', 'lot': 'some lot 1'}, 
   {'vendor_code': '456', 'color': 'adfae', 'lot': 'some lot 2'},
   {'vendor_code': '789', 'color': 'dvade', 'lot': 'some lot 3'},
]

There are no ids in dict objects. How can get objects checking for list of field values(for all 3 values per object at same time)? I know that I can query one by one in loop as:

for item in values:
    fabric = Fabric.objects.filter(vendor_code=item['vendor_code'], color=item['color'], lot=item['lot'])

but amount of objects in list can be large. Is there any proper way to get objects at once, if they exists? Or at least to get them with min amount of db hit.

Thanks in advance!


Solution

  • You can use the in (__in) filter like so:

    fabrics = Fabric.objects.filter(
        vendor_code__in=[value['vendor_code'] for value in values],
        color__in=[value['color'] for value in values],
        lot__in=[value['lot'] for value in values],
        )
    

    This will however iterate the values list 3 times, to only iterate it once use something like this:

    vendor_codes = []
    colors = []
    lots = []
    for value in values:
        vendor_codes.append(value['vendor_code'])
        colors.append(value['color'])
        lots.append(value['lot'])
    
    
    fabrics = Fabric.objects.filter(
        vendor_code__in=vendor_codes,
        color__in=colors,
        lot__in=lots,
        )
    

    To filter according to all three values at the same time you will have to use Q objects like this:

    q_objects = []
    for value in values:
        q_objects.append(Q(
            vendor_code=value['vendor_code'],
            color=value['color'],
            lot=value['lot']
            )
        )
    final_q_object = Q()
    for q_object in q_objects:
        final_q_object.add(q_object, Q.OR)
    
    
    fabrics = Fabric.objects.filter(final_q_object)
    

    The gist of it is to get this query:

    Q(Q(a=i, b=j, c=k)) | Q(Q(a=l, b=m, c=n) | ...)
    

    Final answer after a bit of optimization:

    final_query = Q()
    for item in values:
        final_query.add(
            Q(
                vendor_code=value['vendor_code'],
                color=value['color'],
                lot=value['lot']
            ),
            Q.OR
        )
    fabrics = Fabric.objects.filter(final_query)