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.CurrentUserMiddlewarecms.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,因为他实际上并没有为我工作。

  1. 应用/中间件/ 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 
  2. 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', ] 
  3. 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)