在Django InlineModelAdmin中重写save_model
我有一个模型,其中有一个 user
字段,这个字段需要自动填充为当前登录的用户。如果这个 user
字段在一个标准的 ModalAdmin 中,我可以按照指定的方法让它工作,具体可以参考 这里。但是,如果我正在处理的模型是在一个 InlineModelAdmin
中,并且是从另一个模型的记录中保存的,那么它就无法正常工作了。
6 个回答
2
只有你正在编辑的模型的 save_model
会被执行,如果想要更新嵌套的数据,你需要使用 post_save
信号。
(这不是完全重复的问题,但基本上在 这个链接 中回答了类似的问题,讨论的是嵌套模型表单是否会发出 post_save 信号。)
6
我知道我来得有点晚,但这是我的情况和我想到的解决办法,希望对将来其他人有帮助。
我有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
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 还比较陌生,但目前这就是我项目中的实现。
15
我觉得这是最好的解决办法。我花了一些时间才找到它……这个答案给了我线索: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
在你的 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