There is a certain model with a function called with an additional parameter.

class Product(models.Model): category = models.ForeignKey(Category, verbose_name=u'Группа') name = models.CharField(u'Название товара', max_length=128) price = models.DecimalField(u'Стоимость единицы, руб.', max_digits=10, decimal_places=2) brand = models.ManyToManyField(Brand, related_name='products') def __unicode__(self): return self.name def discounted_price(self, user=None): return float(self.price) * self.max_discount(user=user) / 100 def max_discount(self, user=None): brand_q = Q(content_type=ContentType.objects.get_for_model(Brand), object_id__in=self.brand.all().values('id')) category_q = Q(content_type=ContentType.objects.get_for_model(Category), object_id=self.category.id) product_q = Q(content_type=ContentType.objects.get_for_model(Product), object_id=self.id) attr_q = Q(content_type=ContentType.objects.get_for_model(Attributes), object_id__in=self.attributes.all().values('id')) if user: user_q = Q(content_type=ContentType.objects.get_for_model(User), object_id=user.id) else: user_q = Q(content_type=ContentType.objects.get_for_model(User), object_id=None) discounts = Discount.objects.filter(brand_q | category_q | user_q | product_q | attr_q, start_date__lte=timezone.now(), end_date__gt=timezone.now()).\ aggregate(max_discount=Max('discount')) # discounts = Discount.objects.filter(brand_q | category_q | user_q | product_q | attr_q, # start_date__lte=timezone.now()).\ # aggregate(max_discount=Max('discount')) return discounts['max_discount'] if discounts['max_discount'] else 0.0 class Discount(models.Model): start_date = models.DateField(auto_now_add=True) end_date = models.DateField(auto_now_add=True) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') discount = models.FloatField() def __unicode__(self): return '{} "{}": {}'.format(self.content_type.name, self.content_object, self.discount) 

How can I insert this function in the queryset. I would like something similar, but, clearly - it does not work.

 Product.objects.all().annotate(discounted_price=discounted_price(user=user)).order_by('discounted_price') 
  • The purpose of some_field, field1, field2, annotate is unclear. Describe more specifically what result you want to achieve. So far, nothing is clear. - Nikmoon
  • You cannot annotate a queryset with the result of calling functions Explain what you want to achieve and show more code, maybe we can do without my_property. Or it is necessary to do data post-processing. - Sergey Gornostaev
  • Changed the code. I hope now everything is clearer. It is necessary to sort the data by price with a discount. The discount can be on several models (discount on the brand, category, the product itself or a personal discount from the user). For proper sorting you need to do this when querying the database) - Kamo Petrosyan

1 answer 1

It just didn't work out. I had to go "another way": I created a manager with three-story SQL.

 class ProductManager(models.Manager): def with_discounted_price(self): today = timezone.now().strftime("%Y-%m-%d") query = ''' SELECT ("shop_product"."price" - ("shop_product"."price" * CASE WHEN MAX("shop_discount"."discount") IS NULL THEN 0.00 ELSE MAX("shop_discount"."discount") END / 100)) AS "discounted_price" FROM "shop_discount" WHERE ( (( "shop_discount"."object_id" IN ( SELECT U0."id" FROM "shop_brand" U0 INNER JOIN "shop_product_brand" U1 ON (U0."id" = U1."brand_id") WHERE U1."product_id" = "shop_product"."id" ) AND "shop_discount"."content_type_id" = %s ) OR ( "shop_discount"."object_id" IN (SELECT U0."id" FROM "shop_attributes" U0 WHERE U0."product_id" = "shop_product"."id") AND "shop_discount"."content_type_id" = %s )) OR ("shop_discount"."object_id" = "shop_product"."category_id" AND "shop_discount"."content_type_id" = %s) OR ("shop_discount"."object_id" = "shop_product"."id" AND "shop_discount"."content_type_id" = %s) AND "shop_discount"."start_date" <= %s AND "shop_discount"."end_date" >= %s ) ''' query_params = ( ContentType.objects.get_for_model(Brand).pk, ContentType.objects.get_for_model(Attributes).pk, ContentType.objects.get_for_model(Category).pk, ContentType.objects.get_for_model(Product).pk, today, today,) qs = self.get_queryset() qs = qs.extra(select={'discounted_price': query}, select_params=query_params) return qs