在Django 1.5.1中,我设置了一个供内部使用的SAAS系统,每个部门都有自己的数据集。
为此,我使用 ModelAdmin.queryset (https://docs.djangoproject.com/en/1.5/ref/contrib/admin/#django.contrib.admin.ModelAdmin.queryset)将所有数据限制为仅属于的记录到当前记录的部门在用户。
这适用于主要的管理功能(汇总表等)。但是我在“ModelAdmin.list_filter”上的任何内容都显示了所有值 - 显然使用的是基本查询集,而不是我在 ModelAdmin.queryset 中定义的值。
我可以在这里看到如何定义自定义 ModelAdmin.list_filter 查询:https://docs.djangoproject.com/en/1.5/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter,它还允许您定义自定义过滤器管理器,其中可以包含自定义查询集。
但这是 PER FILTER FIELD!
对于添加 ModelAdmin.queryset 后应该自动生成的内容,这似乎是非常多的工作!
我是否有一种更简单的方式在这里丢失,或者这可能是我应该买的东西?
提前致谢,
富
彼得,谢谢我的裤子。结果我对自己的问题的理解是错误的!
=================我的代码示例================
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)
因此,当用户查看人员的摘要屏幕,并点击过滤器公司时,他们会看到属于其他公司部门的选项。
如果他们点击属于另一个部门的人,他们会得到一个空列表,因为PersonAdmin实际上无法访问该记录。
答案 0 :(得分:2)
您是否有任何特殊原因希望list_filter
中的相关字段关注管理类的查询集?此类过滤器通常不会删除没有相关对象的值。
如果您不想要这种行为,您可以编写一个过滤器类来限制您喜欢的选项作为django.contrib.admin.filter.RelatedFieldListFilter
的子集,并使用它来覆盖其choices
方法(您在模型管理员上定义的查询集将在该方法中以cl.root_query_set
的形式提供)或覆盖其__init__
方法以不同方式创建self.lookup_choices
- 可能基于请求。我认为没有理由继续重新定义类 - 一个定义应该与你喜欢的相关领域一起使用。
如果admin的过滤后的查询集至少有一个过滤器值的对象,那么这个过程应该只包含过滤器中的项:
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_filter
字段中的每个可能值对数据库执行单独查询,因此效率有点低 - 如果问题变成问题,则应自定义__init__
。
跟进:
好的,所以正确的过滤器选择取决于请求会话。这意味着您需要在子类的__init__
方法中建立它们。您的示例显示了一个相关字段,因此我将再次使用RelatedFieldListFilter
- 老实说,我不确定受限查询集的概念是否适用于任何其他类型的过滤器。要做到这一点,懒惰的方法(写入更短,效率更低)将调用超类的__init__
,然后更改self.lookup_choices
。不太懒惰的方法是完全覆盖__init__
。
懒惰的方法是这样的:
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
不太懒惰的方法将涉及从超类的__init__
方法复制基本代码并用上面的代替self.lookup_choices = field.get_choices(include_blank=False)
行。
请注意,我允许会话可能没有site
- 如果是这种情况,你应该考虑一下你想要发生什么。如果用户是超级用户,也许不需要更改lookup_choices
。