如何限制Django raw_id_field的ForeignKey选择
如何限制使用raw_id_fields选项显示的Django 's admin when they'中显示的ForeignKey字段的选项?
当呈现为选择框时,设置该字段's queryset value with the choices to want. However, this queryset appears to be completely ignored when rendered using raw_id_fields. It generates a link to that ForeignKey'的模型很简单,允许您通过弹出窗口从该模型中选择任何记录 . 您仍然可以通过自定义URL来过滤这些值,但我找不到通过ModelAdmin执行此操作的方法 .
回答(5)
对于实际项目,我发现给定的解决方案(自定义 ModelAdmin
查询集)有点过于严格 .
我的工作通常如下:
-
在我的
ModelAdmin
中创建自定义过滤器(例如,子类化admin.SimpleListFilter
,请参阅doc) -
创建我的widget
ForeignKeyRawIdWidget
的子类,如下所示:
class CustomRawIdWidget(ForeignKeyRawIdWidget):
def url_parameters(self):
"""
activate one or more filters by default
"""
res = super(CustomRawIdWidget, self).url_parameters()
res["<filter_name>__exact"] = "<filter_value>"
return res
请注意,自定义小部件唯一做的是“预选”过滤器,而过滤器又负责“限制”查询集
- 使用自定义小部件:
class MyForm(forms.ModelForm):
myfield = forms.ModelChoiceField(queryset=MyModel.objects.all(),
...
widget=CustomRawIdWidget(
MyRelationModel._meta.get_field('myfield').rel,
admin.site))
此方法的一个弱点是窗口小部件选择的过滤器不会阻止从该模型中选择其他实例 . 如果需要,我重写方法 ModelAdmin.save_model(...)
(参见doc)以检查相关实例是否只是允许的实例 .
我发现这种方法有点复杂,但比限制整个 ModelAdmin
的查询集要灵活得多 .
下面的方法适用于我,但它是一个影响每个需要使用Customer模型的管理员的查询集 . 但如果你有另一个管理员,例如需要不同查询集的发票,您可能想要使用模型代理进行一些实验 .
模型
class Customer(models.Model):
name = models.CharField(max_length=100)
is_active = models.BooleanField()
class Order(models.Model):
cust = models.ForeignKey(Customer)
管理员
class CustomerAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(CustomerAdmin, self).queryset(request)
return qs.filter(is_active=1)
class OrderAdmin():
raw_id_fields = ('cust', )
如果您需要根据模型实例过滤raw_id list_view弹出窗口,可以使用以下示例:
1. Write custom widget
class RawIdWidget(widgets.ForeignKeyRawIdWidget):
def url_parameters(self):
res = super(RawIdWidget, self).url_parameters()
object = self.attrs.get('object', None)
if object:
# Filter variants by product_id
res['product_id'] = object.variant.product_id
return res
2. Pass instance on form init
class ModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ModelForm, self).__init__(*args, **kwargs)
obj = kwargs.get('instance', None)
if obj and obj.pk is not None:
self.fields['variant'].widget = RawIdWidget(
rel=obj._meta.get_field('variant').rel,
admin_site=admin.site,
# Pass the object to attrs
attrs={'object': obj}
)
我创建了一个遗传解决方案来解决传递给弹出窗口的自定义参数的问题 . 您只需要在项目中复制此代码:
from django.contrib.admin import widgets
class GenericRawIdWidget(widgets.ForeignKeyRawIdWidget):
url_params = []
def __init__(self, rel, admin_site, attrs=None, \
using=None, url_params=[]):
super(GenericRawIdWidget, self).__init__(
rel, admin_site, attrs=attrs, using=using)
self.url_params = url_params
def url_parameters(self):
"""
activate one or more filters by default
"""
res = super(GenericRawIdWidget, self).url_parameters()
res.update(**self.url_params)
return res
然后,您可以像这样使用:
field.widget = GenericRawIdWidget(YOURMODEL._meta.get_field('YOUR_RELATION').rel,
admin.site, url_params={"<YOURMODEL>__id__exact": object_id})
我用这种方式使用它:
class ANSRuleInline(admin.TabularInline):
model = ANSRule
form = ANSRuleInlineForm
extra = 1
raw_id_fields = ('parent',)
def __init__(self, *args, **kwargs):
super (ANSRuleInline,self ).__init__(*args,**kwargs)
def formfield_for_dbfield(self, db_field, **kwargs):
formfield = super(ANSRuleInline, self).formfield_for_dbfield(db_field, **kwargs)
request = kwargs.get("request", None)
object_id = self.get_object(request)
if db_field.name == 'parent':
formfield.widget = GenericRawIdWidget(ANSRule._meta.get_field('parent').rel,
admin.site, url_params={"pathology__id__exact": object_id})
return formfield
def get_object(self, request):
object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
try:
object_id = int(object_id)
except ValueError:
return None
return object_id
当我使用 GenericRawIdWidget
时,我将dict传递给url_params,它将在url上使用 .
2 years ago
我在Django 1.8 / Python 3.4项目中使用类似于FSp的方法:
在django.admin中只选择blog.type =='PROJ'作为外键Project.blog . 因为最终用户可能会并且将会选择任何东西,不幸的是 .