У меня есть модель с полем created_by, которое связано со стандартной моделью Django User. Мне нужно автоматически заполнить его идентификатором текущего пользователя, когда модель будет сохранена. Я не могу сделать это на уровне администратора, так как большинство частей сайта не будут использовать встроенный администратор. Может ли кто-нибудь посоветовать, как я должен это делать?
Django: заполнение идентификатора пользователя при сохранении модели
Ответ 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
Ответ 8
Для этого в админ-сайте см. Автоматическое заполнение поля created_by с помощью сайта администратора Django
Ответ 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)