Question

I have trouble to make these kind of queries with lots of joins. I didn't found examples, but I guess they are not so complicated to write. It's just there are several FKs.

Here is the models.py (not complicated)

class User(AbstractBaseUser, PermissionsMixin): # Django custom user model
    # Some stuff

class CliProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)

class BizProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)

class Card(models.Model):
    linked_client = models.ForeignKey(CliProfile, blank=True, null=True)

class Points(models.Model):
    benef_card = models.ForeignKey(Card)
    at_owner = models.ForeignKey(BizProfile)
    creation_date = models.DateTimeField(auto_now_add=True)

Quick description of the model

  • a user can be a client (using CliProfile) or a business (using BizProfile)
  • each card is linked to a client
  • each card contains a [points - business] association

This way: a client has a card and can has 3 points at Pizza Hut, and 5 points at McDonalds with the same card)

The request I'm trying to write

Functionally speaking, the purpose is a owner (like PizzaHut) can see all his clients (client who have cards which has points at Pizza Hut)

Technically speaking, I'm trying to write a query to get all clients (ie. a CliProfile queryset) whose cards (at least 1 of all) whose points (at least 1 of all) whose owner (there is only 1) whose user (there is only 1) = request.user ?

Do you have any idea how to write such a query? Thanks a lot.

Was it helpful?

Solution

To match fields within models in filter() you need to use two underscores. The following worked for me

CliProfile.objects.filter(card__points__at_owner=request.user)

But @Alex's suggestion makes the most sense unless this was just an example of what you are trying to do.

If you wanted profiles that are associated with one of several cards you can use the __in field lookup:

CliProfile.objects.filter(card__in=IterableOfCards)

Also you don't use == in filter(). That would return True or False and then pass that value in the filter() call effectively making the call filter(True or False) which won't do anything useful. you have to use = because you are passing a named parameter into the filter function.

Why card instead of card_set()? cart_set only exists within an instance of a CliProfile. You are not in an instance of a CliProfile, you are trying to get a list of them.

You can try it in the terminal and it will tell you the valid choices.

#Note that it doesn't matter what you put after=, since it fails before that is checked.
>>> CliProfile.objects.filter(card_set=True)
FieldError: Cannot resolve keyword 'card_set' into field. Choices are: card, id, user

a CliProfile can be referenced by multiple cards, which is why card_set exists in it but you are trying to match one card. The card whose points at_owner field is request.user.

You would use a_cliprofile_instance.card_set.filter() to get a subset of their cards or a_cliprofile_instance.card_set.all() to display all of their cards

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top