【问题标题】:How to Filter ModelChoiceFilter by current user using django-filter如何使用 django-filter 按当前用户过滤 ModelChoiceFilter
【发布时间】:2018-12-10 17:32:13
【问题描述】:

我正在使用 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。我需要获取当前用户的价值。遵循用于过滤主表的相同逻辑,我最终得到了这个。

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,因此给了我一个空列表。

那么...任何人都可以看到我哪里出错了,这会阻止我访问请求吗?显然,代码的第二部分是基于从我的视图传递的 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}) 

谢谢。

【问题讨论】:

  • 这个问题解决了吗?

标签: django django-filter


【解决方案1】:

来自docs

queryset 参数也支持可调用行为。如果一个可调用的 已通过,它将以 Filterset.request 作为其唯一调用 争论。这使您可以轻松地按属性过滤 请求对象而不必覆盖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 时,您的 ModelChoiceFilter 将只接收一个对象管理器,否则一个 lambda,但它期望接收一个对象管理器 - 它不能迭代一个 lambda,所以它应该给你一些 is not iterable 错误。所以基本上你可以试试:

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
# ...

【讨论】:

  • 感谢 Wiesion,但我仍然收到无类型错误,提示用户没有属性 ID。我假设是因为它没有吸引用户。请看我上面的编辑,显示我做了什么以及我在哪里。感谢您的帮助。
  • 更新了我的答案
  • 谢谢。我已经添加了修改。必须承认我对 lambda 的工作知识充其量是薄弱的。不过我不认为这是问题所在。问题似乎是我正在测试请求,但没有可用的请求。它每次都返回 None ,如果我添加“打印请求”,我会在我的 apache2 日志中看到这一点。因此,为什么我之前没有得到不可迭代的错误,因为我从来没有接触过代码的那部分。我只是不知道如何让我的 request.GET 从视图中访问我的 filter.py 文件。
【解决方案2】:

如以下线程中所述,您必须将请求传递给视图中的过滤器实例:Customize queryset in django-filter ModelChoiceFilter (select) and ModelMultipleChoiceFilter (multi-select) menus based on request

例如:

myFilter = ReportFilter(request.GET, request=request, queryset=reports)

【讨论】:

  • 当然,如果它能让你开心
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-23
  • 1970-01-01
  • 2018-12-23
  • 2021-11-02
  • 2011-04-22
  • 1970-01-01
相关资源
最近更新 更多