Django:保存模型时填充用户ID

45 django django-models django-authentication

我有一个带有created_by字段的模型,该字段链接到标准的Django User模型.保存模型时,我需要使用当前用户的ID自动填充它.我不能在Admin层执行此操作,因为站点的大多数部分都不会使用内置的Admin.任何人都可以建议我应该怎么做?

Dan*_*man 34

如果您想要在管理员和其他地方都能工作的东西,您应该使用自定义模型.基本思想是覆盖__init__方法以获取额外的参数 - 请求 - 并将其存储为表单的属性,然后在保存到数据库之前覆盖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
Run Code Online (Sandbox Code Playgroud)

  • 只是为了完成答案.为了能够访问`__init__`中的`request`对象,必须在视图中初始化表单时传递它.例如:`myform = MyForm(request.POST,request = request)` (4认同)
  • 我无法弄清楚如何让管理员使用“请求”对象初始化 MyModelForm。甚至可以不修改 contrib.admin 代码本身吗? (2认同)

bik*_*der 27

最不受阻碍的方法是使用a 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
Run Code Online (Sandbox Code Playgroud)

现在,您只需要在身份验证中间件之后将此中间件添加到MIDDLEWARE_CLASSES .

settings.py

MIDDLEWARE_CLASSES = (
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...
    'current_user.CurrentUserMiddleware',
    ...
)
Run Code Online (Sandbox Code Playgroud)

您的模型现在可以使用该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)
Run Code Online (Sandbox Code Playgroud)

暗示:

如果您在使用Django CMS你甚至都不需要定义自己的CurrentUserMiddleware但可以使用cms.middleware.user.CurrentUserMiddlewarecms.utils.permissions.get_current_user函数来检索当前用户.

  • 看起来很彻底,但在 django 1.6.2 和 Python 3.3.2 中,当我使用 South ```./manage.py schemamigration app --initial``` 时,我收到以下错误摘录:`return _user.value AttributeError: ' _thread._local' 对象没有属性 'value'``` (2认同)
  • 这是正确的,因为中间件永远不会被调用.要使South(可能还有许多其他管理命令)工作,你需要在`get_current_user`中捕获`AttributeError`并返回`None`. (2认同)
  • @rara_tiru 这取决于您的需求。通过使用线程本地对象和中间件,在请求期间保存的任何对象都将填充“created_by”字段。该代码段仅修补管理员。 (2认同)

Tim*_*her 23

Daniel的答案不会直接用于管理员,因为您需要传入请求对象.您可以通过覆盖类中的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()
Run Code Online (Sandbox Code Playgroud)


min*_*ace 13

整个方法都让我感到害怕.我只想说一次,所以我在中间件中实现了它.只需在身份验证中间件后添加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
Run Code Online (Sandbox Code Playgroud)

  • 此代码不是线程安全的。如果您使用的是使用线程的WSGI容器(例如Apache2 + mod_wsgi,uWSGI等),则最终可能会存储错误的用户。 (2认同)

yre*_*uta 8

这是我如何使用通用视图:

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)
Run Code Online (Sandbox Code Playgroud)


小智 7

如果您使用基于类的视图,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
Run Code Online (Sandbox Code Playgroud)

另外,如前所述,您需要在ModelForm.save()的末尾返回obj