Python Django难以将ModelAdmin.queryset与ModelAdmin.list_过滤器一起使用,以限制显示的过滤器项
在Django 1.5.1中,我为内部使用设置了一个SAAS系统,其中每个部门都有自己的数据集 为此,我使用ModelAdmin.queryset()将所有数据限制为仅属于当前登录用户所在部门的记录 这非常适用于主要的管理功能(汇总表等)。但是我在“ModelAdmin.list\u filter”上放置的任何内容都会显示所有值-显然是使用基本查询集,而不是我在ModelAdmin.queryset中定义的查询集 我可以在这里看到如何定义自定义的ModelAdmin.list\u filter查询:这样还可以定义自定义的过滤器管理器,其中可以包含自定义的查询集 但这是每个过滤器字段的强> 一旦添加了ModelAdmin.queryset,就应该自动完成这项工作,这似乎是一项非常艰巨的工作 有没有一个更简单的方法我在这里错过了,或者也许这是我应该罚单 提前感谢, 富有的 彼得,谢谢你踢我的裤子。原来我对自己问题的理解是错误的强> =========================我的代码中的示例================Python Django难以将ModelAdmin.queryset与ModelAdmin.list_过滤器一起使用,以限制显示的过滤器项,python,django,django-admin,django-queryset,Python,Django,Django Admin,Django Queryset,在Django 1.5.1中,我为内部使用设置了一个SAAS系统,其中每个部门都有自己的数据集 为此,我使用ModelAdmin.queryset()将所有数据限制为仅属于当前登录用户所在部门的记录 这非常适用于主要的管理功能(汇总表等)。但是我在“ModelAdmin.list\u filter”上放置的任何内容都会显示所有值-显然是使用基本查询集,而不是我在ModelAdmin.queryset中定义的查询集 我可以在这里看到如何定义自定义的ModelAdmin.list\u filter查
class Company(models.Model):
"""Company model."""
accountid = models.CharField(_('Account Number'), max_length=20, null=False, blank=True, help_text="Link to old system and records")
name = models.CharField(_('name'), max_length=200, unique=True, help_text=_("Enter full company name, no abbreviations. Duplicates are not allowed."))
nickname = models.CharField(_('nickname'), max_length=50, blank=True, null=True, help_text=_("Enter one or more keywords or abbreviations seperated by a space to make searching easier"))
slug = AutoSlugField(_('slug'), max_length=50, unique=True, blank=False, populate_from=('name', ))
site = models.ForeignKey(Site, default="0", blank=False, null=False, editable=False, help_text="Indicate which site this record belongs to. ")
class CompanyAdmin(admin.ModelAdmin):
list_filter = ('type', 'lead_quality', )
def queryset(self, request):
qs = super(PersonAdmin, self).queryset(request)
if request.user.is_superuser:
self.message_user(request, "Warning: This is the ADMINISTRATOR view!!", 'warning')
return qs
return qs.filter(site__id=request.session['site'].id)
def save_model(self, request, obj, form, change):
if change:
if obj.site.id != request.session['site'].id:
logger.debug("Contacts.Person.save_model: replacing site (%s) with (%s) " % (repr(obj.site), repr(request.session['site'])) )
else:
logger.debug("Contacts.Person.save_model: setting site (%s)" % (repr(request.session['site'])) )
obj.site = request.session['site']
obj.save()
class Person(models.Model):
"""Person model."""
first_name = models.CharField(_('first name'), max_length=100)
last_name = models.CharField(_('last name'), max_length=200)
slug = AutoSlugField(_('slug'), max_length=50, unique=True, blank=False, populate_from=('first_name', 'last_name'))
company = models.ForeignKey(Company, blank=True, null=True, help_text=_("If this person is associated with a Company, indicate which one here.") )
site = models.ForeignKey(Site, default="0", blank=False, null=False, editable=False, help_text="Indicate which site this record belongs to. ")
class PersonAdmin(admin.ModelAdmin):
list_filter = ('company',)
def queryset(self, request):
qs = super(PersonAdmin, self).queryset(request)
if request.user.is_superuser:
self.message_user(request, "Warning: This is the ADMINISTRATOR view!!", 'warning')
return qs
return qs.filter(site__id=request.session['site'].id)
def save_model(self, request, obj, form, change):
if change:
if obj.site.id != request.session['site'].id:
logger.debug("Contacts.Person.save_model: replacing site (%s) with (%s) " % (repr(obj.site), repr(request.session['site'])) )
else:
logger.debug("Contacts.Person.save_model: setting site (%s)" % (repr(request.session['site'])) )
obj.site = request.session['site']
obj.save()
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name in ('company',):
kwargs["queryset"] = Company.objects.get_query_set().filter(site__id=request.session['site'].id)
return super(PersonAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
因此,当用户查看人员的摘要屏幕,并单击公司的过滤器时,他们会看到属于其他公司部门的选择
如果他们单击属于另一个部门的某个部门,他们会得到一个空列表,因为PersonalAdmin实际上无法访问该记录。您是否有任何特殊原因希望列表过滤器中的相关字段关心admin类上的查询集?此类筛选器通常不会删除不存在相关对象的值 如果您不希望出现这种行为,您可以编写一个筛选器类,作为
django.contrib.admin.filter.RelatedFieldListFilter
的子集来限制您喜欢的选项,并使用它,或者重写其选项
方法(您在模型管理中定义的查询集将在该方法中作为cl.root\u query\u set
提供)或者重写它的\uuuu init\uuuu
方法来创建self.lookup\u选项
,可能会根据请求而有所不同。我看不出有任何理由需要继续重新定义类-一个定义应该可以处理任意多个相关字段
下面是一个简单的示例,如果管理员的筛选queryset至少有一个对象作为筛选器值,则该示例应仅包括筛选器中的项:
class RelatedFieldRestrictedListFilter(RelatedFieldListFilter):
def choices(self, cl):
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
yield {
'selected': self.lookup_val is None and not self.lookup_val_isnull,
'query_string': cl.get_query_string({},
[self.lookup_kwarg, self.lookup_kwarg_isnull]),
'display': _('All'),
}
for pk_val, val in self.lookup_choices:
if cl.root_query_set.filter(**{self.lookup_kwarg: pk_val}).exists():
yield {
'selected': self.lookup_val == smart_unicode(pk_val),
'query_string': cl.get_query_string({
self.lookup_kwarg: pk_val,
}, [self.lookup_kwarg_isnull]),
'display': val,
}
if (isinstance(self.field, models.related.RelatedObject)
and self.field.field.null or hasattr(self.field, 'rel')
and self.field.null):
yield {
'selected': bool(self.lookup_val_isnull),
'query_string': cl.get_query_string({
self.lookup_kwarg_isnull: 'True',
}, [self.lookup_kwarg]),
'display': EMPTY_CHANGELIST_VALUE,
}
对于list\u filter
字段中的每个可能值,这会对DB进行单独的查询,因此效率有点低-如果这成为问题,您应该定制\u init\u
后续行动:
好的,正确的筛选器选择取决于请求会话。这意味着您需要在子类的\uuuu init\uuuu
方法中建立它们。您的示例显示了一个相关字段,因此我将再次使用RelatedFieldListFilter
——老实说,我不确定受限查询集的概念对于任何其他类型的筛选器是否有意义。要如果这样做,惰性方法(编写时间更短,效率更低)将是调用超类的\uuuuu init\uuuu
,然后更改self.lookup\u选项
。惰性更小的方法是完全重写\uuuu init\uuuuu
惰性方法如下所示:
from django.utils.encoding import smart_unicode
class RelatedFieldRestrictedListFilter(RelatedFieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
super(RelatedFieldRestrictedListFilter, self).__init__(field, request, params, model, model_admin, field_path)
if 'site' in request.session:
self.lookup_choices = [(instance.pk, smart_unicode(instance) for instance in model.objects.filter(site=request.session['site'])]
else:
# something else here
不那么懒惰的方法将涉及从超类的\uuu init\uuu
方法复制基本代码,并替换self.lookup\u choices=field.get\u choices(include\u blank=False)
行
请注意,我考虑到会话可能没有
站点
——如果是这样的话,你应该考虑一下你希望发生什么。如果用户是超级用户,也许不用麻烦更改查找选项。请给我们一些代码!不,不是“相关字段”过滤器。对于我给出的示例,“公司”是数据库中要汇总的一个字段。例如,它可能是当前公司部门的客户列表。问题是,当“B”部门的用户单击过滤器时,他们会得到一个包含所有部门的所有公司的列表。如果他们单击“a”部门的公司,他们会得到一个“未找到记录”"错误-由于更改视图使用ModelAdmin.queryset。这更清楚吗?因此字段与queryset的状态无关,但它们本身也受请求的限制?这是可行的,但它让我更加困惑,为什么您认为这是自动的。如果您添加了模型和adm,这将更容易理解事实上,你所描述的(“他们得到一份包含所有部门所有公司的列表”)这不是正常的行为。筛选器对管理员的查询集进行操作,因此如果您适当限制该查询集,则无论它们是否匹配筛选器,它们都不会显示在更改列表中。我不清楚您是否担心限制筛选器中显示的选项或更改列表中显示的选项。我可以,但是这是一个复杂的示例。让我们尝试使用为ModelAdmin.queryset提供的简单示例。code
class MyModelAdmin(admin.ModelAdmin):def queryset(self,request):qs=super(MyModelAdmin,self)。queryset(request)if request.user.is_superuser:return qs return qs.filter(author=request.user)code
因此,如果您在ModelAdmin中实际执行了此操作,您只希望让用户看到他保存的记录。因此,在任何列表视图中,您都希望显示的所有筛选器选项也属于该用户。否则,您将看到属于其他用户的选项。如果您在筛选器中单击一个,您将