【问题标题】:Custom Django CheckboxSelectMultiple widget rendered as an empty list自定义 Django CheckboxSelectMultiple 小部件呈现为空列表
【发布时间】:2011-12-16 02:56:09
【问题描述】:

我正在尝试创建一个 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)

提前感谢您的帮助。

【问题讨论】:

    标签: django django-forms django-widget


    【解决方案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]
    

    【讨论】:

    • 谢谢@Alasdair,但我已经尝试过了。它不能解决问题。
    • 我用不同的方法替换了我的答案。
    • @Alasdair 在谷歌搜索时我在这里找到了你的答案,我也在努力解决一个问题需要一些想法来执行此操作,请参阅我的 SO 问题stackoverflow.com/questions/18592136/…
    • @MonkL 这个问题与你的有点不同,因为它是关于自定义显示的选项的值,而你的问题是关于自定义模板中选项的表示(通过添加图像到每一个)。正如 Dante 教授在他的回答中所建议的那样,您应该能够使用 CSS 添加图像。恐怕我没有时间详细讨论你的问题。我希望你设法解决你的问题。
    • @Alasdair 你是正确的,我目前需要的是
    • 标签中的 Multiplechoicefield 渲染值,我想将
    • 替换为
      标签。如果你有任何想法请与我分享。如果我应用 css,它会渲染一次,因为选择是
    • 标签列表。谢谢
    【解决方案2】:

    我设法通过自定义字段将选项传递给自定义小部件来找到解决方法。初始参数继承自 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]
    

    【讨论】:

    • 您不再需要TigerWidget,因为所有自定义都在TigerField 中。你可以改用widget = forms.CheckboxSelectMultiple
    • 另外,你应该继承 forms.CheckboxSelectMultiple 而不是 forms.Field 以便你的 TigerField 继承 CheckboxSelectMultiple 的功能(例如验证)。
    猜你喜欢
    相关资源
    最近更新 更多
    热门标签