代码之家  ›  专栏  ›  技术社区  ›  cander

如何使用django筛选器按当前用户筛选ModelChoiceFilter

  •  1
  • cander  · 技术社区  · 6 年前

    我正在使用django过滤器,它工作得很好,但我在筛选当前用户的下拉选择列表(基于模型)时遇到问题。这是一个相当基本和常见的场景,其中有一个子表,它与父表有多对一的关系。我想通过选择父记录来筛选子记录表。这都是相当容易的,标准的东西。当父记录是由不同的用户创建的,并且您只想在下拉列表中显示属于当前用户的父记录时,就可以使用fly-in软膏了。

    这是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的建议,我按照建议修改了代码,但是仍然会出现一个None-Type错误,说用户没有属性ID,基本上看起来我没有得到当前用户。所以回到文档中,尝试将他们的建议与Wiesion(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}) 
    

    谢谢。

    1 回复  |  直到 6 年前
        1
  •  0
  •   wiesion    6 年前

    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']
    

    另外,如果这取决于web服务器重新启动-是否检查了缓存问题?(在这种情况下,django调试工具栏对此提供了很好的见解)

    编辑

    不可预知的行为很可能是因为您正在检索 user class MasterListFilter 定义,所以 get_current_user() 在类加载时执行,而不是在实际请求和所有后续调用 qs 将检索该查询。通常,与请求相关的所有内容都不应该在类定义中,而应该在方法/lambda中。所以一只羔羊 request 参数并仅创建查询时才应完全覆盖所需内容。

    编辑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 它本身已经是一个可调用的,所以 ModelChoiceFilter 请求 对象是 None ,否则是一个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
    # ...