Django ORM - select_related и order_by с внешними ключами - PullRequest
6 голосов
/ 23 октября 2009

У меня есть простая музыкальная схема: Artist, Release, Track и Song. Первые 3 являются логическими конструкциями, в то время как четвертый (Song) является конкретным экземпляром (Artist, Release, Track) в виде mp3, wav, ogg и т. Д.

У меня возникли проблемы при создании упорядоченного списка песен в базе данных. Уловка в том, что и Track, и Release имеют Artist. В то время как Song.Track.Artist всегда является именем исполнителя, Song.Track.Release.Artist может быть именем исполнителя или «Разные исполнители» для компиляций. Я хочу иметь возможность сортировки по одному или другому, и я не могу найти правильный способ сделать эту работу.

Вот моя схема:

class Artist(models.Model):
    name = models.CharField(max_length=512)

class Release(models.Model):
    name = models.CharField(max_length=512)
    artist = models.ForeignKey(Artist)

class Track(models.Model):
    name = models.CharField(max_length=512)
    track_number = models.IntegerField('Position of the track on its release')
    length = models.IntegerField('Length of the song in seconds')
    artist = models.ForeignKey(Artist)
    release = models.ForeignKey(Release)

class Song(models.Model):
    bitrate = models.IntegerField('Bitrate of the song in kbps')
    location = models.CharField('Permanent storage location of the file', max_length=1024)
    owner = models.ForeignKey(User)
    track = models.ForeignKey(Track)

Мой запрос должен быть довольно простым; отфильтруйте все песни, принадлежащие конкретному пользователю, а затем отсортируйте их по Song.Track.Artist.name или Song.Track.Release.Artist.name. Вот мой код в представлении, которое сортируется по Song.Track.Artist.name:

songs = Song.objects.filter(owner=request.user).select_related('track__artist', 'track__release', 'track__release__artist').order_by('player_artist.name')

Я не могу заставить order_by работать, если я не использую tblname.colname. Я взглянул на метод as_sql базового объекта запроса, который указывает, что когда внутреннее соединение выполняется для получения Song.Track.Release.Artist временного имени T6, используется для таблицы Artist, поскольку внутреннее соединение уже было выполнено эту же таблицу, чтобы получить Song.Track.Artist:

>>> songs = Song.objects.filter(owner=request.user).select_related('track__artist', 'track__release', 'track__release__artist').order_by('T6.name')
>>> print songs.query.as_sql()
('SELECT "player_song"."id", "player_song"."bitrate", "player_song"."location", 
  "player_song"."owner_id", "player_song"."track_id", "player_track"."id", 
  "player_track"."name", "player_track"."track_number", "player_track"."length", 
  "player_track"."artist_id", "player_track"."release_id", "player_artist"."id", 
  "player_artist"."name", "player_release"."id", "player_release"."name", 
  "player_release"."artist_id", T6."id", T6."name" FROM "player_song" INNER JOIN 
  "player_track" ON ("player_song"."track_id" = "player_track"."id") INNER JOIN 
  "player_artist" ON ("player_track"."artist_id" = "player_artist"."id") INNER JOIN 
  "player_release" ON ("player_track"."release_id" = "player_release"."id") INNER JOIN 
  "player_artist" T6 ON ("player_release"."artist_id" = T6."id") WHERE 
  "player_song"."owner_id" = %s  ORDER BY T6.name ASC', (1,))

Когда я добавляю это как имя таблицы в order_by, оно работает (см. Пример выходных данных выше), но это кажется совершенно непереносимым. Конечно, есть лучший способ сделать это! Чего мне не хватает?

1 Ответ

26 голосов
/ 23 октября 2009

Боюсь, я действительно не могу понять, в чем ваш вопрос.

Пара исправлений: select_related не имеет ничего общего с упорядочением (он вообще не меняет набор запросов, просто следует за объединениями, чтобы получить связанные объекты и кэшировать их); и для упорядочения по полю в связанной модели вы используете двойное подчеркивание, а не пунктир. Например:

Song.objects.filter(owner=request.user).order_by('track__artist__name')

Но в вашем примере вы используете 'player_artist', который, кажется, не является полем где-либо в вашей модели. И я не понимаю вашу ссылку на мобильность.

...