django 如何在自定义表单中使用admin自动完成字段?

nuypyhwy  于 2023-08-08  发布在  Go
关注(0)|答案(3)|浏览(96)

在Django管理界面中,你可以设置一个字段为自动完成字段,例如:

autocomplete_fields = ('countries', )

字符串
这对管理页面很有用,如何在自定义视图/表单中使用自动完成字段?我的研究指向django-autocomplete-light,但当Django已经内置了功能时,安装第三方软件包似乎并不理想。

9jyewag0

9jyewag01#

实际上,可以从管理员使用select 2。你只需要使用AutocompleteSelect小部件。AutocompleteSelect小部件需要一个关系和一个管理站点。
如果您的模型A具有ForeignKey字段指向您想要使用自动完成选择的模型B,则只需使用(如cuto所建议的)

from django.contrib.admin.widgets import AutocompleteSelect
from myapp.model import ModelA, ModelB
from django.contrib import admin

class MyForm(form.Form):
    model_b = forms.ModelChoiceField(
        queryset=ModelB.objects.all(),
        widget=AutocompleteSelect(ModelA._meta.get_field('model_b').remote_field, admin.site)
    )

字符串
因为我没有关系,所以使用了FakeRelation类,因为get_url(self)函数只使用model属性。AutocompleteSelect小部件的使用与autocompete_fields小部件的使用绑定在相同的条件下。

from django.contrib.admin.widgets import AutocompleteSelect
from django.contrib import admin
from django import forms
from myapp.models import Countries
    
class FakeRelation:
    def __init__(self, model):
        self.model = model
    
    
class CustomAutocompleteSelect(AutocompleteSelect):
    def __init__(self, model, admin_site, attrs=None, choices=(), using=None):
        rel = FakeRelation(model)
        super().__init__(rel, admin_site, attrs=attrs, choices=choices, using=using)

class PreventionPlanForm(form.Form):
    date_from = forms.DateField(label="From")
    date_to = forms.DateField(label="To")
    pe1_name = forms.ModelChoiceField(
        queryset=countries.objects.all(),
        widget=CustomAutocompleteSelect(Countries, admin.AdminSite)
    )


正如jenniwren所指出的:请确保在模板中加载正确的Javascript/CSS文件(路径可能会在不同的django版本中发生变化):

  • admin/css/vendor/select2/select2.css
  • admin/js/vendor/select2/select2.full.js
  • admin/css/autocomplete.css
  • admin/js/autocomplete.js

或者简单地将{{ form.media }}添加到模板中,如software engineer所指出的。

5cnsuln7

5cnsuln72#

之前提出的FakeRelation解决方案不再适用于Django 2.2,它需要一个字段示例用于AutocompleteSelect构造函数。
我的项目中有一个合适的字段,所以我可以使用它,但必须传递一个字段示例而不是关系。下面是自定义AutocompleateSelect的代码,它还添加了将特定占位符传递给Select2的选项:

class CustomAutocompleteSelect(AutocompleteSelect):
    def __init__(self, field, prompt="", admin_site=None, attrs=None, choices=(), using=None):
        self.prompt = prompt
        super().__init__(field, admin_site, attrs=attrs, choices=choices, using=using)

    def build_attrs(self, base_attrs, extra_attrs=None):
        attrs = super().build_attrs(base_attrs, extra_attrs=extra_attrs)
        attrs.update({
            'data-ajax--delay': 250,
            'data-placeholder': self.prompt,
            'style': 'width: 30em;'
        })
        return attrs

class AddLittermateForm(forms.Form):
    new_littermate = forms.ModelChoiceField(
        queryset=Dog.objects.all(),
        widget=CustomAutocompleteSelect(LitterDog._meta.get_field(
            'dog'), "Search for a littermate here", admin.site)
    )

字符串
如果一个人没有一个具有合适关系的模型,那么他们将不得不声明一个不受管理的模型:

...
class Meta:
    managed = False

igsr9ssn

igsr9ssn3#

我花了几个小时试图理解为什么我的代码不能工作,直到我偶然发现@jenniwren关于引用css/js文件的评论。这是一个完整的工作解决方案。
要在任何自定义表单中使用AutocompleeSelect小部件,这些小部件对给定模型具有“staff”和“view”访问权限,您可以声明以下内容:

from django.urls import reverse
from django.contrib.admin.widgets import AutocompleteSelect
from django.contrib import admin

class UserAutocompleteSelect(AutocompleteSelect):
    def get_url(self):
        model = CustomUser
        return reverse(self.url_name % (self.admin_site.name, model._meta.app_label, model._meta.model_name))

class UserChoiceField(forms.ModelChoiceField):
    def __init__(self, queryset=None, widget=None, **kwargs):
        if queryset is None:
            queryset = CustomUser.objects.all()
        if widget is None:
            widget = UserAutocompleteSelect(None, admin.site)  # pass `None` for `rel`
        super().__init__(queryset, widget=widget, **kwargs)

class UserAutocompleteSelectForm(forms.ModelForm):
    """
    for changing user on Play objects
    using amdin module autocomplete
    """
    user = UserChoiceField(
        # queryset=CustomUser.objects.all(),
        help_text=_('Select the user to replace the current one')
    )

    class Meta:
        model = Play
        fields = ('user', )

字符串
您可以使用相同的,将CustomUserPlay替换为您自己的型号
如果在你使用的html模板中,这并不能立即工作,那意味着你需要在模板中包含所需的css/js文件。这里有一个简单的方法来做到这一点:
假设窗体在视图中声明为:

form = UserAutocompleteSelectForm()
...
context = {
            'form': form, 
            ...
            }
return render(request, 'users/change_user.html', context)


您应该在html模板中添加以下行,以包含所需的css/js文件:

{% block extrahead %}
{{ block.super }}
{{ form.media }}
{% endblock %}

相关问题