Django InlineModelAdmin - 在保存时从请求设置内联字段(自动设置用户字段)(save_formset与save_model)

17 投票
3 回答
12147 浏览
提问于 2025-04-15 23:48

我有两个模型,一个是主模型 MainModel,另一个是相关的内联模型 InlineModel,我想在管理后台把它们显示为内联。这个 InlineModel 可以用来记录关于主模型的笔记,并且需要跟踪当前登录的管理员用户所做的更改。虽然这看起来很简单(而且文档中也有示例说明当用户字段在主模型中时该怎么做),但当用户字段在内联模型上时,我就搞不清楚了。

具体来说,我的目标是:

  1. 用户编辑主模型 MainModel
  2. 用户添加一个 InlineModel,但不填写用户字段
  3. 用户点击保存
  4. 代码自动填写新创建的 InlineModel 实例的用户字段
  5. (额外要求!对于已有的实例,用户字段是只读的;对于新的内联模型,用户字段是隐藏的)

我的问题是:

  1. 这样做对吗?因为 save_model 对 InlineModelAdmin 实例没有被调用,这样是否合理?
  2. 这样做能让我保存而不出错吗?(因为用户字段是必填的,验证会标记出错)
  3. 我该如何隐藏新内联模型的用户输入字段,并让已有内联模型的用户字段变为只读?

这是我目前的想法:


#models.py
class MainModel(models.Model):
    some_info = models.IntegerField()

class InlineModel(models.Model):
    main = models.ForeignKey(MainModel)
    data = models.CharField(max_length=255)
    user = models.ForeignKey('auth.User')

#admin.py
class InlineModelInline(admin.TabularInline):
    model = InlineModel
    fields = ('data', 'user')
    #readonly_fields = ('data', 'user') #Bonus question later

class MainModelAdmin(admin.ModelAdmin):
    list_display = ('id', 'some_info')
    inlines = [InlineModelInline]

    #def save_model(self, request, obj, form, change):
        #http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_model
        #Only called for MainModel, not for any of the inlines
        #Otherwise, would be ideal

    def save_formset(self, request, form, formset, change):
        #http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_formset
        #Experimenting showd this is called once per formset (where the formset is a group of inlines)
        #See code block at http://code.djangoproject.com/browser/django/tags/releases/1.2.1/django/contrib/admin/options.py#L894
        if not isinstance(formset.model, InlineModel):
            return super(MainModelAdmin, self).save_formset(request, form, formset, change)
        instances = formset.save(commit=False)
        for instance in instances:
            if not instance.pk:
                instance.user = request.user
        instance.save()
        formset.save_m2m()

3 个回答

0

关于附加问题:“我怎么能让现有的内联显示‘数据’为只读,但在添加新的内联时仍然可以编辑它?”

我使用了两个内联来处理同一个模型:

#admin.py
class InlineModelInline(admin.TabularInline):
    model = InlineModel
    extra = 1
    max_num = 1

#admin.py
class InlineModelExistingInline(admin.TabularInline):
    model = InlineModel
    readonly_fields = ('data', 'user') #All Fields here except pk
    can_delete = False
    extra = 0
    max_num = 0

class MainModelAdmin(admin.ModelAdmin):
    ...
    inlines = [InlineModelInline, InlineModelExistingInline]
    ...
1

这个方法对我有效。这个方法不让我删除内联项目。

def save_formset(self, request, form, formset, change):
    for form in formset.forms:
        form.instance.user = request.user
    formset.save()
11

我已经解决了我问题的前半部分:

def save_formset(self, request, form, formset, change):
    if formset.model != InlineModel:
        return super(MainModelAdmin, self).save_formset(request, form, formset, change)
    instances = formset.save(commit=False)
    for instance in instances:
        if not instance.pk:
            instance.user = request.user
        instance.save()
    formset.save_m2m()

现在我对额外的功能感兴趣:

  1. 由于验证规则,我在添加新的内联时必须选择一个用户。我猜测可以不把 'user' 字段放在我的 InlineModelInline.fields 元组里,但这样的话,现有的 InlineModel 实例就看不到作者了。(补充:把 'user' 加到 readonly_fields 里就可以解决这个问题)

  2. (补充)我该如何让现有的内联显示 'data' 为只读,但在添加新的内联时又能编辑它呢?

撰写回答