我有这个模型(简化版):
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中正确的方法是什么。我会以某种记录来注释它吗?我是否使用自定义模板过滤器(如果是,这应该如何工作)?
答案 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.