Django表单中,自定义SelectField和SelectMultipleField

发布于 2024-08-14 07:43:49 字数 912 浏览 5 评论 0原文

我现在每天都使用 Django,已经三个月了,它真的很棒。快速 Web 应用程序开发。

我还有一件事不能完全按照自己的意愿去做。 它是 SelectField 和 SelectMultiple 字段。

我希望能够将一些参数添加到 Select 的选项中。

我终于成功使用了 optgroup :

class EquipmentField(forms.ModelChoiceField):
    def __init__(self, queryset, **kwargs):
        super(forms.ModelChoiceField, self).__init__(**kwargs)
        self.queryset = queryset
        self.to_field_name=None

        group = None
        list = []
        self.choices = []

        for equipment in queryset:
            if not group:
                group = equipment.type

            if group != equipment.type:
                self.choices.append((group.name, list))
                group = equipment.type
                list = []
            else:
                list.append((equipment.id, equipment.name))

但是对于另一个 ModelForm,我必须使用模型的颜色属性来更改每个选项的背景颜色。

你知道我该怎么做吗?

谢谢。

I am using Django everyday now for three month and it is really great. Fast web application development.

I have still one thing that I cannot do exactly how I want to.
It is the SelectField and SelectMultiple Field.

I want to be able to put some args to an option of a Select.

I finally success with the optgroup :

class EquipmentField(forms.ModelChoiceField):
    def __init__(self, queryset, **kwargs):
        super(forms.ModelChoiceField, self).__init__(**kwargs)
        self.queryset = queryset
        self.to_field_name=None

        group = None
        list = []
        self.choices = []

        for equipment in queryset:
            if not group:
                group = equipment.type

            if group != equipment.type:
                self.choices.append((group.name, list))
                group = equipment.type
                list = []
            else:
                list.append((equipment.id, equipment.name))

But for another ModelForm, I have to change the background color of every option, using the color property of the model.

Do you know how I can do that ?

Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

追我者格杀勿论 2024-08-21 07:43:49

您需要做的是更改由小部件控制的输出。默认是选择小部件,因此您可以对其进行子类化。它看起来像这样:

class Select(Widget):
    def __init__(self, attrs=None, choices=()):
        super(Select, self).__init__(attrs)
        # choices can be any iterable, but we may need to render this widget
        # multiple times. Thus, collapse it into a list so it can be consumed
        # more than once.
        self.choices = list(choices)

    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = ''
        final_attrs = self.build_attrs(attrs, name=name)
        output = [u'<select%s>' % flatatt(final_attrs)]
        options = self.render_options(choices, [value])
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe(u'\n'.join(output))

    def render_options(self, choices, selected_choices):
        def render_option(option_value, option_label):
            option_value = force_unicode(option_value)
            selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
            return u'<option value="%s"%s>%s</option>' % (
                escape(option_value), selected_html,
                conditional_escape(force_unicode(option_label)))
        # Normalize to strings.
        selected_choices = set([force_unicode(v) for v in selected_choices])
        output = []
        for option_value, option_label in chain(self.choices, choices):
            if isinstance(option_label, (list, tuple)):
                output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
                for option in option_label:
                    output.append(render_option(*option))
                output.append(u'</optgroup>')
            else:
                output.append(render_option(option_value, option_label))
        return u'\n'.join(output)

有很多代码。但您需要做的是使用更改的渲染方法来制作您自己的小部件。渲染方法决定了创建的 html。在本例中,您需要更改的是 render_options 方法。在这里,您可以进行一些检查以确定何时添加您可以设置样式的类。

另一件事,在上面的代码中,您看起来并没有附加最后的组选择。此外,您可能希望将 order_by() 添加到查询集中,因为您需要按类型对其进行排序。您可以在 init 方法中执行此操作,因此在使用表单字段时不必重新执行此操作。

What you need to do, is to change the output which is controlled by the widget. Default is the select widget, so you can subclass it. It looks like this:

