如何在 Django Admin 中使用“list_display”显示多对多字段?

问题描述 投票:0回答:4
class Product(models.Model):
    products = models.CharField(max_length=256)
    
    def __unicode__(self):
        return self.products

class PurchaseOrder(models.Model):
    product = models.ManyToManyField('Product')
    vendor = models.ForeignKey('VendorProfile')
    dollar_amount = models.FloatField(verbose_name='Price')

我有那个密码。不幸的是,错误出现在 admin.py 中

ManyToManyField

class PurchaseOrderAdmin(admin.ModelAdmin):
    fields = ['product', 'dollar_amount']
    list_display = ('product', 'vendor')

错误说:

'PurchaseOrderAdmin.list_display[0]', 'product' 是 ManyToManyField 这是不支持的。

但是,当我从

'product'
中取出
list_display
时,它会编译。那么如何在
'product'
中显示
list_display
而不会出错?

edit:也许更好的问题是如何在

ManyToManyField
中显示
list_display

python django django-models django-admin django-queryset
4个回答
232
投票

您可能无法直接进行。 来自

list_display

的文档

ManyToManyField 字段不受支持,因为这需要 为表中的每一行执行单独的 SQL 语句。如果你 尽管如此,还是想这样做,给你的模型一个自定义方法,然后添加 该方法的名称到 list_display。 (有关自定义的更多信息,请参见下文 list_display 中的方法。)

你可以这样做:

class PurchaseOrderAdmin(admin.ModelAdmin):
    fields = ['product', 'dollar_amount']
    list_display = ('get_products', 'vendor')

    def get_products(self, obj):
        return "\n".join([p.products for p in obj.product.all()])

或者定义一个模型方法,然后使用它

class PurchaseOrder(models.Model):
    product = models.ManyToManyField('Product')
    vendor = models.ForeignKey('VendorProfile')
    dollar_amount = models.FloatField(verbose_name='Price')

    def get_products(self):
        return "\n".join([p.products for p in self.product.all()])

在管理员

list_display

list_display = ('get_products', 'vendor')

28
投票

你可以通过这种方式做到这一点,请检查以下代码片段:

class Categories(models.Model):
    """ Base category model class """

    title       = models.CharField(max_length=100)
    description = models.TextField()
    parent      = models.ManyToManyField('self', default=None, blank=True)
    when        = models.DateTimeField('date created', auto_now_add=True)

    def get_parents(self):
        return ",".join([str(p) for p in self.parent.all()])

    def __unicode__(self):
        return "{0}".format(self.title)

而在你的admin.py模块调用方法如下:

class categories(admin.ModelAdmin):
    list_display    = ('title', 'get_parents', 'when')

16
投票

如果你想保存额外的查询,你可以在get_queryset方法中使用

prefetch_related
,如下所示:

class PurchaseOrderAdmin(admin.ModelAdmin):
    fields = ['product', 'dollar_amount']
    list_display = ('get_products', 'vendor')

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.prefetch_related('product')

    def get_products(self, obj):
        return ",".join([p.products for p in obj.product.all()])

根据 Docs,通过这种方式,只需要一个额外的查询来获取所有

Product
实例的相关
PurchaseOrder
项,而不是每个
PurchaseOrder
实例需要一个查询。


0
投票

例如,

Category
Product
模型具有多对多关系,如下所示。 *我使用Django 4.2.1:

# "models.py"

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

class Product(models.Model):
    categories = models.ManyToManyField(Category)
    name = models.CharField(max_length=50)
    price = models.DecimalField(decimal_places=2, max_digits=5)

然后就是

Category
Product
管理员如下图:

from django.contrib import admin
from .models import Category, Product

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('id', 'name')
    ordering = ('id',)

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'price')
    ordering = ('id',)

然后,

Category
admin 有 5 个对象,如下所示:

并且,

Product
admin 有 6 个对象,如下所示:

现在,用

@admin.display()
定义
get_products
get_categories(),然后分别在Category
Product
admins中将它们设置为
list_display
,如下所示。 *
description
中的参数
@admin.display()
可以将 Django Admin 中的列从
GET PRODUCTS
GET CATEGORIES
分别重命名为
PRODUCTS
CATEGORIES

# "admin.py"

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'get_products')
    ordering = ('id',)            # Here

    # Here
    @admin.display(description='products')
    def get_products(self, obj):
        return [product.name for product in obj.product_set.all()]

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):      # Here  
    list_display = ('id', 'name', 'price', 'get_categories')
    ordering = ('id',)
    
    # Here
    @admin.display(description='categories')
    def get_categories(self, obj):
        return [category.name for category in obj.categories.all()]

然后,

PRODUCTS
列显示在
Category
管理中,如下所示:

然后,

CATEGORIES
列显示在
Product
管理中,如下所示:

此外,您还可以通过在

PRODUCTS
CATEGORIES
模型中分别定义
get_products()
get_categories()
@admin.display()
来显示
Category
Product
列,如下所示:

# "models.py"

from django.db import models
from django.contrib import admin

class Category(models.Model):
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

    # Here
    @admin.display(description='products')
    def get_products(self):
        return [product.name for product in self.product_set.all()]

class Product(models.Model):
    categories = models.ManyToManyField(Category)
    name = models.CharField(max_length=50)
    price = models.DecimalField(decimal_places=2, max_digits=5)

    # Here
    @admin.display(description='categories')
    def get_categories(self):
        return [category.name for category in self.categories.all()]

然后在

list_display
Category
管理员分别设置为
Product
如下图:

# "admin.py"

...

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'get_products')
    ordering = ('id',)            # Here

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):      # Here
    list_display = ('id', 'name', 'price', 'get_categories')
    ordering = ('id',)
© www.soinside.com 2019 - 2024. All rights reserved.