在没有自定义 HTML 的 Django Admin 中,有没有一种方法可以添加自定义 autocomplete_field,在保存时为模型字段提供查询集?
Is there a way in Django Admin, without custom HTML, to add a custom autocomplete_field that upon save provides a queryset to a model field?
目前,在我的 Django 管理模型视图之一中,我能够单独 select 多个可用国家(申请国家),如我的数据库中可用:
但是当我实际上只与少数国家/地区分组合作时,这非常令人厌烦。但我确实需要添加或减去奇数国家/地区,而无需管理和考虑每组国家/地区的类别名称。
我的想法是使用关键字(在应用程序中也有其他用途):
我想做的是通过 admin.ModelAdmin
添加一个自定义字段,它也是一个 autocomplete_field
,在下拉列表中显示与国家相关的关键字。如果此下拉列表中的项目是 selected,它将 return 一个国家查询集,模型字段(申请国家/地区)具有 selected 关键字。不需要保存任何其他内容,不需要跟踪过去使用了哪个国家/地区相关的关键字。这只是一次批量 select 多个国家的捷径(因此在 add/edit 后,自定义字段将始终显示为空)。
我希望尽可能以 Django 管理方式做事,而不涉及自定义 HTML 之类的事情。如果这不可能,我将探索涉及 Django-Q 任务的解决方案。
下面是一些解释关系的代码:
models.py -> 关键词:
class Keyword(models.Model):
name = models.TextField()
models.py -> 国家:
class Country(models.Model):
"""
More info: https://en.wikipedia.org/wiki/ISO_3166-1
"""
name = models.TextField()
alpha_2_code = models.CharField(max_length=2)
alpha_3_code = models.CharField(max_length=3)
independent = models.BooleanField()
keywords = models.ManyToManyField(Keyword)
models.py -> 资助计划
class FundingProgram(models.Model):
# Unimportant fields redacted
applicant_countries = models.ManyToManyField(Country, blank=True)
admin.py -> FundingProgramAdmin
@admin.register(models.FundingProgram)
class FundingProgramAdmin(admin.ModelAdmin, DynamicArrayMixin):
autocomplete_fields = (
"applicant_countries",
)
search_fields = (
"applicant_countries__keywords__name",
)
# Here is where I think I should make a custom field,
# that acts as a multiple choice (autocomplete) dropdown where
# a function takes input and returns a queryset to applicant_countries field
我设法使用 Django Autocomplete Light 库解决了这个问题。
这就是我所做的(这里只包括相关的补充,阅读关于如何安装库等的文档)。
views.py(待办事项:让下面的代码块更干)
from dal import autocomplete
from my_app import models
class KeywordsSelect(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return models.Keyword.objects.none()
qs = models.Keyword.objects.all()
if self.q:
# Todo: Probably worth sorting the queryset for consistency
qs = qs.filter(name__istartswith=self.q)
return qs
def get_result_value(self, result):
return result.pk
class CountriesSelect(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return models.Country.objects.none()
qs = models.Country.objects.all()
if self.q:
# Todo: Probably worth sorting the queryset for consistency
qs = qs.filter(name__istartswith=self.q)
return qs
def get_result_value(self, result):
return result.pk
urls.py
from django.conf.urls import url
from my_app import views
urlpatterns = [
url(r"^keywords-select/$", views.KeywordsSelect.as_view(), name="keywords-select",),
url(r"^countries-select/$", views.CountriesSelect.as_view(), name="countries-select",),
]
上面创建了一个端点,可以像这样查询:http://localhost:8000/countries-select/?q=Austri
,返回一个输出,如:
{
"results": [
{
"id": 9,
"text": "Austria",
"selected_text": "Austria"
}
],
"pagination": {
"more": false
}
}
现在我们可以修改 admin 以包含修改后的表单,该表单将在自动选择框中调用此端点。请注意,我需要明确列出所有字段并让两个字段并排存在,它们需要包含在自己的元组中,如下 ("applicant_countries", "bulk_add_countries_by_keyword",)
。
admin.py
from dal import autocomplete
from django import forms
class FundingProgramForm(forms.ModelForm):
# Newly created field not retrieved from models.py
bulk_add_countries_by_keyword = forms.ModelMultipleChoiceField(
queryset=models.Keyword.objects.all(),
widget=autocomplete.ModelSelect2Multiple(url="keywords-select"),
required=False,
)
class Meta:
# applicant_countries is a field from models.py which gets overridden here
widgets = {
"applicant_countries": autocomplete.ModelSelect2Multiple(
url="countries-select"
)
}
def save(self, commit=True):
qs = self.cleaned_data["applicant_countries"].union(
models.Country.objects.filter(
keywords__in=self.cleaned_data["bulk_add_countries_by_keyword"]
)
)
self.cleaned_data["applicant_countries"] = qs
return super(FundingProgramForm, self).save(commit=commit)
@admin.register(models.FundingProgram)
class FundingProgramAdmin(admin.ModelAdmin):
form = FundingProgramForm
# Other fields hardcoded below, otherwise they won't appear in model form
fields = (
("applicant_countries", "bulk_add_countries_by_keyword",),
)
# Warning: Remove the following from autocomplete_fields, as it is already overidden with a django autocomplete light widget
# autocomplete_fields = (
# "applicant_countries",
# )
目前,在我的 Django 管理模型视图之一中,我能够单独 select 多个可用国家(申请国家),如我的数据库中可用:
但是当我实际上只与少数国家/地区分组合作时,这非常令人厌烦。但我确实需要添加或减去奇数国家/地区,而无需管理和考虑每组国家/地区的类别名称。
我的想法是使用关键字(在应用程序中也有其他用途):
我想做的是通过 admin.ModelAdmin
添加一个自定义字段,它也是一个 autocomplete_field
,在下拉列表中显示与国家相关的关键字。如果此下拉列表中的项目是 selected,它将 return 一个国家查询集,模型字段(申请国家/地区)具有 selected 关键字。不需要保存任何其他内容,不需要跟踪过去使用了哪个国家/地区相关的关键字。这只是一次批量 select 多个国家的捷径(因此在 add/edit 后,自定义字段将始终显示为空)。
我希望尽可能以 Django 管理方式做事,而不涉及自定义 HTML 之类的事情。如果这不可能,我将探索涉及 Django-Q 任务的解决方案。
下面是一些解释关系的代码:
models.py -> 关键词:
class Keyword(models.Model):
name = models.TextField()
models.py -> 国家:
class Country(models.Model):
"""
More info: https://en.wikipedia.org/wiki/ISO_3166-1
"""
name = models.TextField()
alpha_2_code = models.CharField(max_length=2)
alpha_3_code = models.CharField(max_length=3)
independent = models.BooleanField()
keywords = models.ManyToManyField(Keyword)
models.py -> 资助计划
class FundingProgram(models.Model):
# Unimportant fields redacted
applicant_countries = models.ManyToManyField(Country, blank=True)
admin.py -> FundingProgramAdmin
@admin.register(models.FundingProgram)
class FundingProgramAdmin(admin.ModelAdmin, DynamicArrayMixin):
autocomplete_fields = (
"applicant_countries",
)
search_fields = (
"applicant_countries__keywords__name",
)
# Here is where I think I should make a custom field,
# that acts as a multiple choice (autocomplete) dropdown where
# a function takes input and returns a queryset to applicant_countries field
我设法使用 Django Autocomplete Light 库解决了这个问题。
这就是我所做的(这里只包括相关的补充,阅读关于如何安装库等的文档)。
views.py(待办事项:让下面的代码块更干)
from dal import autocomplete
from my_app import models
class KeywordsSelect(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return models.Keyword.objects.none()
qs = models.Keyword.objects.all()
if self.q:
# Todo: Probably worth sorting the queryset for consistency
qs = qs.filter(name__istartswith=self.q)
return qs
def get_result_value(self, result):
return result.pk
class CountriesSelect(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated:
return models.Country.objects.none()
qs = models.Country.objects.all()
if self.q:
# Todo: Probably worth sorting the queryset for consistency
qs = qs.filter(name__istartswith=self.q)
return qs
def get_result_value(self, result):
return result.pk
urls.py
from django.conf.urls import url
from my_app import views
urlpatterns = [
url(r"^keywords-select/$", views.KeywordsSelect.as_view(), name="keywords-select",),
url(r"^countries-select/$", views.CountriesSelect.as_view(), name="countries-select",),
]
上面创建了一个端点,可以像这样查询:http://localhost:8000/countries-select/?q=Austri
,返回一个输出,如:
{
"results": [
{
"id": 9,
"text": "Austria",
"selected_text": "Austria"
}
],
"pagination": {
"more": false
}
}
现在我们可以修改 admin 以包含修改后的表单,该表单将在自动选择框中调用此端点。请注意,我需要明确列出所有字段并让两个字段并排存在,它们需要包含在自己的元组中,如下 ("applicant_countries", "bulk_add_countries_by_keyword",)
。
admin.py
from dal import autocomplete
from django import forms
class FundingProgramForm(forms.ModelForm):
# Newly created field not retrieved from models.py
bulk_add_countries_by_keyword = forms.ModelMultipleChoiceField(
queryset=models.Keyword.objects.all(),
widget=autocomplete.ModelSelect2Multiple(url="keywords-select"),
required=False,
)
class Meta:
# applicant_countries is a field from models.py which gets overridden here
widgets = {
"applicant_countries": autocomplete.ModelSelect2Multiple(
url="countries-select"
)
}
def save(self, commit=True):
qs = self.cleaned_data["applicant_countries"].union(
models.Country.objects.filter(
keywords__in=self.cleaned_data["bulk_add_countries_by_keyword"]
)
)
self.cleaned_data["applicant_countries"] = qs
return super(FundingProgramForm, self).save(commit=commit)
@admin.register(models.FundingProgram)
class FundingProgramAdmin(admin.ModelAdmin):
form = FundingProgramForm
# Other fields hardcoded below, otherwise they won't appear in model form
fields = (
("applicant_countries", "bulk_add_countries_by_keyword",),
)
# Warning: Remove the following from autocomplete_fields, as it is already overidden with a django autocomplete light widget
# autocomplete_fields = (
# "applicant_countries",
# )