自定义Django CheckboxSelectMultiple小部件呈现为空列表

时间:2011-10-26 22:35:51

标签: django django-forms django-widget

我正在尝试创建一个CheckboxSelectMultiple小部件,其中列出了我项目的所有内容类型。我开始在ModelForm中定义MultipleChoiceField字段时使用基本窗口小部件,它工作正常。我现在想把它变成一个自定义小部件,我可以将应用程序导入到任何项目中。

以下是我正在使用的代码:

# myapp/models.py

from django.db import models

class Tiger(models.Model):
    color = models.CharField(max_length=100)

# myapp/admin.py

from django import forms
from django.contrib import admin
from django.contrib.contenttypes.models import ContentType
from myapp.models import Tiger

class TigerForm(forms.ModelForm):
    """
    Default ModelForm that will be overriden.
    """
    class Meta:
        model = Tiger

这是我定义自定义小部件的地方。我猜我没有正确传递值列表(请参阅代码中的注释)。

class TigerWidget(forms.CheckboxSelectMultiple):
    """
    Custom widget that displays a list of checkboxes for each content type.
    The goal is to make it generic enough to put it in an external app
    that can be imported into any project.
    """
    def __init__(self, attrs=None, choices=()):

        # List all content types
        content_types = ContentType.objects.all()
        classes = tuple([(c.model, c.name) for c in content_types])

        # Define widget choices as the list of these content types
        self.choices = classes  # I am guessing it should be done differently?

        # Select all of them by default
        self.initial = [c[0] for c in classes]

        # Same behavior as parent
        super(TigerWidget, self).__init__(attrs)

以下是使用它的其余类。

class TigerCustomForm(TigerForm):
    """
    Custom form that adds a field using the custom widget to the form.
    """
    # content_types = ContentType.objects.all()
    # classes = tuple([(c.model, c.name) for c in content_types])

    # This works fine.
    # nickname = forms.MultipleChoiceField(
    #     widget=forms.CheckboxSelectMultiple,
    #     choices=classes,
    #     # Select all by default
    #     initial=[c[0] for c in classes]
    # )

    # This does not. An empty list (<ul></ul>) is rendered in the place of the widget.
    nickname = forms.MultipleChoiceField(
        widget=TigerWidget,
    )

class TigerAdmin(admin.ModelAdmin):
    form = TigerCustomForm

admin.site.register(Tiger, TigerAdmin)
admin.site.register(ContentType)

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

小部件负责呈现html,例如显示多个选择框(forms.MultipleSelect)或多个复选框(forms.CheckboxSelectMultiple)。这是对该字段显示选项的单独决定。

我认为继承forms.MultipleChoiceField并在那里设置选择会更好。

class TigerMultipleChoiceField(forms.MultipleChoiceField):
    """
    Custom widget that displays a list of checkboxes for each content type.
    The goal is to make it generic enough to put it in an external app
    that can be imported into any project.
    """
    def __init__(self, *args, **kwargs):
        # Same behavior as parent
        super(TigerMultipleChoiceField, self).__init__(*args, **kwargs)


        # List all content types
        content_types = ContentType.objects.all()
        classes = tuple([(c.model, c.name) for c in content_types])

        # Define widget choices as the list of these content types
        self.choices = classes

        # Select all of them by default
        self.initial = [c[0] for c in classes]

答案 1 :(得分:0)

我设法通过自定义字段将选项传递给自定义窗口小部件来找到解决方法。初始参数继承自Field类,我在自定义Field构造函数方法中定义它。

这里是最终代码:

class TigerWidget(forms.CheckboxSelectMultiple):
    """
    Custom widget that displays a list of checkboxes for
    each content type.
    The goal is to make it generic enough to put it  in an
    external app that can be imported into any project.
    """
    def __init__(self, attrs=None, choices=()):
        super(TigerWidget, self).__init__(attrs=attrs, choices=choices)

class TigerField(forms.Field):
    """
    Custom Field that makes use of the custom widget (in the external app too). It will need to be herited
    from.
    """
    # Default behavior: displays all existing content types. Can be overriden in
    # the child class.
    content_types = ContentType.objects.all()
    choices = tuple([(c.model, c.name) for c in content_types])

    widget = TigerWidget(choices=choices)

    def __init__(self, *args, **kwargs):
        super(TigerField, self).__init__(args, kwargs)
        # Selects all by default
        self.initial = [c[0] for c in self.__class__.choices]