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

Django Admin实现站点-商品-价格关联及内联价格编辑

Django Admin 多站点商品分价实现方案

模型层修改(models.py)

直接用原生ManyToManyField关联Site和Item的方式无法存储「同商品在不同站点售价不同」的差异化数据,需要替换为带中间表的多对多关系,把价格字段存在中间关联表中:

  • 保留原有Department、Item模型逻辑不变
  • 新增SiteItem中间表,外键分别关联Site和Item,额外增加price字段存储对应站点的商品售价,增加联合唯一约束避免同站点同商品重复建档
  • 修改Site模型的items字段,指定through参数指向新建的中间表

修改后的完整models.py代码:

from django.db import models

class Department(models.Model):
    number = models.IntegerField(unique=True)
    description = models.CharField(max_length=30)

    def __str__(self):
        return f"{self.number}: {self.description}"

    class Meta:
        ordering = ["number"]


class Item(models.Model):
    upc = models.CharField(max_length=30, unique=True, verbose_name="UPC")
    description = models.CharField(max_length=30)
    department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return f"{self.upc}: {self.description}"

    class Meta:
        ordering = ["upc"]


# 站点-商品关联中间表,存储对应站点的商品专属售价
class SiteItem(models.Model):
    site = models.ForeignKey("Site", on_delete=models.CASCADE, related_name="site_items")
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name="site_items")
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="售价(美元)")

    class Meta:
        # 约束同一站点下同个商品仅能存在一条价格记录
        constraints = [
            models.UniqueConstraint(fields=["site", "item"], name="unique_site_item_price")
        ]
        ordering = ["item__upc"]

    def __str__(self):
        return f"{self.site} - {self.item}: ${self.price}"


class Site(models.Model):
    site = models.IntegerField(unique=True)
    address = models.CharField(max_length=50)
    # 指定through参数关联自定义中间表
    items = models.ManyToManyField(Item, through=SiteItem, related_name="sites")

    def __str__(self):
        return str(self.site)

    class Meta:
        ordering = ["site"]

修改完模型后依次执行python manage.py makemigrations、python manage.py migrate同步数据库结构。如果之前已经存在Site和Item的关联存量数据,迁移时会提示给price字段设置临时默认值,输入0即可,迁移完成后再到后台修正实际售价。

后台配置修改(admin.py)

替换原来的内联类为基于中间表SiteItem的TabularInline,把商品基础信息(UPC、描述、所属部门)配置为只读列,价格字段设为可编辑列,就能实现「商品描述右侧直接修改价格」的内联效果,完全不需要自定义模板。

修改后的完整admin.py代码:

from django.contrib import admin
from .models import Site, Department, Item, SiteItem

class SiteItemInline(admin.TabularInline):
    model = SiteItem
    can_delete = False
    verbose_name = '站点在售商品'
    verbose_name_plural = '站点在售商品'
    extra = 0
    # 按展示顺序配置列:商品选择器、只读商品属性、可编辑价格
    fields = ("item", "item_upc", "item_description", "item_department", "price")
    # 商品基础信息设为只读,避免在站点编辑页误改全局商品属性
    readonly_fields = ("item_upc", "item_description", "item_department")

    # 取关联商品的UPC字段
    def item_upc(self, obj):
        return obj.item.upc
    item_upc.short_description = "UPC"

    # 取关联商品的描述字段
    def item_description(self, obj):
        return obj.item.description
    item_description.short_description = "商品描述"

    # 取关联商品的所属部门字段
    def item_department(self, obj):
        return obj.item.department
    item_department.short_description = "所属部门"

    # 保留原有配置,禁用商品选择框的关联新增/修改/删除按钮
    def formfield_for_dbfield(self, db_field, request, **kwargs):
        formfield = super().formfield_for_dbfield(db_field, request, **kwargs)
        if db_field.name == "item":
            formfield.widget.can_delete_related = False
            formfield.widget.can_change_related = False
            formfield.widget.can_add_related = False
        return formfield


@admin.register(Site)
class SiteAdmin(admin.ModelAdmin):
    fields = ("site", "address")
    list_display = ("site", "address")
    inlines = (SiteItemInline, )


@admin.register(Department)
class DepartmentAdmin(admin.ModelAdmin):
    fields = ("number", "description")
    list_display = ("number", "description")


@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):
    fields = ("upc", "description", "department")
    list_display = ("upc", "description", "department")
    save_as = True
    
    def formfield_for_dbfield(self, *args, **kwargs):
        formfield = super().formfield_for_dbfield(*args, **kwargs)
        formfield.widget.can_delete_related = False
        formfield.widget.can_change_related = False
        formfield.widget.can_add_related = False 
        return formfield

配置完成后打开站点编辑页,内联表格会逐行展示该站点所有在售商品,价格列支持直接输入修改,保存后数据会直接写入中间表,和参考截图的交互效果完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:57:14