class Select(Widget):
    def __init__(self, attrs=None, choices=()):
        super(Select, self).__init__(attrs)
        # choices can be any iterable, but we may need to render this widget
        # multiple times. Thus, collapse it into a list so it can be consumed
        # more than once.
        self.choices = list(choices)

    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = ''
        final_attrs = self.build_attrs(attrs, name=name)
        output = [u'<select%s>' % flatatt(final_attrs)]
        options = self.render_options(choices, [value])
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe(u'\n'.join(output))

    def render_options(self, choices, selected_choices):
        def render_option(option_value, option_label):
            option_value = force_unicode(option_value)
            selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
            return u'<option value="%s"%s>%s</option>' % (
                escape(option_value), selected_html,
                conditional_escape(force_unicode(option_label)))
        # Normalize to strings.
        selected_choices = set([force_unicode(v) for v in selected_choices])
        output = []
        for option_value, option_label in chain(self.choices, choices):
            if isinstance(option_label, (list, tuple)):
                output.append(u'<optgroup label="%s">' % escape(force_unicode(option_value)))
                for option in option_label:
                    output.append(render_option(*option))
                output.append(u'</optgroup>')
            else:
                output.append(render_option(option_value, option_label))
        return u'\n'.join(output)

It's a lot of code. But what you need to do, is to make your own widget with an altered render method. It's the render method that determines the html that is created. In this case, it's the render_options method you need to change. Here you could include some check to determine when to add a class, which you could style.

Another thing, in your code above it doesn't look like you append the last group choices. Also you might want to add an order_by() to the queryset, as you need it to be ordered by the type. You could do that in the init method, so you don't have to do it all over when you use the form field.

柠檬色的秋千 2024-08-21 07:43:49

render_option 已从 Django 1.11 开始删除。这就是我为实现这一目标所做的事情。稍微挖掘一下,这看起来就简单明了。适用于 Django 2.0+

class CustomSelect(forms.Select):
    def __init__(self, attrs=None, choices=()):
        self.custom_attrs = {}
        super().__init__(attrs, choices)

    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        index = str(index) if subindex is None else "%s_%s" % (index, subindex)
        if attrs is None:
            attrs = {}
        option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
        if selected:
            option_attrs.update(self.checked_attribute)
        if 'id' in option_attrs:
            option_attrs['id'] = self.id_for_label(option_attrs['id'], index)

        # setting the attributes here for the option
        if len(self.custom_attrs) > 0:
            if value in self.custom_attrs:
                custom_attr = self.custom_attrs[value]
                for k, v in custom_attr.items():
                    option_attrs.update({k: v})

        return {
            'name': name,
            'value': value,
            'label': label,
            'selected': selected,
            'index': index,
            'attrs': option_attrs,
            'type': self.input_type,
            'template_name': self.option_template_name,
        }


class MyModelChoiceField(ModelChoiceField):

    # custom method to label the option field
    def label_from_instance(self, obj):
        # since the object is accessible here you can set the extra attributes
        if hasattr(obj, 'type'):
            self.widget.custom_attrs.update({obj.pk: {'type': obj.type}})
        return obj.get_display_name()

形式:

class BookingForm(forms.ModelForm):

    customer = MyModelChoiceField(required=True,
                                  queryset=Customer.objects.filter(is_active=True).order_by('name'),
                                  widget=CustomSelect(attrs={'class': 'chosen-select'}))

我需要的输出如下:

  <select name="customer" class="chosen-select" required="" id="id_customer">
      <option value="" selected="">---------</option>
      <option value="242" type="CNT">AEC Transcolutions Private Limited</option>
      <option value="243" type="CNT">BBC FREIGHT CARRIER</option>
      <option value="244" type="CNT">Blue Dart Express Limited</option>

render_option has been removed from Django 1.11 onwards. This is what I did to achieve this. A little bit of digging and this seems straightforward and neat. Works with Django 2.0+

class CustomSelect(forms.Select):
    def __init__(self, attrs=None, choices=()):
        self.custom_attrs = {}
        super().__init__(attrs, choices)

    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        index = str(index) if subindex is None else "%s_%s" % (index, subindex)
        if attrs is None:
            attrs = {}
        option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
        if selected:
            option_attrs.update(self.checked_attribute)
        if 'id' in option_attrs:
            option_attrs['id'] = self.id_for_label(option_attrs['id'], index)

        # setting the attributes here for the option
        if len(self.custom_attrs) > 0:
            if value in self.custom_attrs:
                custom_attr = self.custom_attrs[value]
                for k, v in custom_attr.items():
                    option_attrs.update({k: v})

        return {
            'name': name,
            'value': value,
            'label': label,
            'selected': selected,
            'index': index,
            'attrs': option_attrs,
            'type': self.input_type,
            'template_name': self.option_template_name,
        }


