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

时间:2009-05-14 10:02:43

标签: django django-models django-authentication

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

13 个答案:

答案 0 :(得分:33)

如果您想要在管理员和其他地方都可以使用的东西,您应该使用自定义模型。基本思想是覆盖__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

答案 1 :(得分:25)

最不受阻碍的方法是使用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

现在,您只需要在认证中间件之后将此中间件添加到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函数来检索当前用户。

答案 2 :(得分:22)

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()

答案 3 :(得分: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

答案 4 :(得分: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

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

答案 5 :(得分:7)

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

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)

答案 6 :(得分:3)

根据bikeshedder的答案,我找到了一个解决方案,因为他实际上并没有为我工作。

  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)
    
  4. 我使用的是python 3.5和django 1.11.3

答案 7 :(得分:1)

forms.ModelForm中的'save'方法返回已保存的实例。

您应该向MyModelForm添加最后一行:
...
return obj

如果您使用create_object或update_object通用视图,则必须进行此更改 他们使用保存的对象进行重定向。

答案 8 :(得分:1)

我不相信Daniel的答案是最好的,因为它通过始终保存对象来更改模型表单的默认行为。

我将使用的代码:

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

答案 9 :(得分:1)

使用类似这样的问题是什么?

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's save function,它只有一个commit参数。

答案 10 :(得分:0)

为了将来参考,我找到了关于这个主题的最佳解决方案:

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字段。

答案 11 :(得分:0)

摘自Django文档Models and 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)

答案 12 :(得分:-6)

请注意,如果您正在寻找此项,请添加以下内容

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)