我有一个带有 created_by 字段的模型,该字段链接到标准 Django User 模型。保存模型时,我需要使用当前用户的 ID 自动填充它。我不能在 Admin 层执行此操作,因为站点的大部分部分都不会使用内置的 Admin。任何人都可以建议我应该如何去做吗?
13 回答
更新 2020-01-02
⚠ 以下答案从未更新到最新的 Python 和 Django 版本。自从几年前写这篇文章以来,已经发布了包来解决这个问题。现在我强烈推荐使用django-crum
which 实现相同的技术但有测试并定期更新:https ://pypi.org/project/django-crum/
最不显眼的方法是使用 aCurrentUserMiddleware
将当前用户存储在线程本地对象中:
current_user.py
from threading import local
_user = local()
class CurrentUserMiddleware(object):
def process_request(self, request):
_user.value = request.user
def get_current_user():
return _user.value
现在您只需在身份验证中间件之后将此中间件添加到您的 MIDDLEWARE_CLASSES中。
设置.py
MIDDLEWARE_CLASSES = (
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
...
'current_user.CurrentUserMiddleware',
...
)
您的模型现在可以使用该get_current_user
函数访问用户,而无需传递请求对象。
模型.py
from django.db import models
from current_user import get_current_user
class MyModel(models.Model):
created_by = models.ForeignKey('auth.User', default=get_current_user)
暗示:
如果您使用 Django CMS,您甚至不需要定义自己的 CurrentUserMiddleware,但可以使用cms.middleware.user.CurrentUserMiddleware
和cms.utils.permissions.get_current_user
检索当前用户的函数。
如果您想要在管理员和其他地方都可以使用的东西,您应该使用自定义模型表单。基本思想是重写该__init__
方法以获取额外的参数-request-并将其存储为表单的属性,然后还重写save方法以在保存到数据库之前设置用户ID。
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(MyModelForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(MyModelForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
return obj
丹尼尔的回答不会直接对管理员起作用,因为您需要传入请求对象。您可能可以通过覆盖类中的get_form
方法来做到这一点,ModelAdmin
但远离表单自定义可能更容易,只需覆盖save_model
您的ModelAdmin
.
def save_model(self, request, obj, form, change):
"""When creating a new object, set the creator field.
"""
if not change:
obj.creator = request.user
obj.save()
这整个方法让我大吃一惊。我想只说一次,所以我在中间件中实现了它。只需在身份验证中间件之后添加 WhodidMiddleware。
如果您的 created_by 和 modified_by 字段设置为,editable = False
那么您根本不必更改任何表单。
"""Add user created_by and modified_by foreign key refs to any model automatically.
Almost entirely taken from https://github.com/Atomidata/django-audit-log/blob/master/audit_log/middleware.py"""
from django.db.models import signals
from django.utils.functional import curry
class WhodidMiddleware(object):
def process_request(self, request):
if not request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
if hasattr(request, 'user') and request.user.is_authenticated():
user = request.user
else:
user = None
mark_whodid = curry(self.mark_whodid, user)
signals.pre_save.connect(mark_whodid, dispatch_uid = (self.__class__, request,), weak = False)
def process_response(self, request, response):
signals.pre_save.disconnect(dispatch_uid = (self.__class__, request,))
return response
def mark_whodid(self, user, sender, instance, **kwargs):
if 'created_by' in instance._meta.fields and not instance.created_by:
instance.created_by = user
if 'modified_by' in instance._meta.fields:
instance.modified_by = user
这是我使用通用视图的方法:
class MyView(CreateView):
model = MyModel
def form_valid(self, form):
object = form.save(commit=False)
object.owner = self.request.user
object.save()
return super(MyView, self).form_valid(form)
If you are using class based views Daniel's answer needs more. Add the following to ensure that the request object is available for us in your ModelForm object
class BaseCreateView(CreateView):
def get_form_kwargs(self):
"""
Returns the keyword arguments for instanciating the form.
"""
kwargs = {'initial': self.get_initial()}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
'request': self.request})
return kwargs
Also, as already mentioned, you need to return the obj at the end of ModelForm.save()
使用类似的东西有什么问题:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ['created_by']
def save(self, user):
obj = super().save(commit = False)
obj.created_by = user
obj.save()
return obj
现在myform.save(request.user)
在视图中调用它。
这是ModelForm的保存函数,它只有一个commit
参数。
根据Bikeshedder的回答,我找到了一个解决方案,因为他实际上并没有为我工作。
应用程序/中间件/current_user.py
from threading import local _user = local() class CurrentUserMiddleware(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): _user.value = request.user return self.get_response(request) def get_current_user(): return _user.value
设置.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'common.middleware.current_user.CurrentUserMiddleware', ]
模型.py
from common.middleware import current_user created_by = models.ForeignKey(User, blank=False, related_name='created_by', editable=False, default=current_user.get_current_user)
我正在使用 python 3.5 和 django 1.11.3
为了将来的参考,我找到了关于这个主题的最佳解决方案:
https://pypi.python.org/pypi/django-crum/0.6.1
这个库由一些中间件组成。设置这个库后,只需覆盖模型的保存方法并执行以下操作,
from crum import get_current_user
def save(self, *args, **kwargs):
user = get_current_user()
if not self.pk:
self.created_by = user
else:
self.changed_by = user
super(Foomodel, self).save(*args, **kwargs)
如果您为所有模型创建和抽象模型并从它继承,您将获得自动填充的 created_by 和 changed_by 字段。
来自 Django 文档Models 和 request.user:
" 要跟踪使用 CreateView 创建对象的用户,您可以使用自定义 ModelForm。在视图中,确保您没有将 [用户字段] 包含在要编辑的字段列表中,并将 form_valid() 覆盖为添加用户:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(LoginRequiredMixin, CreateView):
model = Author
fields = ['name']
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
我不相信丹尼尔的答案是最好的,因为它通过始终保存对象来改变模型表单的默认行为。
我会使用的代码:
表格.py
from django import forms
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(MyModelForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
obj = super(MyModelForm, self).save(commit=False)
if obj.created_by_id is None:
obj.created_by = self.user
if commit:
obj.save()
return obj
forms.ModelForm 中的“保存”方法返回保存的实例。
您应该在 MyModelForm 中添加最后一行:
...
return obj
如果您使用 create_object 或 update_object 通用视图,则此更改是必要的。
他们使用保存的对象进行重定向。
请注意确定您是否正在寻找这个,但添加以下内容
user = models.ForeignKey('auth.User')
到模型将有助于将用户 ID 添加到模型中。
在下文中,每个层次结构都属于一个用户。
class Hierarchy(models.Model):
user = models.ForeignKey('auth.User')
name = models.CharField(max_length=200)
desc = models.CharField(max_length=1500)