Переопределить save_model в Django InlineModelAdmin - PullRequest
17 голосов
/ 14 ноября 2009

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

Ответы [ 6 ]

13 голосов
/ 13 апреля 2015

Вот то, что я считаю лучшим решением. Мне потребовалось время, чтобы найти его ... этот ответ дал мне подсказки: https://stackoverflow.com/a/24462173/2453104

На вашем 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

В ваших формах.

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
5 голосов
/ 22 мая 2015

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

У меня есть 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

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

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

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)

Или если вам нужен только пользователь, если задано определенное поле (например, логическое поле 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, но это то, что будет в моем проекте на данный момент.

2 голосов
/ 31 января 2010

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

(Не совсем дубликат, но, по сути, на тот же вопрос отвечают в Излучают ли встроенные модели модели сигналы post_save? (Django) )

1 голос
/ 16 августа 2010

У меня была похожая проблема с пользовательским полем, которое я пытался заполнить во встроенной модели. В моем случае в родительской модели также было определено пользовательское поле, поэтому я переопределил 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

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

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

0 голосов
/ 20 ноября 2009

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

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

0 голосов
/ 14 ноября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...