如何将计算字段添加到 django 管理内联

How to add a calculated field to a django admin inline

我正在尝试将计算字段添加到 Django Admin Inline。考虑这组 类:

models.py:

class MyGroup(models.Model):
  group_name = models.CharField()
  size = models.IntegerField()

class MyUser(models.Model):
  user_name = models.CharField()
  groups = models.ManyToMany(MyGroup, related_name="users", through=MyMembership)
    
class MyMembership(models.Model):
  group = models.ForeignKey(MyGroup)
  user = models.ForeignKey(MyUser)
  timestamp = models.DateTimeField(auto_add_now=True)

我想在内联中显示 group.size 字段。我认为以下代码可以工作(基于this answer):

admin.py:


class MyMembershipInline(admin.TabularInline):
  model = MyUser.groups.through
  fields = (
    "group",
    "timestamp",
    "size",
  )
  readonly_fields = (
    "timestamp",
    "size",
  )

  def size(self, instance):
    return instance.group.size

@admin.register(MyUser):
class MyUserAdmin(admin.ModelAdmin)
  fields = ("user_name",)
  inlines = (MyMembershipInline,)

但我收到以下错误:

Unknown field(s) (size) specified for MyMembership

有什么建议吗?

我不得不在内联中使用自定义 ModelForm。我是这样做的:

admin.py:

class MyMembershipInlineForm(forms.ModelForm):
  class Meta:
    model = MyUser.groups.through
    fields = ("group", )
    readonly_fields = (
      "timestamp",
      "size",
    )
  size = forms.IntegerField(disabled=True)

  def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    my_membership = self.instance
    self.initial = {
      "size": my_membership.group.size if my_membership.pk else None
    }

class MyMembershipInline(admin.TabularInline):
  model = MyUser.groups.through
  fields = (
    "group",
    "timestamp",
    "size",
  )
  form = MyMembershipInlineForm

@admin.register(MyUser):
class MyUserAdmin(admin.ModelAdmin)
  fields = ("user_name",)
  inlines = (MyMembershipInline,)

在寻找此问题的解决方案时,我在以下 URL 找到了适合我的解决方案: https://www.boris.co/2013/04/computed-field-on-tabularinline.html

给出的例子是:

class StatsInline(admin.TabularInline):
    model = Stats
    fields = ('clicked', 'shown', 'avg')
    readonly_fields = ('avg',)
    verbose_name = 'Stats'
    verbose_name_plural = 'Stats'
    can_delete = False

    def avg(self, obj):
        return float(obj.clicked) / obj.shown if obj.shown else 0

需要在fieldsreadonly_fields属性中提供计算字段。