1

プロジェクトのすべてのコンテンツ タイプを一覧表示する 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)

よろしくお願いします。

4

2 に答える 2

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]
于 2011-10-26T23:10:53.363 に答える
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]
于 2011-10-27T09:19:53.817 に答える