You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django:如何在Vendor详情页展示关联的Product列表?

问题:Django供应商详情页关联产品列表展示需求

我需要在Vendor的基于类的详情视图中,同时展示该供应商的基本信息和其所有关联的Product列表。目前尝试用两个视图指向同一模板,但无法通过URL的PK筛选对应供应商的产品,求正确实现方案。

现有模型代码

from django.db import models
import datetime
import os

class Vendor(models.Model):
    name = models.CharField(max_length=126, unique=True, blank=True, null=True)
    contact_number = models.CharField(max_length=126, blank=True, null=True)
    address = models.CharField(max_length=126, blank=True, null=True)
    email = models.EmailField(max_length=126, blank=True, null=True)
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return self.name

def productFilepath(instance, filename):
    year = datetime.now().strftime("%Y/")
    return os.path.join(instance.vendors.name, year, filename)

class Product(models.Model):
    vendors = models.ForeignKey(Vendor, on_delete=models.CASCADE)
    collection = models.CharField(max_length=126, unique=True, blank=True, null=True)
    product = models.CharField(max_length=126, blank=True, null=True)
    size = models.CharField(max_length=126, blank=True, null=True)
    vendor_price = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    retail_price = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
    sku = models.CharField(max_length=126, unique=True, blank=True, null=True)
    year = models.PositiveSmallIntegerField(blank=True, null=True)
    quarters_in_year = {
        'q1': 'Q1',
        'q2': 'Q2',
        'q3': 'Q3',
        'q4': 'Q4',
    }
    quarter = models.CharField(max_length=2, choices=quarters_in_year.items(), blank=True, null=True)
    image = models.FileField(upload_to=productFilepath, blank=True, null=True)

原视图与URL配置

视图代码

from django.views.generic import DetailView, ListView
from .models import Vendor, Product

class VendorDetailView(DetailView):
    model = Vendor
    template_name = 'deliveries/vendor_detail.html'

class Productlist(ListView):
    model = Product
    template_name = 'deliveries/vendor_detail.html'
    def get(self, request, *args, **kwargs):
        return super().get(request, *args, **kwargs)

URL配置

from django.urls import path
from . import views

app_name = 'deliveries'
urlpatterns = [
    path('vendordetail/<int:pk>/', views.VendorDetailView.as_view(), name='vendordetail'),
]

原模板代码

{% extends "base.html" %}
{% block style_block %}
    <style>
    #title {
       padding: 20px;
    }
    
    .add-button {
        background-color: #04AA6D;
        border: none;
        color: white;
        padding: 15px 32px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 4px 2px;
        cursor: pointer;
        float: right;
    }
    </style>
{% endblock %}
{% block body_block %}   
        
    <h1 id="title">{{ vendor.name }}</h1>
    
    <a href="{% url "deliveries:addproduct" %}"><button class="button add-button" type="button">Add Product</button></a> 
    <ul>
        <li>{{ vendor.name }}</li>
        <li>{{ vendor.contact_number }}</li>
        <li>{{ vendor.email }}</li>
        <li>{{ vendor.address }}</li>
    </ul>
    
    <a href="{% url "deliveries:uploadproduct" %}"><button class="button add-button" type="button">Upload</button></a> 
    
    <div>
        <h1>Products</h1>

        <table style="width:100%">
            <tr>
                <th>SKU</th>
                <th>Collection</th> 
                <th>Product Name</th> 
                <th>Size</th>
                <th>Vendor Price</th>
                <th>Retail Price</th>
            </tr>
            {% for product in products %}
            <tr>
                <td>{{ product.sku }}</td>
                <td>{{ product.collection }}</td>
                <td>{{ product.product }}</td>
                <td>{{ product.size }}</td>
                <td>${{ product.retail_price }}</td>
                <td>${{ product.vendor_price }}</td>
            </tr>
            {% empty %}
                <li>No products yet.</li>
            {% endfor %}
        </table>
    </div>
    
{% endblock %}

尝试过的无效方案

  1. 重写ListView的get方法,未实现筛选逻辑
def get(self, request, *args, **kwargs):
    return super().get(request, *args, **kwargs)
  1. 基于过滤的ListView实现,未正确关联供应商PK
class ProductList(ListView):
    model = Product
    template_name = 'deliveries/vendor_detail.html'
    context_object_name = 'products'

    def get_queryset(self):
        pk = self.kwargs['pk']
        queryset = super().get_queryset()
        self.filterset = ProductFilter(self.request.GET, queryset=queryset)
        return self.filterset.qs

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'] = self.filterset.form
        return context

正确解决方案

不需要使用两个视图指向同一模板,单个VendorDetailView即可完成需求,核心是在详情视图中把关联产品列表注入到模板上下文:

修改后的视图代码

class VendorDetailView(DetailView):
    model = Vendor
    template_name = 'deliveries/vendor_detail.html'
    context_object_name = 'vendor'  # 显式指定上下文变量名,可选,默认是object

    def get_context_data(self, **kwargs):
        # 获取父类生成的基础上下文
        context = super().get_context_data(**kwargs)
        # 获取当前详情页的供应商实例
        current_vendor = self.get_object()
        # 通过反向关联获取该供应商的所有产品,添加到上下文
        context['products'] = current_vendor.product_set.all()
        return context

优化建议:规范外键命名与关联

为了代码更直观,建议修改Product模型的外键字段名,并设置related_name:

# Product模型中外键字段修改如下
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, related_name='products')

修改后,视图中获取产品列表的代码可简化为:

context['products'] = current_vendor.products.all()

说明

  • 每个HTTP请求只会触发一个视图,双视图指向同一模板的思路不成立,因为另一个视图不会被调用,模板无法获取对应上下文变量。
  • 通过DetailView的get_object()方法可以直接拿到当前供应商实例,利用Django的反向关联(模型名_set或自定义related_name)即可获取该供应商的所有产品。
  • 模板无需修改,原有的{% for product in products %}遍历逻辑可直接使用。

内容的提问来源于stack exchange,提问作者Gio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:19:49