如何从Django中的相关表中获取一条记录?

时间:2019-01-20 14:22:11

标签: python sql django

我有这个模型(简化版):

class Photo(models.Model):
    image = models.ImageField(null=True, blank=True, upload_to='photos')
    photographer = models.CharField(max_length=255)

class Genre(models.Model):
    title = models.CharField(max_length=255)

class Decade(models.Model):
    title = models.CharField(max_length=255)

class Album(models.Model):
    title = models.CharField(max_length=255)
    genre = models.ForeignKey(Genre, on_delete=models.CASCADE)
    decade = models.ForeignKey(Decade, on_delete=models.CASCADE)
    photo = models.ForeignKey(Photo, on_delete=models.CASCADE, null=True, blank=True)

您可以看到一些相册中有照片。我想做的是显示一个包含所有流派的列表,该列表将显示该流派在特定十年内有多少张专辑,以及该流派的照片。照片应该是该类型专辑中的照片。我无法获得第一张唱片的照片,因为它可能没有照片,因此它应该获得包含照片的相册的照片。

示例:

类型:

1 | Rock
2 | Punk
3 | Jazz

相册:

1 | Rock All   | genre = rock | decade = sixties
2 | Rock It Up | genre = rock | decade = sixties
3 | Jazz Basiq | genre = jazz | decade = nineties
4 | Jazz Uno   | genre = jazz | decade = sixties
5 | Punkio     | genre = punk | decade = sixties

照片:

1 | rockitup.jpg   | Belongs to Album 2
2 | uno.jpg        | Belongs to Album 4
3 | punkio.jpg     | Belongs to Album 5
4 | punkio2.jpg    | Belongs to Album 5
5 | punkio3.jpg    | Belongs to Album 5
6 | basiq.jpg      | Belongs to Album 3

因此,我最终希望在模板中显示的内容如下:

In the sixties this was happening:

{% for genre in genres %}
  <li>Genre: {{ genre.title }} has {{ genre.total }} albums and here is an image: 
    <img src="{{ genre.image.url }}"></li>
{% endfor %}

我认为这是我要做的:

genres = Genre.objects.annotate(total=Count('albums', filter=Q(decade__name='sixties')))

效果很好-我成功地检索了我想展示的十年专辑的总数。现在的问题是如何显示正确的图像?在SQL中,我将考虑一个子查询:

... (SELECT photo.image FROM album a JOIN photo ON album.photo = photo.id WHERE a.genre = genre.id AND photo.id IS NOT NULL) AS image ... 

类似的东西。但是我不确定在Django中正确的方法是什么。我会以某种记录来注释它吗?我是否使用自定义模板过滤器(如果是,这应该如何工作)?

1 个答案:

答案 0 :(得分:2)

我认为您可以使用backward related objects来做到这一点:

{% for genre in genres %}
  <li>Genre: {{ genre.title }} has {{ genre.total }} albums and here are an image: 
    <img src="{{ genre.album_set.all.0.photo.url }}"></li>
{% endfor %}

它将获得该类型的第一张专辑的图像。您可以通过以下方式获取所有类型的图像:

{% for genre in genres %}
  <li>Genre: {{ genre.title }} has {{ genre.total }} albums and here is all images: 
    {% for album in genre.album_set.all %}
        <img src="{{ album.photo.url }}"></li>
    {% endfor %}
{% endfor %}

在流派模型内创建一个属性方法,并在模板中显示它。喜欢:

#model
class Genre(models.Model):
    title = models.CharField(max_length=255)

    def get_image(self):
      album = self.album_set.all().first()
      if album:
         return album.photo

# template
{% for genre in genres %}
  <li>Genre: {{ genre.title }} has {{ genre.total }} albums and here is an image: 
    {{ genre.get_image.photo.url }}
{% endfor %}

更新:使用子查询

from django.db.models import OuterRef, Subquery


albums_subquery = Album.objects.filter(genre=OuterRef('pk'))
Genre.objects.annotate(image=Subquery(albums_subquery.values('photo')[:1]))

# and then you can keep the template implementation as it is as described in question.

Subquery documentation