【问题标题】:Django - Performing a subquery on a subquery and then getting all associated fieldsDjango - 对子查询执行子查询,然后获取所有关联字段
【发布时间】:2019-03-08 18:42:29
【问题描述】:

我有以下形式的数据:

collection_name | type       | manufacturer | description | image_url
---------------------------------------------------------------------------
beach           | bed        | company a    | nice bed    | 1.jpg
beach           | king bed   | company a    | nice bed    | 1.jpg
beach           | nightstand | company a    | nice ns     | 1.jpg
grass           | chest      | company a    | nice chest  | 2.jpg
apple           | chest      | company a    | nice chest  | 3.jpg
fiver           | chest      | company b    | good chest  | 4.jpg

以及类似的模型:

class Product(models.Model):
    collection_name = models.TextField(null='true',blank='true')
    type = models.TextField(null='true',blank='true')
    manufacturer = models.TextField(null='true',blank='true')
    description = models.TextField(null='true',blank='true')
    image_url = models.TextField(null='true',blank='true')

我目前在我的应用中尝试做的是:

  • 通过 id 链接到产品(使用隐藏的 pk id 字段),
  • 然后为该 id 获取集合名称
  • 获取具有该集合名称的产品列表
  • 从具有特定集合名称的产品集中获取不同的 image_url 列表 *
  • 对于新集合中的每个image_url,获取所有具有相同image_url的记录

我想这样做的原因是,正如您在上面的示例数据中看到的那样,不同的产品有时会重复使用同一张图片(有些图片在一张图片中显示多个产品)。我只想显示每张图片一次,同时能够显示与给定图片相关的所有产品(其中可能有多个)。

我一直在考虑根据this 的答案做以下类似的事情,我认为它遵循上面给出的逻辑,但我不确定这是正确的方法。

collectionname = product.objects.filter(id=id).values('collection_name').distinct()
images = product.objects.filter(collection_name__in=collectionname).values("image_url").distinct()
results = []
for img in images:
    pbis = product.objects.filter(collection_name__in=collectionname, image_url=img['image_url'])
    obj = {"image": img['image_url'], "items":[{"attr":pbi.attr, ...} for pbi in pbis]}
    results.append(obj)

我的方法中有哪些明显的错误,是否有更好、更简洁的方法来做到这一点?如果相关,后端是 postgres。

我希望能够在模板中做的事情是这样的:

{% for instance in image_url %}
{{ collection_name }} Collection:
<img src="{{ instance }}">
Product type: {{ instance.type }}
Product Description: {{ instance.description }}
{% endfor %}

应该输出如下内容:

对于 1.jpg:

Beach Collection
<img src="1.jpg">
Product type: bed
Product Description: nice bed
Product type: king bed
Product Description: nice bed
Product type: nightstand
Product Description: nice ns

对于 2.jpg:

Grass Collection
<img src="2.jpg">
Product type: chest
Product Description: nice chest

对于 3.jpg:

Apple Collection
<img src="3.jpg">
Product type: chest
Product Description: nice chest

对于 4.jpg:

Fiver Collection
<img src="4.jpg">
Product type: chest
Product Description: good chest

【问题讨论】:

    标签: django


    【解决方案1】:

    听起来像几个额外的模型可以更轻松地处理您的内容:CollectionImage。对于您的 Image 模型,我建议使用 Django 的 ImageField,它比将 URL 保存为文本更全面。要使用 ImageField,您需要安装 Pillow,它是 Python 图像库 (PIL) 的当前维护分支。在命令行上使用pip install Pillow 执行此操作。 ImageField 具有内置属性url,因此您仍然可以轻松访问图像的 URL(参见下面的模板)。那么你的新 models.py 可以是:

    # models.py
    class Image(models.Model):
        source = models.ImageField(upload_to='my_media_path')
    
    class Collection(models.Model):
        name = models.CharField(max_length=30)
        products = models.ManyToManyField(Product, blank=True)
        feature_image = models.ForeignKey(Image, related_name='collections', on_delete=models.SET_NULL)
    
    class Product(models.Model):
        ...
        image = models.ForeignKey(Image, related_name='products', on_delete=models.SET_NULL) 
        # or ManyToManyField if a product has several images
    

    然后你可以得到与你选择的产品在同一图像中的其他产品:

    bucket = Product.objects.get(pk=1)
    bucket.image.products.all()
    <Product: Bucket>
    <Product: Spade>
    

    对于您的模板 - 主要按集合组织内容:

    # query
    collections = Collection.objects.all()
    
    # template.html
    {% for collection in collections %}
        <h1>{{ collection.name }}</h1>
        <img src="{{ collection.feature_image.source.url }}">
        {% for product in collection.products.all %}
    
            <p>{{ product.name }}</p>
            <p>{{ product.description }}</p>
    
        {% endfor %}
    {% endfor %}
    

    编辑:

    没有时间实施上述方法,您提到的方法将可以正常工作。您可以在视图代码中更改一行以使其更简单:

    ...
    obj = {"image": img['image_url'], "items": pbis}
    

    然后当您将results dict 传递给模板上下文时:

    {% for collection in results %}
        {{ collection.items.0.collection_name }} Collection:
        <img src="{{ collection.image }}">
        {% for item in collection.items %}
            <p>Product type: {{ item.type }}</p>
            <p>Product Description: {{ item.description }}</p>
        {% endfor %}
    {% endfor %}
    

    【讨论】:

    • 谢谢你的回答,这很有趣,我还在看。我应该注意到我的情况并不典型,当我使用 django 制作模型时,我将数据直接导入 postgres,因为它通过 API 批量传入,需要进行大量修改才能使用。我实际上有一个 image_path 和 image_url 字段,其中 image_path 字段是 FileField 类型并与 easy_thumbnails 包一起使用。
    • 我担心像这样更改模型定义,因为我认为它会使我需要的数据更难导入,并且目前一切正常,除了能够以我需要的方式查询/想要。
    • 我明白你的意思。如果您的显示基于集合,那么您在内存中创建集合模型和查询集的等效方法对我来说感觉很尴尬。如果您保留 Product 模型和图像设置,只是将 Collection 模型添加到您的 Django 项目(和 Postgres),怎么样?它不会影响任何现有的数据导入过程。当您导入产品数据时,它可以根据产品的集合名称获取或创建一个集合,并将产品添加到该集合中 - 例如,使用信号或自定义保存方法。
    • 如果您不想这样做,那么您在问题中提到的方法没有任何问题。视图代码很好,您的模板 sn-p 只需稍作调整即可按预期工作。
    • 我同意我目前的方法很笨拙,但我一直在压力下工作,而且知识有限,无法让它发挥作用。我计划按照您的建议执行“版本 2”,有一个集合模型并在插入数据时以某种方式创建它,但这是我需要/想要学习如何做的事情,并且在我需要之前无法做到准备好当前的项目。
    猜你喜欢
    • 2018-11-18
    • 2014-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-30
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    相关资源
    最近更新 更多