简体   繁体   English

Django-filter:具有基于 request.user 的查询集的 ModelChoiceFilter

[英]Django-filter: ModelChoiceFilter with request.user based queryset

I have a Django class based ListView listing objects.我有一个基于 Django 类的 ListView 列表对象。 These objects can be filtered based on locations.这些对象可以根据位置进行过滤。 Now I want that the location ModelChoiceFilter only lists locations which are relevant to the current user.现在我希望位置 ModelChoiceFilter 仅列出与当前用户相关的位置。 Relevant locations are the locations he owns.相关地点是他拥有的地点。 How can I change the queryset?如何更改查询集?

# models.py
from django.db import models
from django.conf import settings
from rules.contrib.models import RulesModel
from django.utils.translation import gettext_lazy as _


class Location(RulesModel):
    name = models.CharField(_("Name"), max_length=200)
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        verbose_name=_("Owner"),
        related_name="location_owner",
        on_delete=models.CASCADE,
        help_text=_("Owner can view, change or delete this location."),
    )

class Object(RulesModel):
    name = models.CharField(_("Name"), max_length=200)
    description = models.TextField(_("Description"), blank=True)
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        verbose_name=_("Owner"),
        related_name="location_owner",
        on_delete=models.CASCADE,
        help_text=_("Owner can view, change or delete this location."),
    )
    location = models.ForeignKey(
        Location,
        verbose_name=_("Location"),
        related_name="object_location",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )

This is my current filters.py file which shows all the locations to the user.这是我当前的 filters.py 文件,它向用户显示所有位置。

# filters.py
from .models import Object
import django_filters

class ObjectFilter(django_filters.FilterSet):
    class Meta:
        model = Object
        fields = ["location", ]

This is the view which by default shows objects the user owns.这是默认显示用户拥有的对象的视图。 It's possible to filter further by location.可以按位置进一步过滤。 But the location drop-down shows too many entries.但是位置下拉菜单显示的条目太多。

# views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Object
from .filters import ObjectFilter

class ObjectListView(LoginRequiredMixin, ListView):
    model = Object
    paginate_by = 10

    def get_queryset(self):
        queryset = Object.objects.filter(owner=self.request.user)
        filterset = ObjectFilter(self.request.GET, queryset=queryset)
        return filterset.qs

    def get_context_data(self, **kwargs):
        context = super(ObjectListView, self).get_context_data(**kwargs)
        filterset = ObjectFilter(self.request.GET, queryset=self.queryset)
        context["filter"] = filterset
        return context

My last attempt我的最后一次尝试

I've tried to tweak the filters.py with adding a ModelChoiceFilter, but it ends up with an AttributeError: 'NoneType' object has no attribute 'request' .我尝试通过添加 ModelChoiceFilter 来调整 filters.py,但最终出现AttributeError: 'NoneType' object has no attribute 'request'

# filters.py
from .models import Object
import django_filters

def get_location_queryset(self):
    queryset = Location.objects.filter(location__owner=self.request.user)
    return queryset

class ObjectFilter(django_filters.FilterSet):
    location = django_filters.filters.ModelChoiceFilter(queryset=get_location_queryset)

    class Meta:
        model = Object
        fields = ["location", ]

I believe a few different issues are at play here.我相信这里有几个不同的问题在起作用。 First, as per the django-filter docs , when a callable is passed to ModelChoiceFilter , it will be invoked with Filterset.request as its only argument.首先,根据django-filter 文档,当一个可调用对象被传递给ModelChoiceFilter时,它将使用Filterset.request作为其唯一参数来调用。 So your filters.py would need to be rewritten like so:所以你的filters.py需要像这样重写:

# filters.py
from .models import Object
import django_filters

def get_location_queryset(request): # updated from `self` to `request`
    queryset = Location.objects.filter(location__owner=request.user)
    return queryset

class ObjectFilter(django_filters.FilterSet):
    location = django_filters.filters.ModelChoiceFilter(queryset=get_location_queryset)

    class Meta:
        model = Object
        fields = ["location", ]

This is half of the puzzle.这是难题的一半。 I believe the other issue is in your view.我相信另一个问题在你看来。 django-filter has view classes that handle passing requests to filtersets, but this does not happen automatically using Django's generic ListView . django-filter具有处理将请求传递给过滤器集的视图类,但这不会使用 Django 的通用ListView自动发生。 Try updating your view code to something like this:尝试将您的视图代码更新为以下内容:

# views.py
from django_filters.views import FilterView

class ObjectListView(LoginRequiredMixin, FilterView): # FilterView instead of ListView
    model = Object
    filterset_class = ObjectFilter

This should take care of passing the request for you.这应该负责为您传递请求。

Also note that, as per the django-filter docs linked above, your queryset should handle the case when request is None .另请注意,根据上面链接的 django-filter 文档,您的查询集应处理requestNone的情况。 I've personally never seen that happen in my projects, but just FYI.我个人从未在我的项目中看到过这种情况,仅供参考。

As an alternative, if you don't want to use FilterView , I believe the code in your example is almost there:作为替代方案,如果您不想使用FilterView ,我相信您示例中的代码几乎就在那里:

# views.py alternative
class ObjectListView(LoginRequiredMixin, ListView):
    model = Object
    paginate_by = 10

    def get_queryset(self):
        filterset = ObjectFilter(self.request)
        return filterset.qs

I think this would also work with the filters.py I specified above.我认为这也适用于我上面指定的filters.py

The problem with this code:这段代码的问题:

def get_location_queryset(self):
    queryset = Location.objects.filter(location__owner=self.request.user)
    return queryset

is that it is a function based view, you added self as an argument, and tried to access request which does not exist in context of self since self value is undefined to us是它是基于函数的视图,您将self添加为参数,并尝试访问在self上下文中不存在的request ,因为self值对我们来说是未定义的

What i would do to filter out location based on user by creating a class based view for location filtering通过为位置过滤创建基于类的视图,我将如何根据用户过滤位置

class LocationView(ListView):
      def get_queryset(self):
          return Location.objects.filter(owner=self.request.user)

in filters.py:在filters.py中:

class ObjectFilter(django_filters.FilterSet):
    location = django_filters.filters.ModelChoiceFilter(queryset=LocationView.as_view())

    class Meta:
        model = Object
        fields = ["location", ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Django-Filter:使用与当前用户相关的queryset的ModelChoiceFilter - Django-Filter: ModelChoiceFilter using queryset related to the current user Python Django request.user 在 Class 基于视图和查询集中的最佳实践 - Python Django Best Practice for request.user in Class Based Views and Queryset django-filter 获取查询集 - django-filter get queryset 同时通过用户列表和 request.user 过滤查询集 - Filter a queryset by a list of users and request.user at the same time django-filter自定义过滤器无法获取queryset - django-filter custom filter not get queryset 如何将当前用户传递给 Django_filters ModelChoiceFilter 查询集 - How to pass Current user to Django_filters ModelChoiceFilter queryset Django管理员:如何根据对象(而非request.user)的数据过滤ForeignKeyField小部件? - Django admin: how do I filter a ForeignKeyField widget based on the object's (not request.user's) data? Django Rest Api过滤器字段基于request.user的选择选项 - Django Rest Api filter field's select options based on request.user Django:如何使用ModelFormSet通过request.user过滤ForeignKey选择 - Django: How to filter ForeignKey choices by request.user with ModelFormSet 如何通过request.user过滤django-tastypie的ToManyField? - How to filter ToManyField of django-tastypie by request.user?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM