How to find django objects in a manytomany relation (with a throught)?

Viewed 27

I need to find services with pricing for each user. I've defined my models:

class User(AbstractUser):
    """Default user model."""
    username = None
    email = models.EmailField(unique=True)    
    proposals = models.ManyToManyField(
        Service,
        through=Pricing,
        blank=True)


class Service(models.Model):
    name = models.CharField(max_length=50)

class Pricing(models.Model):
    user = models.ForeignKey('users.User', on_delete=models.PROTECT)
    service = models.ForeignKey(Service, on_delete=models.PROTECT)
    price = models.IntegerField()

In my template, I can get my user with associated proposal with this for loop

{% for proposal in object.proposals.all %}
    {{ proposal }}
{% endfor %}

I do not have prices but only services names. What am i missing ?

2 Answers

You enumerate over the object.pricing_set.all() with:

{% for pricing in object.pricing_set.all %}
    {{ pricing.service }}: {{ pricing.price }}
{% endfor %}

This will however result in an N+1 problem, by making use of a Prefetch object in the view, we can avoid that.

If you are for example fetching a User object, you can prefetch the pricings and the related services with:

User.objects.prefetch_related(
    Prefetch('pricing_set', Pricing.objects.select_related('service'))
)

If you thus make use of a single User with primary key pk, then this can be implemeted as:

object = User.objects.prefetch_related(
    Prefetch('pricing_set', Pricing.objects.select_related('service'))
).get(pk=some_pk)

Sets the related_name of the foreign key field:

class Pricing(models.Model):
    user = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='pricing_instances') # Replace 'pricing_instances' with what makes sense to you
    service = models.ForeignKey(Service, on_delete=models.PROTECT)
    price = models.IntegerField()

Then you can access price like this:

{% for pricing_instance in object.pricing_instances.all %}
    {{ pricing_instance.price }}
{% endfor %}
Related