如何在模型表单中使用自动完成选择小部件

时间:2019-04-26 04:17:58

标签: python django modelform

我知道Django 2.0中有一个新功能,即ModelAdmin中的AutocompleteSelect小部件。 我正在尝试在自定义的modelForm中使用它,但是失败了。

尝试过

#unit is the foreign key to the incident

class AccountForm(forms.ModelForm):
    class Meta:
        model = Invoice
        ...
        ...
        widgets = {       'incident':widgets.AutocompleteSelect(Invoice._meta.get_field('incident').remote_field, admin.site)
        }
        ...
#Invoice model

class Invoice(models.Model):
    ...
    incident = models.ForeignKey(Unit, on_delete=models.CASCADE,null=True)
    ...

希望任何人都可以帮助我。 谢谢

3 个答案:

答案 0 :(得分:1)

“自动完成选择”窗口小部件将无法在管理站点之外运行。 如果您在管理站点中使用AccountForm,则可以使用以下代码:

class AccountForm(forms.ModelForm):
    ...
    incident = forms.ModelChoiceField(
                 queryset= Unit.objects.all(),
                 widget=AutocompleteSelect(Invoice.incident.field.remote_field, admin.site),
               )
    ...
    class Meta:
        model = Invoice
        fields = [
            'incident',
            ...
        ]

@admin.register(Invoice)
class InvoiceAdmin(admin.ModelAdmin):
    form = AccountForm

答案 1 :(得分:0)

AutocompleteSelect具有2个必需的参数reladmin_siterel用于提取模型,该模型用于从ForeignKey或ManyToManyField上的属性查询数据并与之相关。由于我想在实际上不是外键的字段上使用它,因此我需要重写一些内容以使其起作用:

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

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

    def to_python(self, value):
        return value  # just return the client_id and not a Client object

class MyAdminForm(forms.ModelForm):
    client_id=ClientChoiceField()

    ...

这要求最终用户对要查询的模型的自动完成端点具有管理员读取权限。不过,您也许可以进行更多的黑客操作来更改get_url,并使用自己的端点提供搜索结果。

答案 2 :(得分:0)

我花了几个小时试图理解为什么我的代码(基于 @Tim 的答案)不起作用,直到我偶然发现了关于缺少对 css/js 文件的引用的评论。

以下是一个完整的工作解决方案,可以在任何自定义表单中使用 AutocompleteSelect 小部件,供对给定模型具有“员工”和“查看”访问权限的登录用户使用:

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 %}