У меня есть модель, которая имеет поле user
, которое должно быть автоматически заполнено от текущего пользователя. Я могу заставить его работать как указано здесь, если поле user
находится в стандартном ModalAdmin, но если модель, с которой я работаю, находится в InlineModelAdmin
и сохраняться из записи другой модели внутри Admin, это не займет.
Переопределить save_model на Django InlineModelAdmin
Ответ 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-список, которую вы ссылаетесь в вопросе.