57

標準の Django User モデルにリンクされた created_by フィールドを持つモデルがあります。モデルを保存するときに、現在のユーザーの ID を自動的に入力する必要があります。サイトのほとんどの部分は組み込みの管理者を使用しないため、管理者レイヤーでこれを行うことはできません。これについてどうすればよいか、誰にもアドバイスできますか?

4

13 に答える 13

38

UPDATE 2020-01-02
⚠ 次の回答は、最新の Python および Django バージョンに更新されていません。数年前にこれを書いて以来、この問題を解決するパッケージがリリースされました。現在django-crum、同じ手法を実装していますが、テストがあり、定期的に更新されているものを使用することを強くお勧めします: https://pypi.org/project/django-crum/

最も邪魔にならない方法は、 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

認証ミドルウェアの後に、このミドルウェアを MIDDLEWARE_CLASSES に追加するだけです。

設定.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関数を使用して現在のユーザーを取得できます。

于 2014-02-14T18:23:26.683 に答える
36

管理画面と他の場所の両方で機能するものが必要な場合は、カスタム モデルフォームを使用する必要があります。基本的な考え方は、__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
于 2009-05-14T11:43:20.707 に答える
27

リクエストオブジェクトを渡す必要があるため、ダニエルの答えは管理者には直接機能しません。get_formクラスでメソッドをオーバーライドすることでこれを行うことができるかもしれませんがModelAdmin、フォームのカスタマイズを避けsave_modelModelAdmin.

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()
于 2011-07-07T17:36:31.390 に答える
15

この全体的なアプローチは、私を悩ませました。一度だけ言いたかったので、ミドルウェアに実装しました。認証ミドルウェアの後に 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
于 2012-10-19T15:37:32.520 に答える
9

一般的なビューでそれを行う方法は次のとおりです。

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)
于 2013-03-21T05:25:55.607 に答える
7

クラスベースのビューを使用している場合、ダニエルの答えにはさらに多くのことが必要です。以下を追加して、リクエスト オブジェクトが 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 を返す必要があります。

于 2012-03-02T01:49:03.027 に答える
1

forms.ModelForm の「save」メソッドは、保存されたインスタンスを返します。

MyModelForm に最後の 1 行を追加する必要があります:
...
return obj

この変更は、create_object または update_object ジェネリック ビューを使用している場合に必要です。
保存されたオブジェクトを使用してリダイレクトを行います。

于 2010-08-04T17:56:28.750 に答える
1

オブジェクトを常に保存することでモデルフォームのデフォルトの動作を変更するため、ダニエルの答えが最善であるとは思いません。

私が使用するコード:

フォーム.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
于 2014-12-20T14:46:47.127 に答える