Подтвердить что ты не робот

Переопределить save_model на Django InlineModelAdmin

У меня есть модель, которая имеет поле user, которое должно быть автоматически заполнено от текущего пользователя. Я могу заставить его работать как указано здесь, если поле user находится в стандартном ModalAdmin, но если модель, с которой я работаю, находится в InlineModelAdmin и сохраняться из записи другой модели внутри Admin, это не займет.

4b9b3361

Ответ 1

Вот то, что я считаю лучшим решением. Взял меня, чтобы найти его... этот ответ дал мне подсказки: fooobar.com/questions/316868/...

На вашем admin.py:

class YourInline(admin.TabularInline):
    model = YourInlineModel
    formset = YourInlineFormset

    def get_formset(self, request, obj=None, **kwargs):
        formset = super(YourInline, self).get_formset(request, obj, **kwargs)
        formset.request = request
        return formset

На ваших forms.py:

class YourInlineFormset(forms.models.BaseInlineFormSet):
    def save_new(self, form, commit=True):
        obj = super(YourInlineFormset, self).save_new(form, commit=False)
        # here you can add anything you need from the request
        obj.user = self.request.user

        if commit:
            obj.save()

        return obj

Ответ 2

Я знаю, что опаздываю на вечеринку, но здесь моя ситуация и то, что я придумал, что может быть полезно кому-то еще в будущем.

У меня есть 4 встроенных модели, которым нужен текущий пользователь.

  • 2 как поле типа created_by. (устанавливается один раз при создании)
  • и 2 в качестве поля типа closed_by. (только при условии)

Я использовал ответ rafadev и превратил его в простой микс, который позволяет мне указать имя поля пользователя в другом месте.

Общий набор форм в forms.py

from django.forms.models import BaseInlineFormSet

class SetCurrentUserFormset(forms.models.BaseInlineFormSet):
    """
    This assume you're setting the 'request' and 'user_field' properties
    before using this formset.
    """
    def save_new(self, form, commit=True):
        """
        This is called when a new instance is being created.
        """
        obj = super(SetCurrentUserFormset, self).save_new(form, commit=False)
        setattr(obj, self.user_field, self.request.user)
        if commit:
            obj.save()
        return obj

    def save_existing(self, form, instance, commit=True):
        """
        This is called when updating an instance.
        """
        obj = super(SetCurrentUserFormset, self).save_existing(form, instance, commit=False)
        setattr(obj, self.user_field, self.request.user)
        if commit:
            obj.save()
        return obj

Класс Mixin в вашем admin.py

class SetCurrentUserFormsetMixin(object):
    """
    Use a generic formset which populates the 'user_field' model field
    with the currently logged in user.
    """
    formset = SetCurrentUserFormset
    user_field = "user" # default user field name, override this to fit your model

    def get_formset(self, request, obj=None, **kwargs):
        formset = super(SetCurrentUserFormsetMixin, self).get_formset(request, obj, **kwargs)
        formset.request = request
        formset.user_field = self.user_field
        return formset

Как использовать его

class YourModelInline(SetCurrentUserFormsetMixin, admin.TabularInline):
    model = YourModel
    fields = ['description', 'closing_user', 'closing_date']
    readonly_fields = ('closing_user', 'closing_date')
    user_field = 'closing_user' # overriding only if necessary

Будьте осторожны...

... поскольку этот код mixin будет устанавливать пользователя, который в настоящий момент вошел в систему, каждый раз для каждого пользователя. Если вам нужно, чтобы поле было заполнено только при создании или конкретном обновлении, вам нужно иметь дело с этим в методе сохранения модели. Вот несколько примеров:

class UserOnlyOnCreationExampleModel(models.Model):
    # your fields
    created_by = # user field...
    comment = ...

    def save(self, *args, **kwargs):
        if not self.id:
            # on creation, let the user field populate
            self.date = datetime.today().date()
            super(UserOnlyOnCreationExampleModel, self).save(*args, **kwargs)
        else:
            # on update, remove the user field from the list
            super(UserOnlyOnCreationExampleModel, self).save(update_fields=['comment',], *args, **kwargs)

Или, если вам нужен только пользователь, если задано определенное поле (например, boolean field closed):

def save(self, *args, **kwargs):

    if self.closed and self.closing_date is None:
        self.closing_date = datetime.today().date()
        # let the closing_user field set
    elif not self.closed :
        self.closing_date = None
        self.closing_user = None # unset it otherwise

    super(YourOtherModel, self).save(*args, **kwargs)  # Call the "real" save() method.

Этот код, вероятно, мог бы стать более универсальным, так как я довольно новичок в python, но что будет в моем проекте на данный момент.

Ответ 3

Выполняется только save_model для модели, которую вы редактируете, вместо этого вам нужно будет использовать сигнал post_save для обновления встроенных данных.

(На самом деле не дубликат, но по существу тот же вопрос отвечает в Выполняют ли встроенные формы модели emmit post_save сигналы? (django))

Ответ 4

У меня была аналогичная проблема с пользовательским полем, которое я пытался заполнить встроенной моделью. В моем случае родительская модель также определяла поле пользователя, поэтому я переопределял save на дочерней модели следующим образом:

class inline_model:
    parent = models.ForeignKey(parent_model)
    modified_by = models.ForeignKey(User,editable=False) 
    def save(self,*args,**kwargs):
        self.modified_by = self.parent.modified_by
        super(inline_model,self).save(*args,**kwargs)

Пользовательское поле первоначально было автоматически заполнено родительской моделью, переопределив save_model в ModelAdmin для родительской модели и назначив

obj.modified_by = request.user

Имейте в виду, что если у вас также есть отдельная страница администратора для дочерней модели, вам понадобится другой механизм для синхронизации родительских и дочерних полей modified_by (например, вы можете переопределить save_model на child ModelAdmin и обновите/сохраните поле modified_by родителя перед вызовом save для дочернего элемента).

Я не нашел хороший способ справиться с этим, если пользователь не находится в родительской модели. Я не знаю, как получить request.user с помощью сигналов (например, post_save), но, возможно, кто-то еще может дать более подробную информацию об этом.

Ответ 5

Сохраняет ли другая модель пользователя? В этом случае вы можете использовать сигнал post_save, чтобы добавить эту информацию в набор встроенной модели.

Ответ 6

Попробовали ли вы выполнить выборочную проверку в админе, как описано в документации? Переопределение функции clean_user() в форме модели может сделать трюк для вас.

Еще один, более привлекательный вариант приходит на ум. Вы можете переопределить шаблон администратора, который отображает форму изменения. Переопределение формы изменения позволит вам создать собственный тег шаблона, который передает зарегистрированного пользователя в ModelForm. Затем вы можете написать пользовательскую функцию init в форме модели, которая автоматически устанавливает пользователя. Этот ответ дает хороший пример того, как это сделать, а также ссылку на b-список, которую вы ссылаетесь в вопросе.