Home > OS >  Better way of hiding entries not created by current user in Django?
Better way of hiding entries not created by current user in Django?

Time:10-22

Im pretty new to Django, and right now I have created a site that lets users register/login, make entries that are associated to that user via a "user" field in the entries database.

Iv'e managed to get all the functionality of hiding other users entries by adding get_queryset() methods that only return the current users entries.

But I feel like this is a bad way of doing this. I feel like im reusing a lot of code and that there could be backdoors to display other users entries.

Is there a way to completely disable other users entries from the current user, not just not display it?

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)

CodePudding user response:

Create, Detail and Update views are referring to one single entry (and relation with this entry is made from url kwarg (it can be by generic 'pk' or 'slug' etc), so get_queryset inside those 3 views are not actually helping.

If you open an Entry inside browser, you will se the url is something like /entry/<id_of_entry> so if you change that value you will access to different object. If you want to prevent users from accessing entries that doesn't belong to them, you can create some mixin like this:

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)

What is happening here is every time when user access certain view, it will check if he is assigned to that object (note that this mixin will work only with single object views (detail, update, delete), for list will need to modify.

CodePudding user response:

Create a serializer for Entry model and provide the field you want to display. Make the user field read_only so that.

serializers.py

from rest_framework import serializers

class EntriesSerializer(serializers.ModelSerializer):
    class Meta:
        model = Entry
        fields = '__all__'
        extra_kwargs = {
            'user': {'read_only': True}
        }

Then create a Viewset for entries that can easily perform crud operations. override the get_queryset method to get current user records. Also override perform_create method to provide current user as the entry'e user while creating a new record.

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)

Create a router for viewset and register the viewset to the router. Then include the router's urls in 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)),
]

Now you can access the list of all entries created by the current user through /entries/ and to get details of a single entry use /entries/<entry_id>/.

  • Related