在没有自定义HTML的Django Admin中,有没有一种方法可以添加自定义的autocomplete_field,从而在保存时向模型字段提供查询集?

时间:2020-07-31 10:51:24

标签: python django django-forms django-views widget

当前,在我的Django管理员模型视图之一中,我能够按照数据库中的可用状态分别选择多个可用国家(申请国):

enter image description here

但是,当我实际上只与少数几个国家/地区小组一起工作时,这非常烦人。但是我确实需要添加或减去奇数国家,而无需管理和考虑每个国家组的类别名称。

我当时的想法是使用关键字(在应用程序中还可以用于其他目的)

enter image description here

我想做的是通过admin.ModelAdmin添加一个自定义字段,该字段也是autocomplete_field,在下拉列表中显示与国家/地区相关的关键字。如果选择了该下拉菜单中的一个项目,它将向模型字段(适用国家/地区)返回带有所选关键字的国家/地区的查询集。无需保存其他任何内容,无需跟踪过去使用了哪个与国家/地区相关的关键字。这只是一次批量选择多个国家/地区的快捷方式(因此,添加/修改后,自定义字段始终显示为空)。

我想尽可能地以Django admin的方式进行操作,而不用碰触自定义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-> FundingProgram

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 quryset to applicant_countries field

 

1 个答案:

答案 0 :(得分:0)

我设法使用Django Autocomplete Light库解决了这个问题。

这就是我所做的(仅在此处包括相关的补充内容,请阅读有关如何安装该库的文档,等等)。

views.py(Todo:使下面的代码块更加干燥)

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",
    #     )

相关问题