DRY запросы для моделей Django

Представте, что вам нужно создать музыкальную базу. Она должна включать в себя песни, альбомы и исполнителей. Причем одну и туже песню могут исполнять несколько исполнителей. В упрощенном виде мы получаем такую структуру:

from django.db import models


class Artist(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)


class Album(models.Model):
    name = models.CharField(max_length=50)
    release_date = models.DateField()


class Song(models.Model):
    artists = models.ManyToManyField(Artist)
    albums = models.ManyToManyField(Album)
    name = models.CharField(max_length=100)
    release_date = models.DateField()

Вы можете заметить, что получить информацию об исполнителях той или иной песни не просто. Предположим, нам надо получить всех исполнителей в альбоме «Lion King». Для этого вам надо выполнить следующее:

songs = Song.objects.filter(albums__name='Lion King')
artists_from_lion_king = Artist.objects.filter(song_set__in=songs)

Заметим, что это не лучшее решение. Но вполне возможно, что и вы ловили себя на том, что применяете подобное в views, передавая туда лишь название как параметр. Не надо так делать. Лучше сразу писать правильно. Например так:

from django.db import models


class Artist(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    @property
    def albums(self):
        songs = self.song_set.all()
        return Album.objects.filter(song_set__in=songs)


class Album(models.Model):
    name = models.CharField(max_length=50)
    release_date = models.DateField()

    @property
    def artists(self):
        songs = self.song_set.all()
        return Artist.objects.filter(song_set__in=songs)


class Song(models.Model):
    artists = models.ManyToManyField(Artist)
    albums = models.ManyToManyField(Album)
    name = models.CharField(max_length=100)
    release_date = models.DateField()

Немного поясню. Мы для каждой модели (Artist или Album) выбираем все песни, ей принадлежащие, и получаем общий метод, который возращает queryset (не список, что гараздо удобнее).

Данный паттерн очень удобен, если его постоянно использовать.

 
comments powered by Disqus