1

Having these models (simplified):

class UserProfile(models.Model):  
    user = models.OneToOneField(User)
    products = models.ManyToManyField(Product, through='UserProduct')


class Product(models.Model):
    title = models.CharField(max_length=100, blank=False)


class UserProduct(models.Model):
    user = models.ForeignKey(UserProfile)
    product = models.ForeignKey(Product)


class Recipe(models.Model):
    ingredients = models.ManyToManyField(Product, through='Ingredient')


class Ingredient(models.Model):
    product = models.ForeignKey(Product)
    recipe = models.ForeignKey(Recipe)

I need in some cases to get a list of recipes, marked on each ingredient, "whether it is user have that product.". And, maybe other calculated fields, according to given user. Example of what i want:

>>> Recipe.objects.get_for_user(user=user)[0].ingredients[0].is_user_have
>>> True

But, of course, in other cases i don't want that field attached to ingredients.

I understand that the i need custom manager. But straightforward solution - add "is_user_have" as property to Ingredient model, define custom manager with get_for_user method, call base get_queryset and then in for-loop populate that field - doesn't work.

UPDATE 1
I figured out how to get annotations that i wanted, here is my custom manager for ingredients:

class UserIngredientsManager(models.Manager):
    def get_queryset(self):
    result = super(UserIngredientsManager, self).get_queryset()

    return (result
        .annotate(
            user_have_count=models.Count(
                models.Case(
                    models.When(
                        # Hardcoded !!!
                        product__userproduct__user_id=1,
                        then=True),
                    output_field=models.IntegerField())))
        .annotate(
            is_user_have=models.Case(
                models.When(
                    user_have_count__gt=0,
                    then=models.Value(True)),
                output_field=models.BooleanField(),
                default=models.Value(False))))

But there are two problems:

  1. I can't pass user to this manager (its hardcoded for testing)
  2. I can't create proxy model for situtations when i want this annotations (see below), it only works when i replace default manager on Ingredient model.

This code doesn't work, for ingredients default related manager used instead:

class RecipeWithUserInfo(Recipe):
    class Meta:
        proxy = True

    objects = UserRecipesManager()
    ingredients = UserIngredientsManager()

It works only when i replace default manager on Ingredient model (but that not what i want):

class Ingredient(models.Model):
    ...
    objects = UserIngredientsManager()

0 Answers0