Django:保存模型时填充用户标识
我有一个与被链接到标准Django用户模型的created_by字段的模型。 保存模型时,我需要自动填充当前用户的ID。 我不能在pipe理层做到这一点,因为网站的大部分地方不会使用内置的pipe理员。 任何人都可以build议我应该如何去做呢?
如果你想在pipe理员和其他地方都能使用的东西,你应该使用自定义模型。 基本的想法是覆盖__init__
方法来获取额外的参数 – 请求 – 并将其作为表单的一个属性存储,然后在保存到数据库之前覆盖保存方法来设置用户标识。
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
Daniel的答案不会直接为pipe理员工作,因为您需要传递请求对象。 您可以通过重写ModelAdmin
类中的ModelAdmin
方法来做到这一点,但是可能更容易远离表单自定义,并在您的ModelAdmin
重写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()
最不显眼的方法是使用CurrentUserMiddleware
将当前用户存储在线程本地对象中:
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
现在,您只需要在authentication中间件之后将此中间件添加到MIDDLEWARE_CLASSES。
settings.py
MIDDLEWARE_CLASSES = ( ... 'django.contrib.auth.middleware.AuthenticationMiddleware', ... 'current_user.CurrentUserMiddleware', ... )
您的模型现在可以使用get_current_user
函数访问用户,而不必传递请求对象。
models.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
函数来检索当前用户。
这整个方法窃听了我。 我只想说一次,所以我在中间件中实现了它。 在您的身份validation中间件后添加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
如果您使用基于类的视图Daniel的答案需要更多。 添加以下内容以确保您的ModelForm对象中的请求对象可用
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
另外,如前所述,您需要在ModelForm.save()的末尾返回obj
以下是我如何使用通用视图进行操作:
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)
为了在pipe理站点中这样做,请参阅使用Djangopipe理站点自动填充created_by字段
forms.ModelForm中的'save'方法返回保存的实例。
您应该添加一个最后一行到MyModelForm:
…
返回obj
如果使用create_object或update_object通用视图,则此更改是必需的。
他们使用保存的对象来做redirect。
我不相信丹尼尔的答案是最好的,因为它通过总是保存对象来改变模型表单的默认行为。
我将使用的代码:
forms.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
为了将来的参考,我发现有关这个问题的最佳解决scheme
https://pypi.python.org/pypi/django-crum/0.6.1
这个库包含一些中间件。 build立这个库后,只需重写模型的保存方法并执行以下操作,
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)
如果您为所有模型创build并抽象模型并inheritance模型,则会自动填充created_by和changed_by字段。
@bikeshedder你的答案没有为我工作我越来越
“NameError:名称'get_current_user'未定义”
和
导入模块时出错:'没有名为'current_user'的模块?
PS。 我使用python 3.5和django 1.11
基于自行车的答案,我find了一个解决scheme,因为他实际上并没有为我工作。
-
应用/中间件/ 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
-
settings.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', ]
-
model.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
请注意,如果你正在寻找这个,但添加以下内容
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)