ModelAdmin потокобезопасность / вопросы кэширования - PullRequest
3 голосов
/ 02 августа 2010

В конечном счете, моя цель состоит в том, чтобы расширить ModelAdmin в Django для предоставления разрешений на уровне полей, то есть, учитывая свойства объекта запроса и значения полей редактируемого объекта, я хотел бы контролировать, будут ли поля /строки видны пользователю.В конечном итоге я достиг этого, добавив метод can_view_field() в ModelAdmin и изменив встроенные методы get_form() и get_fieldset(), чтобы удалить / исключить поля + inline, которые пользователь не имеет разрешений (как определено can_view_field())видеть.Если вы хотите увидеть код, я поместил его в папку , так как он длинный и только в некоторой степени актуален.

Он отлично работает ... почти.Кажется, я столкнулся с какой-то проблемой безопасности потоков или кэширования, когда состояние объекта ModelAdmin воспроизводится из одного запроса в другой воспроизводимым образом.

Я проиллюстрирую проблему с помощьюпростой примерПредположим, у меня есть модель, модель которой я добавил в код доступа на уровне поля.Эта модель имеет два поля: - public_field, которое может просматривать / редактировать любой сотрудник; - secret_field, которое может просматривать / редактировать только суперпользователи

В этом случае метод can_view_field()будет выглядеть следующим образом:

def can_view_field(self, request, obj, field_name):
    """
    Returns boolean indicating whether the user has necessary permissions to
    view the passed field.
    """
    if obj is None:
        return request.user.has_perm('%s.%s_%s' % (
            self.opts.app_label,
            action,
            obj.__class__.__name__.lower()
        ))
    else:
        if field_name == "public_field":
            return True
        if field_name == "secret_field" and request.is_superuser:
            return True
        return False

Контрольный пример 1: при новом перезапуске сервера, если вы сначала просматриваете форму списка изменений как суперпользователь, вы видите форму, как и в случае public_field и secret_field видимый.Если вы выходите из системы и просматриваете ее как сотрудник (но не как суперпользователь), вы увидите только public_field.

Тестовый пример 2: с новым перезапуском сервера, если вы вошли в систему сначала как сотрудник,вы все еще видите только public_field.Однако, если вы выйдете из системы и увидите, что вы суперпользователь, вы не см. secret_field.Воспроизводится на 100%.

Я провел некоторые базовые диагностики безопасности потоков:

  1. В конце get_form() я распечатал адрес памятиОбъект ModelForm.Как и должно быть, он уникален для каждого запроса.Поэтому объект ModelForm не является проблемой.
  2. Непосредственно перед регистрацией администратора я попытался распечатать адрес памяти объекта ModelAdmin.В тестовом примере 1 он уникален для обоих запросов.Однако в тестовом примере 2 он вообще не печатается при втором запросе.

На данный момент я не в курсе.Следующим моим исследованием будет система регистрации администраторов (о которой я, по общему признанию, ничего не знаю).Состояние сбрасывается при перезапуске сервера, поэтому кажется, что ModelAdmin должен быть кэширован?Или это проблема безопасности потоков?Если я превращу его в фабрику и верну deepcopy() из ModelAdmin, будет ли он обслуживать новый ModelAdmin с каждым запросом?Я невежественен и буду признателен за любые мысли.Спасибо!

1 Ответ

1 голос
/ 02 августа 2010

Я не понимаю, почему вы думаете, что ModelAdmin должен быть новым экземпляром для каждого запроса. Объекты admin создаются с помощью вызовов admin.site.register(Model) в каждом admin.py, который, в свою очередь, вызывается из admin.autodiscover() в urls.py. Другими словами, это происходит при запуске процесса. Принимая во внимание динамический многопроцессный характер большинства сред веб-обслуживания, вы можете или не можете получить новый процесс с каким-либо конкретным запросом - конечно, вы не получите его каждый раз.

Из-за этого не стоит хранить или изменять состояние в глобальном объекте, таком как ModelAdmin. Я не просмотрел ваш связанный код должным образом, но был по крайней мере один случай, когда вы изменяли атрибут в self в результате вызова метода. Не делайте этого - вам нужно найти какой-то другой способ передачи динамических значений между методами.

...