ManyToManyField.through_fields

Assuming the following models showing only the relevant porytions

class PlantProfile(Base):
    latin_name = models.CharField(max_length=75, unique=True)
    english_name = models.CharField(max_length=75, blank=True)
    french_name = models.CharField(max_length=75, blank=True)
class ProjectUser(AbstractUser):
    plants = models.ManyToManyField(PlantProfile, through="PlantCollection")
class PlantCollection(models.Model):
    owner = models.ForeignKey(ProjectUser, on_delete=models.CASCADE)
    plants = models.ForeignKey(PlantProfile, on_delete=models.CASCADE)
    details = models.CharField(max_length=125, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(name="unique_plant_owner", fields=["owner", "plants"]),
        ]

How can I query PlantProfile and get the owner (or a boolean) assuming that the current logged in user has the plant in his collection (bool True). I am not sure if I make myself clear.

Output would be 4 fields : latin_name, french_name, english_name, owner

owner would be either true or false. True if plant is part of the collection of the user

I’m sorry, I’m not sure I’m understanding what you’re asking for here.

Are you asking for a list of all PlantProfile, with a field indicating whether the current user is related to each instance?

Or are you only looking for a list containing those PlantProfile where the ProjectUser is related to it?

I am asking for a list of all PlantProfile that would include the field owner in a column.

Maybe the image will help. It shows a list of all the plants (PlantProfile). If a plant is in the PlantCollection of the current user, then in the last column (My Plant) indicate True.

You’re still asking for two different things here.

Do you want a column containing the owner(s), or do you want the indicator showing whether the requesting user is related? (These are two different queries.)

Sorry Ken, I did not realize it was 2 different queries. I want the indicator showing whether the requesting user is related.

But I would be curious to know how the two queries differ.

The best I can do for now is using this strategy within the template, but I am not convinced it’s the most efficient way.

{% for item in object_list  %}
...
 <td>
{% for i in item.plantcollection_set.all %}
{% if i.owner == request.user %}Yes{% endif %}
{% endfor %}
</td>
...
{% endfor %}

No, that’s not particularly good - it doesn’t scale well.

The best I’ve been able to come up with so far is this:

plant_profile_query = PlantProfile.objects.annotate(is_owner = 
  Coalesce(
    Subquery(
      PlantCollection.objects.filter(
        owner=request.user, plants=OuterRef('pk')
      ).annotate(owns=Value('Yes')).values('owns')
    ), Value('No')
  )
)

If a PlantCollection exists relating a PlantProfile with a ProjectUser, the filter in the subquery will return the value “Yes”. If the filter fails to find a match, the Coalesce function will return the value “No”.
The is_owner annotated attribute will then have either “Yes” or “No” for each instance of PlantProfile.

1 Like

YES!! That’s pretty awesome. It works