简体   繁体   中英

Django filter by extra field on many to many relationship

I have this kind of model definition and I wish to have a list of product that have attribute distance < 40 from the respective product

class Product(models.Model):
    title = models.CharField(max_length=255)
    near_duplicate_images = models.ManyToManyField("self", through="NearDuplicate")

class NearDuplicate(models.Model):
    first_product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="first_product")
    second_product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="second_product")
    distance = models.IntegerField(null=True, blank=True)

I've tried doing this to directly access the relation

p = Product.objects.filter(near_duplicate_images__distance__lt=40).prefetch_related('near_duplicate_images')

But it raise this exception

django.core.exceptions.FieldError: Related Field got invalid lookup: distance

I've also tried doing this

p = Product.objects.all().prefetch_related(Prefetch("near_duplicate_images", queryset=NearDuplicate.objects.filter(distance__lt=40), to_attr="near_duplicate_images_list"))

But it raise this exception

django.core.exceptions.FieldError: Cannot resolve keyword 'near_duplicate_images_rel_+' into field. Choices are: distance, first_product, first_product_id, id, second_product, second_product_id

I think you don't need "near_duplicate_images" field. Try something like (haven't tested):

p = Product.objects.filter(title__in=NearDuplicate.object.filter(first_product=current_product, distance=40).values('second_product',)

QuerySet API - "_in"

I think a query like this should work. As you want the list of products whose distance is less than 40.

products = NearDuplicate.objects.filter(distance__lt=40).values('first_product', 'second_product')

This would give an output similar to

<QuerySet [{'first_product': 1, 'second_product': 2}]>

UPDATE - I have played around the queries a bit. If you want to get the absolute list of products present in any of the first_product or second_product . You may need to use multiple queries like

q1= NearDuplicate.objects.filter(distance__lt=40).values_list('first_product', flat=True)

This would give output as

<QuerySet [1]>

and

q2 = NearDuplicate.objects.filter(distance__lt=40).values_list('second_product', flat=True)

This would give output as

<QuerySet [2]>

First query queries all the products listed in first_product and second query lists all the products present in second_product . Now you can merge them both and take out the distinct values using the following query

q1.union(q2).distinct()

This would give the final output as

<QuerySet [1, 2]>

I hope it helps. :)

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