web-dev-qa-db-ja.com

Django:モデルを保存するときにユーザーIDを入力します

標準のDjango=ユーザーモデルにリンクされているcreated_byフィールドを持つモデルがあります。モデルを保存するときに、現在のユーザーのIDを自動的に入力する必要があります。サイトのほとんどの部分ではビルトイン管理を使用しないため、管理レイヤーでこれを実行します。

45
seanf

管理者と他の場所の両方で機能するものが必要な場合は、カスタムモデルフォームを使用する必要があります。基本的な考え方は、__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
34
Daniel Roseman

最も目立たない方法は、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.CurrentUserMiddlewareおよびcms.utils.permissions.get_current_user関数を使用して現在のユーザーを取得できます。

26
bikeshedder

ダニエルの答えは、リクエストオブジェクトを渡す必要があるため、管理者には直接働きません。これを行うには、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()
23
Tim Fletcher

この全体的なアプローチは、私を悩ませました。一度だけ言いたいので、ミドルウェアで実装しました。認証ミドルウェアの後に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
13
mindlace

汎用ビューを使用して行う方法は次のとおりです。

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)
8
yretuta

クラスベースのビューを使用している場合、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を返す必要があります

7
Monster

bikeshedder の答えに基づいて、彼が実際に私のために働かなかったので、私は解決策を見つけました。

  1. app/middleware/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を使用しています

3
leila

次のようなものを使用することの問題は何ですか:

_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
_

viewsmyform.save(request.user)のように呼び出します。

ここに ModelFormの保存関数 があり、commitパラメーターのみがあります。

2
suhailvs

ダニエルの答えが最高だとは思わない.

私が使用するコード:

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
1
jgadelange

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

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

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

1
Florentin

Django documentation Models and request.user :から

"CreateViewを使用してオブジェクトを作成したユーザーを追跡するには、カスタムModelFormを使用できます。ビューでは、編集するフィールドのリストに[ユーザーフィールド]を含めないでください。ユーザーを追加します。

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)
1
Martin CR

将来の参考のために、この主題について見つけた最良の解決策:

https://pypi.python.org/pypi/Django-crum/0.6.1

このライブラリは、いくつかのミドルウェアで構成されています。このライブラリを設定した後、単純にモデルのsaveメソッドをオーバーライドし、次を実行します。

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フィールドが取得されます。

0
durdenk