Пользовательские менеджеры Django - как вернуть только объекты, созданные вошедшим в систему пользователем?
Я хочу перезаписать менеджер модели пользовательских объектов, чтобы он возвращал только объекты, созданные конкретным пользователем. Пользователи с правами администратора по-прежнему должны возвращать все объекты, используя менеджер моделей объектов.
Теперь я нашел подход, который мог бы работать. Они предлагают создать ваше собственное промежуточное программное обеспечение, похожее на это:
#### myproject/middleware/threadlocals.py
try:
from threading import local
except ImportError:
# Python 2.3 compatibility
from django.utils._threading_local import local
_thread_locals = local()
def get_current_user():
return getattr(_thread_locals, 'user', None)
class ThreadLocals(object):
"""Middleware that gets various objects from the
request object and saves them in thread local storage."""
def process_request(self, request):
_thread_locals.user = getattr(request, 'user', None)
#### end
А в Custom Manager вы можете позвонить get_current_user()
метод возврата только объектов, созданных конкретным пользователем.
class UserContactManager(models.Manager):
def get_query_set(self):
return super(UserContactManager, self).get_query_set().filter(creator=get_current_user())
Это хороший подход к этому варианту использования? Будет ли это работать? Или это как "использовать кувалду, чтобы сломать орех"?;-)
Просто с помощью:
Contact.objects.filter(created_by= user)
в каждом представлении не выглядит очень опрятным для меня.
РЕДАКТИРОВАТЬ Не используйте этот подход промежуточного программного обеспечения!!!
используйте подход, изложенный Джеком М. нижеПосле некоторого времени тестирования этот подход вел себя довольно странно, и с этим подходом вы смешали глобальное состояние с текущим запросом.
Используйте подход, представленный ниже. Это действительно легко и не нужно взламывать промежуточное ПО.
создайте пользовательский менеджер в вашей модели с функцией, которая ожидает текущего пользователя или любого другого пользователя в качестве входных данных.
#in your models.py
class HourRecordManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(created_by=user)
class HourRecord(models.Model):
#Managers
objects = HourRecordManager()
#in vour view you can call the manager like this and get returned only the objects from the currently logged-in user.
hr_set = HourRecord.objects.for_user(request.user)
Смотрите также эту дискуссию о подходе middelware.
4 ответа
Один из способов справиться с этим - создать новый метод вместо переопределения get_query_set
, Что-то вроде:
class UserContactManager(models.Manager):
def for_user(self, user):
return super(UserContactManager, self).get_query_set().filter(creator=user)
class UserContact(models.Model):
[...]
objects = UserContactManager()
Это позволяет вашему взгляду выглядеть так:
contacts = Contact.objects.for_user(request.user)
Это должно помочь сделать ваш взгляд простым, и поскольку вы будете использовать встроенные функции Django, в будущем он вряд ли сломается.
Кажется необходимым использовать промежуточное программное обеспечение для хранения пользовательской информации.
Тем не менее, я бы не стал изменять ModelManager по умолчанию objects
, но подключите его к другому менеджеру, который я буду использовать в коде, скажем, в вашем случае user_objects
вместо предметов.
Так как вы будете использовать это только в представлениях, которые @login_required
Вам не нужна вся сложная обработка ошибок в Middleware.
Просто мои 2¢.
Спасибо, что поделились кодом. Не очень хорошее решение с точки зрения тестируемости, но я не нашел другого способа настроить менеджеров моделей по данным объекта запроса. Было бы лучше иметь контроль над созданием менеджера, но Django не позволяет этого.
Или даже проще и используйте внешний ключ для получения набора запросов.
Если у вас есть такая модель
class HourRecord(models.Model):
created_by = ForeignKey(get_user_model(), related_name='hour_records')
Вы можете запросить HourRecords
в представлении пользователя просто:
request.user.hour_records.all()