【问题标题】:How to get a single record from a related table in Django?如何从 Django 中的相关表中获取单个记录?
【发布时间】:2019-01-20 14:22:11
【问题描述】:

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

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 中执行此操作的正确方法是什么。我是否以某种方式用一条记录对其进行注释?我是否使用自定义模板过滤器(如果是,应该如何工作)?

【问题讨论】:

    标签: python sql django


    【解决方案1】:

    我认为你可以使用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 %}
    

    或者

    在 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.
    

    Subquery documentation.

    【讨论】:

    • 谢谢,但问题是我只想得到一张图片,它必须是该类型的专辑,并且来自我正在查看的特定十年。所以基本上我想在那个子集上运行一些查询,不知何故......
    • 谢谢。我会看看子查询。但是,在示例中没有考虑“十年”过滤器。基本上我需要查询所有专辑的一个子集(WHERE 十年 = 'sixties'),而不是只列出 all 专辑,然后获取第一条有图像的记录(AND photo IS NOT NULL )。请参阅我原来的问题。
    • 您可以同时注释两者。我只是做了一个简化的例子。你可以做Queryset.annotate(..).annotate(..)annotate(count=..., image=...)
    • 好的,太好了!我将对此进行调查,这听起来很有希望。
    猜你喜欢
    • 2018-03-31
    • 2019-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多