Подтвердить что ты не робот

Django: заполнение идентификатора пользователя при сохранении модели

У меня есть модель с полем created_by, которое связано со стандартной моделью Django User. Мне нужно автоматически заполнить его идентификатором текущего пользователя, когда модель будет сохранена. Я не могу сделать это на уровне администратора, так как большинство частей сайта не будут использовать встроенный администратор. Может ли кто-нибудь посоветовать, как я должен это делать?

4b9b3361

Ответ 1

Если вы хотите что-то, что будет работать как в админке, так и в другом месте, вы должны использовать пользовательскую модель. Основная идея состоит в том, чтобы переопределить метод __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

Ответ 2

Наименее затруднительным способом является использование 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

Теперь вам нужно только добавить это промежуточное программное обеспечение к среднему средству MIDDLEWWARE_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 для извлечения текущего пользователя.

Ответ 3

Ответ 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()

Ответ 4

Весь этот подход выгнал меня из себя. Я хотел сказать это ровно один раз, поэтому я реализовал его в промежуточном программном обеспечении. Просто добавьте 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

Ответ 5

вот как я делаю это с общими представлениями:

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

Если вы используете классы, основанные на взглядах, ответам 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

Также, как уже упоминалось, вам нужно вернуть obj в конце ModelForm.save()

Ответ 7

В ответ на bikeshedder я нашел решение, так как его на самом деле не работало для меня.

  • Приложение/промежуточного/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
    
  • 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',
    ]
    
  • 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

Ответ 9

Метод save из форм .ModelForm возвращает сохраненный экземпляр.

Вы должны добавить одну последнюю строку в MyModelForm:
...
return obj

Это изменение необходимо, если вы используете общие представления create_object или update_object.
Они используют сохраненный объект для перенаправления.

Ответ 10

Я не верю, что ответ Дэниела лучше всего, поскольку он меняет поведение модели по умолчанию, сохраняя объект всегда.

Код, который я использовал бы:

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

Ответ 11

В чем проблема с использованием чего-то вроде:

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, которая имеет только параметр commit.

Ответ 12

Для будущих ссылок лучшее решение, которое я нашел по этому вопросу:

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.

Ответ 13

@bikeshedder, ваш ответ не сработал у меня. Я получаю

"NameError: имя" get_current_user "не определено"

и

Модуль импорта ошибок: 'Нет модуля с именем' current_user ''????

PS. Я использую python 3.5 и django 1.11

Ответ 14

Из документации Django Models и 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)

Ответ 15

Обратите внимание, что если вы искали это, но добавляете следующие

user = models.ForeignKey('auth.User')

к модели будет работать, чтобы добавить идентификатор пользователя к модели.

Ниже каждая иерархия принадлежит пользователю.

class Hierarchy(models.Model):
    user = models.ForeignKey('auth.User')
    name = models.CharField(max_length=200)
    desc = models.CharField(max_length=1500)