class MyModelChoiceField(ModelChoiceField):

    # custom method to label the option field
    def label_from_instance(self, obj):
        # since the object is accessible here you can set the extra attributes
        if hasattr(obj, 'type'):
            self.widget.custom_attrs.update({obj.pk: {'type': obj.type}})
        return obj.get_display_name()

The form:

class BookingForm(forms.ModelForm):

    customer = MyModelChoiceField(required=True,
                                  queryset=Customer.objects.filter(is_active=True).order_by('name'),
                                  widget=CustomSelect(attrs={'class': 'chosen-select'}))

The output which I needed is as:

  <select name="customer" class="chosen-select" required="" id="id_customer">
      <option value="" selected="">---------</option>
      <option value="242" type="CNT">AEC Transcolutions Private Limited</option>
      <option value="243" type="CNT">BBC FREIGHT CARRIER</option>
      <option value="244" type="CNT">Blue Dart Express Limited</option>
离笑几人歌 2024-08-21 07:43:49

进行搜索时,我多次遇到这个问题

在通过“如何自定义/填充 Django SelectField 选项”

Dimitris Kougioumtzis 提供的答案很简单

希望它可以帮助像我这样的人。

# forms.py
from django.forms import ModelForm, ChoiceField
from .models import MyChoices

class ProjectForm(ModelForm):
    choice = ChoiceField(choices=[
        (choice.pk, choice) for choice in MyChoices.objects.all()])
# admin.py
class ProjectAdmin(BaseAdmin):
    form = ProjectForm
    ....

I run into this question many times when searching by

'how to customize/populate Django SelectField options'

The answer provided by Dimitris Kougioumtzis is quite easy

Hope it could help somebody like me.

# forms.py
from django.forms import ModelForm, ChoiceField
from .models import MyChoices

class ProjectForm(ModelForm):
    choice = ChoiceField(choices=[
        (choice.pk, choice) for choice in MyChoices.objects.all()])
# admin.py
class ProjectAdmin(BaseAdmin):
    form = ProjectForm
    ....
葬花如无物 2024-08-21 07:43:49

您不应该为了向呈现的 html 标记添加一些自定义属性而弄乱表单字段。但是您应该子类化并将这些添加到小部件中。

来自 文档:customizing-widget-instances

您可以将 attrs 字典提交到表单小部件,该字典在输出表单小部件上呈现为属性。

class CommentForm(forms.Form):
    name = forms.CharField(
                widget=forms.TextInput(attrs={'class':'special'}))
    url = forms.URLField()
    comment = forms.CharField(
               widget=forms.TextInput(attrs={'size':'40'}))
Django will then include the extra attributes in the rendered output:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>

You should not mess with form fields for adding some custom attributes to the rendered html tag. But you should subclass and add a these to the Widget.

From the docs: customizing-widget-instances

You can submit attrs dictionary to the form Widgets, that render as attributes on the output form widgets.

class CommentForm(forms.Form):
    name = forms.CharField(
                widget=forms.TextInput(attrs={'class':'special'}))
    url = forms.URLField()
    comment = forms.CharField(
               widget=forms.TextInput(attrs={'size':'40'}))
Django will then include the extra attributes in the rendered output:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
怪我闹别瞎闹 2024-08-21 07:43:49

http://code.djangoproject.com/browser /django/trunk/django/newforms/widgets.py?rev=7083

正如在 类 Select(Widget): 下所见,无法添加 样式< /strong> 属性到 option 标记。为此,您必须对该小部件进行子类化并添加此类功能。

class Select(Widget): 定义仅将 style 属性添加到主 select 标记中。

http://code.djangoproject.com/browser/django/trunk/django/newforms/widgets.py?rev=7083

As seen under the class Select(Widget):, there is no way to add the style attribute to an option tag. To this, you will have to subclass this widget and add such functionality.

The class Select(Widget): definition only adds style attribute to the main select tag.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文