martin
martin

Reputation: 885

Defining a property method in Django with a ForeignKey

I'm working with my Django app, in which I have two models: Store and Turn. There is a one-to-many relationship where a store has many turns that need to be processed. This is the code:

class Store(models.Model):
    name = models.CharField(max_length=20)
    adress = models.CharField(max_length=40)
    image = models.ImageField(upload_to=)

    @property
    def average_wait_time(self):
        return #here's the problem

    def __str__(self):
        return self.name


class Turn(models.Model):
    store = models.ForeignKey(Store, on_delete=models.SET_NULL, null=True)
    creation_time = models.TimeField(auto_now=True)
    completion_time = models.TimeField(blank=True, null=True)

    def complete(self):
        self.completion_time = timezone.now()

    def __str__(self):
        return f'Turno a las {creation_time} para el Negocio {completion_time}'

As you can see, I have a @property method that I need to use in order to calculate the average wait time in a store, determined by the average of the turn durations. How can I make this work? I cannot access the Turn model from the 'Store' model...

Upvotes: 0

Views: 325

Answers (1)

tim-mccurrach
tim-mccurrach

Reputation: 6815

related_name

What you need is the related_name argument in ForeignKey. It works as follows:

store = models.ForeignKey(Store, on_delete=models.SET_NULL, null=True, related_name="turns")

This means you can now access all of the turns of a particular store as follows:

store = Store.objects.get(id=1)  # get a store
turns = store.turns

turns in the above will be a queryset of Turn objects.

Note also, that if you don't specify a related_name django actually creates one for you automatically by adding _set onto the end of the related model name in lowercase, and you can do the same with the following:

turns = store.turn_set

But it's probably better to name the related_name explicitly.

Your particular case

class Store(models.Model):
    ...

    @property
    def average_wait_time(self):
        turns = self.turns
        total_time = sum([turn.completion_time - turn.creation_time for turn in turns.all()])
        average_time = total_time / turns.count()
        return average_time

You can also do this using aggregate and Avg, but the above is probably fine.

Upvotes: 1

Related Questions