python - 如何从 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 中执行此操作的正确方法是什么。我是否以某种方式用一条记录对其进行注释?我是否使用自定义模板过滤器(如果是,应该如何工作)?
解决方案
我认为您可以使用向后相关的对象这样做:
{% 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 %}
或者
在 Genre Model 中创建一个属性方法并将其显示在模板中。喜欢:
#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.
推荐阅读
- python - sqlite3 通过客户端工作,但在 python 中不起作用
- c# - 搜索特定字符的字符串并将其嵌入代码
- reactjs - 使用 Create React App 在运行时加载静态文件
- python - Python 2d 矩阵类错误
- c# - 无法运行 xaml 和 c# 示例
- javascript - 创建根据表格行更改的元素 UI 工具提示内容
- shell - 使用 shell 列出列表中不存在的现有文件
- javascript - JS中的自定义排序/路径查找
- postgresql - PostgreSQL:指向其他表的表?
- r - 为列 R 中的每个重复值分配一个唯一的 ID 号