我使用的django-filter效果很好,但是在过滤当前用户的下拉列表(基于模型)时出现问题。这是一个相当基本且常见的方案,其中您有一个子表,该子表与父表具有多对一关系。我想通过选择父项来过滤子项记录表。这都是相当简单的标准东西。美中不足的是,父记录是由不同的用户创建的,而您只想在属于当前用户的下拉列表中显示父记录。
这是我来自filter.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})
谢谢。
答案 0 :(得分:1)
如以下线程所述,您必须将请求传递到视图中的过滤器实例:Customize queryset in django-filter ModelChoiceFilter (select) and ModelMultipleChoiceFilter (multi-select) menus based on request
例如:
myFilter = ReportFilter(request.GET, request=request, queryset=reports)
答案 1 :(得分:0)
从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对此提供了深刻的见解)
编辑
不可预测的行为很可能发生,因为您正在获取user
定义中的class MasterListFilter
,因此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
本身已经是可调用的,因此当ModelChoiceFilter
对象时,您的request
将仅接收对象管理器是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
# ...