Лучший способ скрыть записи, не созданные текущим пользователем в Django?
Я довольно новичок в Django, и сейчас я создал сайт, который позволяет пользователям регистрироваться/входить в систему, делать записи, которые ассоциируются с этим пользователем через поле "user" в базе данных записей.
Мне удалось получить всю функциональность скрытия записей других пользователей, добавив методы get_queryset()
, которые возвращают только записи текущего пользователя.
Но я чувствую, что это плохой способ сделать это. Мне кажется, что я повторно использую много кода, и что могут быть "черные ходы" для отображения записей других пользователей.
Есть ли способ полностью отключить записи других пользователей от текущего пользователя, а не просто не отображать их?
views.py
class EntryListView(LockedView, ListView):
model = Entry
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user).order_by("-date_created")
class EntryDetailView(LockedView, DetailView):
model = Entry
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
class EntryCreateView(LockedView, SuccessMessageMixin, CreateView):
model = Entry
fields = ["title", "content"]
success_url = reverse_lazy("entry-list")
success_message = "Your new entry was created!"
def form_valid(self, form):
user = self.request.user
form.instance.user = user
return super(EntryCreateView, self).form_valid(form)
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
class EntryUpdateView(LockedView, SuccessMessageMixin, UpdateView):
model = Entry
fields = ["title", "content"]
success_message = "Your entry was updated!"
def get_success_url(self):
return reverse_lazy(
"entry-detail",
kwargs={"pk": self.object.pk}
)
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
Виды Create, Detail и Update ссылаются на одну единственную запись (и связь с этой записью осуществляется по url kwarg (он может быть общим 'pk' или 'slug' и т.д.), поэтому get_queryset внутри этих 3 видов на самом деле не помогает.
Если вы откроете запись в браузере, вы увидите, что url будет выглядеть примерно так: /entry/<id_of_entry>, поэтому если вы измените это значение, вы получите доступ к другому объекту. Если вы хотите предотвратить доступ пользователей к записям, которые им не принадлежат, вы можете создать некоторый миксин, например, такой:
class EntryMixin:
"""
Prevent users to access entries that has different author
"""
def dispatch(self, request, *args, **kwargs):
if not self.get_object().user == request.user:
raise PermissionError
return super().dispatch(request, *args, **kwargs)
Здесь происходит следующее: каждый раз, когда пользователь получает доступ к определенному представлению, он будет проверять, назначен ли он на этот объект (обратите внимание, что этот миксин будет работать только с представлениями одного объекта (detail, update, delete), для списка потребуется модификация.
). Создайте сериализатор для модели Entry
и предоставьте поле, которое вы хотите отобразить. Сделайте пользовательское поле доступным только для чтения, чтобы.
serializers.py
from rest_framework import serializers
class EntriesSerializer(serializers.ModelSerializer):
class Meta:
model = Entry
fields = '__all__'
extra_kwargs = {
'user': {'read_only': True}
}
Затем создайте Viewset для записей, которые могут легко выполнять грубые операции. переопределите метод get_queryset
для получения записей текущего пользователя. Также переопределите метод perform_create
для предоставления текущего пользователя в качестве пользователя записи при создании новой записи.
views.py
from rest_framework import viewsets, mixins
from .serializers import EntriesSerializer
class EntriesViewSet(
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet
):
serializer_class = EntriesSerializer
def get_queryset(self):
queryset = Entry.objects.filter(user=self.request.user)
return queryset
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Создайте маршрутизатор для viewset и зарегистрируйте viewset в маршрутизаторе. Затем включите урлы маршрутизатора в urlpatterns.
urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register('entries', EntriesViewSet, basename='entries')
urlpatterns = [
path('', include(router.urls)),
]
Теперь вы можете получить доступ к списку всех записей, созданных текущим пользователем, через /entries/
, а для получения подробной информации об отдельной записи используйте /entries/<entry_id>/
.