django - 如何使用 django-filter 按当前用户过滤 ModelChoiceFilter

标签 django django-filter

我使用的是 django-filter,它运行良好,但我在过滤当前用户选择的下拉列表(基于模型)时遇到问题。这是一个相当基本和常见的场景,您有一个子表,该子表与父表具有多对一的关系。我想通过选择父项来过滤子记录表。这都是相当简单、标准的东西。美中不足的是父记录由不同的用户创建,而您只想在属于当前用户的下拉列表中显示父记录。

这是我来自 filters.py 的代码

import django_filters
from django import forms
from .models import Project, Task
from django_currentuser.middleware import get_current_user, get_current_authenticated_user

class MasterListFilter(django_filters.FilterSet):
    project = django_filters.ModelChoiceFilter(
        label='Projects',
        name='project_fkey',
        queryset=Project.objects.filter(deleted__isnull=True, user_fkey=3).distinct('code')
        )

    class Meta:
        model = Task
        fields = ['project']

    @property
    def qs(self):
        parent = super(MasterListFilter, self).qs
        user = get_current_user()        
        return parent.filter(master=True, deleted__isnull=True, user_fkey=user.id) 

这个位工作正常:

@property
    def qs(self):
        parent = super(MasterListFilter, self).qs
        user = get_current_user()        
        return parent.filter(master=True, deleted__isnull=True, user_fkey=user.id) 

这会过滤我的主列表,以便只显示设置了主标志、尚未删除且属于当前用户的记录。这正是我想要的。

以下位也有效,并为我提供了我正在寻找的过滤下拉列表,因为我已将 3 硬编码为 user.id

queryset=Project.objects.filter(deleted__isnull=True, user_fkey=3).distinct('code'),

显然我不想有一个硬编码的 id。我需要获取当前用户的值(value)。按照用于过滤主表的相同逻辑,我最终得到了这个。

class MasterListFilter(django_filters.FilterSet):
    **user = get_current_user()**
    project = django_filters.ModelChoiceFilter(
        label='Projects',
        name='project_fkey',
        queryset=Project.objects.filter(deleted__isnull=True, user_fkey=**user.id**).distinct('code')
        )

然而,这是不可靠的,因为有时它会显示正确的列表,有时则不会。例如,如果我登录并且它没有显示列表(即它只显示'--------')然后我重新启动我的 apache2 服务,它又开始工作,然后在某个时候它再次退出.显然,这不是一个长期的解决方案。

那么我如何可靠地让当前用户进入我的 filter.py 以便我可以使用它来过滤我的下拉过滤器列表。

提前致谢,祝您编码愉快。

编辑: 因此,按照 Wiesion 的建议,我按照建议更改了代码,但仍然收到“无类型错误”,指出用户没有属性 ID。基本上,我似乎没有得到当前用户。所以回到文档并尝试将他们的建议与 Wiesion 合并(其解释完全有意义 - 谢谢 Wiesion)我想出了以下内容:

def Projects(request):
    if request is None:
        return Project.objects.none()
    return lambda req: Project.objects.filter(deleted__isnull=True, user_fkey=req.user.id)

class MasterListFilter(django_filters.FilterSet):
    project = django_filters.ModelChoiceFilter(
        label='Projects',
        name='project_fkey',
        queryset=Projects
        )

    class Meta:
        model = Task
        fields = ['project']

这种在理论上可行,但在下拉列表中什么也没有给我,因为

 if request is None:

返回 True,因此给我一个空列表。

所以...任何人都可以看到我哪里出错了,这阻止了我访问请求吗?显然,代码的第二部分是基于从我的 View 传递的 qs 工作的,所以也许我还需要传递其他内容?我的 view.py 代码如下:

def masterlist(request, page='0'):
    #Check to see if we have clicked a button inside the form
    if request.method == 'POST':
        return redirect ('tasks:tasklist')
    else:
        # Pre-filtering of user and Master = True etc is done in the MasterListFilter in filters.py
        # Then we compile the list for Filtering by. 
        f = MasterListFilter(request.GET, queryset=Task.objects.all())
        # Then we apply the complete list to the table, configure it and then render it.
        mastertable = MasterTable(f.qs)
        if int(page) > 0:
            RequestConfig(request, paginate={'page': page, 'per_page': 10}).configure(mastertable) 
        else:
            RequestConfig(request, paginate={'page': 1, 'per_page': 10}).configure(mastertable)  
        return render (request,'tasks/masterlist.html',{'mastertable': mastertable, 'filter': f}) 

谢谢。

最佳答案

来自docs

The queryset argument also supports callable behavior. If a callable is passed, it will be invoked with Filterset.request as its only argument. This allows you to easily filter by properties on the request object without having to override the FilterSet.__init__.

这根本没有经过测试,但我认为这就是您所需要的:

class MasterListFilter(django_filters.FilterSet):
    project = django_filters.ModelChoiceFilter(
        label='Projects',
        name='project_fkey',
        queryset=lambda req: Project.objects.filter(
            deleted__isnull=True, user_fkey=req.user.id).distinct('code'),
    )

    class Meta:
        model = Task
        fields = ['project']

此外,如果它取决于网络服务器重启 - 您是否检查了缓存问题? (以防万一,django-debug-toolbar 提供了很好的见解)

编辑

不可预知的行为很可能发生,因为您正在 class MasterListFilter 定义中检索 user,因此 get_current_user() 在类中执行加载时间,而不是在实际请求期间,所有对 qs 的后续调用都将检索该查询。一般来说,所有与请求相关的东西都不应该在类定义中,而应该在方法/lambda 中。因此,接收 request 参数并仅在那时创建查询的 lambda 应该完全满足您的需求。

编辑 2

关于您的编辑,以下代码存在一些问题:

def Projects(request):
    if request is None:
        return Project.objects.none()
    return lambda req: Project.objects.filter(deleted__isnull=True, user_fkey=req.user.id)

这要么返回一个空的对象管理器,要么返回一个可调用的——但是方法 Project 本身已经是一个可调用的,所以当request 对象是 None,否则是 lambda,但它期望接收对象管理器 - 它不能迭代 lambda 所以它应该给你一些 不可迭代 错误。所以基本上你可以尝试:

def project_qs(request):
    # you could add some logging here to see what the arguments look like
    if not request or not 'user' in request:
        return Project.objects.none()
    return Project.objects.filter(deleted__isnull=True, user_fkey=request.user.id)

# ...
queryset=project_qs
# ...

关于django - 如何使用 django-filter 按当前用户过滤 ModelChoiceFilter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51137005/

相关文章:

python - Django:关键字参数 'initial' 的多个值

python - Django,如何使用过滤器检查参数中是否包含字符串字段

python - 如何在 Django 中编写自己的权限

javascript - 如何将自定义 DateRangePicker 小部件与 Django Filter 的 DateFromToRangeFilter 一起使用

python - Django 休息框架 : How do I order/sort a search/filter query?

Python selenium 测试卡在 urlopen 中

jquery - 在 Django 中 AJAX 发布后重定向

python - Django过滤模块: lookup expression (year) does not return the desired queryset

django - 你如何使用带有参数列表的 django-filter 包?

python - django-filter:使用 DateFromToRangeFilter 的输入格式