【问题标题】:Better way of hiding entries not created by current user in Django?在 Django 中隐藏当前用户未创建的条目的更好方法?
【发布时间】:2023-03-24 10:21:01
【问题描述】:

我对 Django 很陌生,现在我创建了一个网站,允许用户注册/登录,通过条目数据库中的“用户”字段创建与该用户关联的条目。

通过添加仅返回当前用户条目的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)

【问题讨论】:

  • 您可以轻松地将所有四个视图合并为一个viewsets.GenericViewSet。这将使您可以在一个视图中执行所有操作,只需创建一个 url,您还可以通过 perform_create 方法向用户提供,这样您就不需要验证表单。
  • 除了我的代码更长更混乱之外,处理这样的条目是否存在安全问题?
  • 我认为处理条目是有效的。
  • 太棒了。我想像你说的那样把我的观点合并成一个,但我真的不明白这是怎么回事。我还能在 urls.py 中保留所有 url 路径,但它们都指向这个新的 GenericViewSet 吗?
  • 我已经发布了一个解释如何创建视图集的答案。

标签: python django database class


【解决方案1】:

Create、Detail 和 Update 视图指的是单个条目(并且与该条目的关系由 url kwarg (可以是通用的 'pk' 或 'slug' 等),所以这 3 个视图中的 get_queryset 不是确实有帮助。

如果您在浏览器中打开一个条目,您将看到该 url 类似于 /entry/ 所以如果您更改该值,您将访问不同的对象。如果你想阻止用户访问不属于他们的条目,你可以像这样创建一些 mixin:

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)

这里发生的情况是每次用户访问某个视图时,它会检查他是否被分配给该对象(请注意,此 mixin 仅适用于单个对象视图(详细信息、更新、删除),因为列表需要修改。

【讨论】:

  • 现在访问不是由当前用户创建的 /entry/ 会引发 Not Found 错误,这正是我想要的。像你写的那样在 EntryMixin 类中处理它有什么好处?
  • 查询集可能会在以后被编辑,这样做的人可能会删除或破坏用户过滤器,而不是意识到它的重要性。而删除专门引发 PermissionError 的 Mixin 显然是愚蠢的 :-)
  • 哦,我明白了,谢谢!
【解决方案2】:

Entry 模型创建一个序列化程序并提供您要显示的字段。将用户字段设为只读,以便。

serializers.py

from rest_framework import serializers

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

然后为可以轻松执行 crud 操作的条目创建一个 Viewset。覆盖get_queryset 方法以获取当前用户记录。同时覆盖perform_create 方法以在创建新记录时将当前用户提供为entry'e 用户。

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)

为视图集创建一个路由器并将视图集注册到路由器。然后在 urlpatterns 中包含路由器的 url。

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>/ 获取单个条目的详细信息。

【讨论】:

  • 非常感谢!我已经能够实现这一点,我在/entries/ 上获得了用户条目,在/entries/<entry_id> 等上获得了特定条目。这在Django Rest Framework 可浏览API 中显示。但是我一辈子都无法弄清楚如何将这些数据实现到我当前的 html 模板中。例如,当在我的 html 模板中调用 {{ entry.content }} 时,它实际上在哪里寻找该数据?抱歉,这有点过头了,但我正在开始拼凑。
  • 好的,所以你想在你的模板中展示它,我以为你想为它创建一个 API。很抱歉我误读了这个问题。
  • 好吧,我已经恢复了更改哈哈,我现在不需要 API。但是你还是建议结合我的观点吗?
  • 否,如果要为每个视图呈现不同的模板,请不要合并视图。
  • 好吧,还是谢谢你,学习一切。 ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-21
  • 2020-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多