Django InlineModelAdmin - установить встроенное поле из запроса на сохранение (автоматически установить поле пользователя) (save_formset vs save_model)

У меня есть две модели: MainModel и связанная с ними InlineModel, которую я хотел бы показать как встроенный администратор. Этот InlineModel может быть использован, например, для создания заметок о модели и должен отслеживать внесенные в журнал администратором изменения. Хотя это кажется простым (и действительно, документы показывают пример для этого, когда поле пользователя является частью MainModel), я не могу понять его, когда поле находится в Inline.

Чтобы быть конкретным, моя цель:

  • Пользовательские изменения MainModel
  • Пользователь добавляет InlineModel, не заполняя поле пользователя
  • Пользователь нажимает кнопку сохранения
  • Код заполняет поле пользователя для вновь созданных экземпляров InlineModel.
  • (Поле пользователя Bonus! доступно только для существующих экземпляров и скрыто для новых встроенных линий)

И мои вопросы:

  • Это правильно? Его слишком bas save_model не вызывается для экземпляров InlineModelAdmin
  • Делает ли это так, чтобы я мог сохранить, не вызывая ошибки? (требуется пользователь, флаги проверки его)
  • Как я могу скрыть поле ввода пользователя для новых строк и иметь его только для существующих строк?

Вот мои текущие идеи:


#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()

Ответ 1

Я решил первую половину своего вопроса:

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()

Теперь меня интересует поведение бонуса:

  • Я должен выбрать пользователя при добавлении новой строки из-за правил проверки. Мое лучшее предположение состоит в том, чтобы не включать поле пользователя в мой кортеж InlineModelInline.fields, но тогда это не покажет автору существующие экземпляры InlineModel. (Изменить: добавляет 'user' to readonly_fields работает здесь)

  • (Edit) Как я могу сделать существующие inline визуализировать "данные" как только для чтения, но все же иметь возможность редактировать его при добавлении новой строки?

Ответ 2

Это сработало для меня. Этот подход не позволит мне удалять элементы Inline.

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

Ответ 3

Чтобы ответить на вопрос о бонусе: "Как я могу сделать существующие потоковые данные визуализации" как только для чтения, но все же иметь возможность редактировать его при добавлении нового встроенного? ":

Я использую две строчки для одной и той же модели:

#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]
    ...