![](/img/trans.png)
[英]Django ForeignKey limit_choices_to a different ForeignKey id
[英]How to limit choices of ForeignKey choices for Django raw_id_field
當使用raw_id_fields選項顯示時,如何限制在 Django 管理中為ForeignKey
字段顯示的選項?
當呈現為選擇框時, 定義自定義ModelForm
以使用所需的選項設置該字段的查詢集值很簡單。 但是,當使用raw_id_fields
呈現時,這個查詢集似乎被完全忽略了。 它生成指向該ForeignKey
模型的鏈接,允許您通過彈出窗口從該模型中選擇任何記錄。 您仍然可以通過自定義 URL 來過濾這些值,但我找不到通過ModelAdmin
執行此操作的ModelAdmin
。
我在我的 Django 1.8 / Python 3.4 項目中使用了類似於 FSp 的方法:
from django.contrib import admin
from django.contrib.admin import widgets
from django.contrib.admin.sites import site
from django import forms
class BlogRawIdWidget(widgets.ForeignKeyRawIdWidget):
def url_parameters(self):
res = super().url_parameters()
res['type__exact'] = 'PROJ'
return res
class ProjectAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['blog'].queryset = Blog.objects.filter(type='PROJ')
self.fields['blog'].widget = BlogRawIdWidget(rel=Project._meta.get_field('blog').remote_field, admin_site=site)
class Meta:
# Django 1.8 convenience:
fields = '__all__'
model = Project
class ProjectAdmin(admin.ModelAdmin):
form = ProjectAdminForm
raw_id_fields = ('blog',)
只選擇blog.type == 'PROJ'
為外鍵Project.blog
在django.admin
。 因為最終用戶可能並且願意選擇任何東西,不幸的是。
下面的方法對我有用,但它是一個查詢集,會影響需要使用 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', )
對於實際項目,我發現給定的解決方案(自定義ModelAdmin
)有點過於嚴格。
我所做的,通常如下:
ModelAdmin
創建一個自定義過濾器(例如子類化admin.SimpleListFilter
,請參閱文檔) 創建我的小部件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(...)
方法(請參閱文檔)以檢查相關實例是否僅是允許的實例。
我發現這種方法有點復雜,但比限制整個ModelAdmin
靈活得多。
如果您需要根據模型實例過濾 raw_id list_view 彈出窗口,您可以使用以下示例:
1. 編寫自定義小部件
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.在表單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
,我將一個字典傳遞給 url_params,它將在 url 上使用。
如果您只需要過濾靜態類型,@Dmitriy Sintsov 的回答就很好,但在我的情況下,我在兩個模型之間都有外部關系,我希望它根據我正在使用的特定 ID 進行過濾。
以他的回答為基礎,假設Project
與Blog
有外鍵關系,並且在選擇要過濾的Blog
時,您希望它只顯示與Project
相關的那些。 他的回答的這兩個變化表明:
project_id
class BlogRawIdWidget(widgets.ForeignKeyRawIdWidget):
def __init__(self, *args, **kwargs):
self.project_id = kwargs.pop('project_id')
super().__init__(*args, **kwargs)
rel=Project._meta.get_field('blog').remote_field, admin_site=site,
project_id=self.instance.project.id)
完整代碼如下:
from django.contrib import admin
from django.contrib.admin import widgets
from django.contrib.admin.sites import site
from django import forms
class BlogRawIdWidget(widgets.ForeignKeyRawIdWidget):
def __init__(self, *args, **kwargs):
self.project_id = kwargs.pop('project_id')
super().__init__(*args, **kwargs)
def url_parameters(self):
res = super().url_parameters()
res['type__exact'] = 'PROJ'
return res
class ProjectAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['blog'].queryset = Blog.objects.filter(type='PROJ')
self.fields['blog'].widget = BlogRawIdWidget(rel=Project._meta.get_field('blog').remote_field, admin_site=site,
project_id=self.instance.project.id)
class Meta:
# Django 1.8 convenience:
fields = '__all__'
model = Project
class ProjectAdmin(admin.ModelAdmin):
form = ProjectAdminForm
raw_id_fields = ('blog',)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.