Django: как получить текущего пользователя в админ-формах

В Django ModelAdmin мне нужно отображать формы, настроенные в соответствии с разрешениями пользователя. Есть ли способ получить текущий объект пользователя в класс формы, чтобы я мог настроить форму в методе __init__?
Я думаю, что сохранение текущего запроса в локальном потоке было бы возможным, но это было бы моим последним приложением, я думаю, что это плохой дизайн.

Ответ 1

Вот что я недавно сделал для блога:

class BlogPostAdmin(admin.ModelAdmin):
    form = BlogPostForm

    def get_form(self, request, **kwargs):
         form = super(BlogPostAdmin, self).get_form(request, **kwargs)
         form.current_user = request.user
         return form

Теперь я могу получить доступ к текущему пользователю в forms.ModelForm, обратившись к self.current_user

РЕДАКТИРОВАТЬ: Это старый ответ, и в последнее время я понял, что в метод get_form должны быть внесены следующие изменения:

    def get_form(self, request, *args, **kwargs):
         form = super(BlogPostAdmin, self).get_form(request, *args, **kwargs)
         form.current_user = request.user
         return form

(Обратите внимание на добавление *args)

Ответ 2

Ответ Joshmaker для меня не работает на Django 1.7. Вот что я должен был сделать для Django 1.7:

class BlogPostAdmin(admin.ModelAdmin):
    form = BlogPostForm

    def get_form(self, request, obj=None, **kwargs):
        form = super(BlogPostAdmin, self).get_form(request, obj, **kwargs)
        form.current_user = request.user
        return form

Подробнее об этом методе см. эту соответствующую документацию Django

Ответ 3

Я думаю, что нашел решение, которое работает для меня: для создания ModelForm Django использует метод admin formfield_for_db_field в качестве обратного вызова.
Поэтому я перезаписал этот метод в своем админе и передал текущий пользовательский объект как атрибут с каждым полем (что, вероятно, не является самым эффективным, но кажется мне более чистым, чем использование threadlocals:

    def formfield_for_dbfield(self, db_field, **kwargs):
        field = super(MyAdmin, self).formfield_for_dbfield(db_field, **kwargs)
        field.user = kwargs.get('request', None).user
        return field

Теперь я могу получить доступ к текущему пользовательскому объекту в формах __init__ с чем-то вроде:

    current_user=self.fields['fieldname'].user

Ответ 4

наткнулся на одно и то же, и это был первый результат google на моей странице. Он помог, немного поработал и работает!

Вот как это работает для меня (django 1.7+):

class SomeAdmin(admin.ModelAdmin):
    # This is important to have because this provides the
    # "request" object to "clean" method
    def get_form(self, request, obj=None, **kwargs):
        form = super(SomeAdmin, self).get_form(request, obj=obj, **kwargs)
        form.request = request
        return form

class SomeAdminForm(forms.ModelForm):
    class Meta(object):
        model = SomeModel
        fields = ["A", "B"]

    def clean(self):
        cleaned_data = super(SomeAdminForm, self).clean()
        logged_in_email = self.request.user.email #voila
        if logged_in_email in ['[email protected]']:
            raise ValidationError("Please behave, you are not authorised.....Thank you!!")
        return cleaned_data

Ответ 5

Другой способ решить эту проблему - использовать Django currying, который немного чище, чем просто привязать объект запроса к модели формы.

from django.utils.functional import curry

class BlogPostAdmin(admin.ModelAdmin):
    form = BlogPostForm

    def get_form(self, request, **kwargs):
        form = super(BlogPostAdmin, self).get_form(request, **kwargs)
        return curry(form, current_user=request.user)

Это добавило преимущества, приведя ваш метод init к вашей форме немного более ясно, поскольку другие поймут, что он передается как объект kwarg, а не только случайно привязанный атрибут к объекту класса до его инициализации.

class BlogPostForm(forms.ModelForm):

   def __init__(self, *args, **kwargs):
       self.current_user = kwargs.pop('current_user')
       super(BlogPostForm, self).__init__(*args, **kwargs)

Ответ 6

Этот пример использования документирован на ModelAdmin.get_form

[...], если вы хотите предложить дополнительные поля суперпользователям, вы можете поменять их в другой базовой форме:

class MyModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        if request.user.is_superuser:
            kwargs['form'] = MySuperuserForm
        return super().get_form(request, obj, **kwargs)

Если вам просто нужно сохранить поле, вы можете просто переопределить ModelAdmin.save_model

from django.contrib import admin

class ArticleAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.user = request.user
        super().save_model(request, obj, form, change)