【问题标题】:Django RadioSelect widget, add additional informationDjango RadioSelect 小部件,添加附加信息
【发布时间】:2020-12-02 03:20:39
【问题描述】:

我有这个简化的模型和形式:

class Books(models.Model):
    name = models.CharField(max_length=500)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    default = models.BooleanField(default=False)

    def __str__(self):
        return self.name



class BookForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields['Field'] = forms.ModelChoiceField(queryset=None, empty_label=None, widget=forms.RadioSelect)
        self.fields['Field'].queryset = Books.objects.all()
        self.fields['Field'].initial = Books.objects.filter(default=True).first()

这将产生一个 RadioSelect-Form,如下所示:

(x) Book1
( ) Book2
( ) Book3

我的问题是,如何在 RadioSelect 表单中添加价格,它只是可见的。 它应该出现在书名之后,最好是使用不同的字体,我在引导类(例如“text-primary”)上设置(这不是强制性的)

(x) Book1 (10 €)
( ) Book2 (20 €)
( ) Book3 (30 €)

我知道我可以返回模型中的名称和价格,比如

class Books(models.Model):
    name = models.CharField(max_length=500)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    default = models.BooleanField(default=False)

    def __str__(self):
        return '%s (%s €)' % (self.value, str(self.price))

但由于其他原因,我不能这样做。我只需要返回名称。还有其他方法可以做到这一点吗?

我什至阅读了 django-crispy-forms,但找不到解决方案。

【问题讨论】:

  • 如果你不能连接标签,我相信信息不在你的模型里面或更接近的东西......所以我相信挑战仍然存在于表单实例......它可以使用 ajax 吗?一个从端点(URL)加载额外数据并在客户端连接它的javascript?
  • 感谢您的想法,但我对 ajax/javascript 不太熟悉。也许我必须在稍后连接标签并去掉价格,这在 imo 中有点 hacky solultion。
  • 额外信息在哪里?以某种方式在您的数据库中,或者您必须从另一个站点(或外部服务)获取它?

标签: django django-forms django-crispy-forms


【解决方案1】:

您可以使用.label_from_instance

来自documentation

将调用模型的__str__() 方法来生成对象的字符串表示形式,以用于字段的选择。要提供自定义表示,请继承 ModelChoiceField 并覆盖 label_from_instance

您可以定义一个函数,为您提供所需的表示,然后在您的字段上设置.label_from_instance

您的BookForm 如下所示:

class BookForm(forms.Form):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields['Field'] = forms.ModelChoiceField(queryset=None, empty_label=None, widget=forms.RadioSelect)
        self.fields['Field'].queryset = Books.objects.all()
        self.fields['Field'].initial = Books.objects.filter(default=True).first()
        # customize how your option is rendered in the template
        self.fields["Field"].label_from_instance = lambda item: f"{item} ({item.price} €)"

要在标签上应用 CSS,请添加 HTML。除了使用style='...',您还可以使用类,以便它与 Bootstrap 一起使用。

self.fields["Field"].label_from_instance = lambda item: f"{item} <span style='color:red;'>({item.price} €)</span>"

对于 3.7 之前的 Python 版本:

self.fields["Field"].label_from_instance = lambda item: str(item) + "<span style='color:red;'>(" + str(item.price) + " €)</span>"

然后像这样在模板中呈现您的表单:

<form action="{% url 'books' %}" method="post">
    {% csrf_token %}
    {% for hidden_field in form.hidden_fields %}
        {{ hidden_field }}
    {% endfor %}
    <ul>
    {% for choice in form.Field %}
        <li>{{ choice.choice_label|safe }}</li>
    {% endfor %}
    </ul>
    <input class="btn btn-dark" type="submit" value="Submit">
</form>

遍历您的领域的选择,然后您可以通过以下方式获得自定义标签:

{{ choice.choice_label|safe }}

safe 过滤器是必需的,这样您的 HTML 就不会被转义。

【讨论】:

  • 非常感谢!您能否详细说明一下这个 lambda 函数以及 item 的来源?
  • lambda 函数在模板被渲染时获取Book 实例,以获取标签。您也可以使用带有一个参数的普通函数而不是 lambda,它只需要是可调用的,然后返回您希望标签在呈现模板中的样子。
  • 一点说明,在 Python 3.6 之前并没有引入 Format ("f") 字符串。您推荐future-fstrings 还是有其他方法可以为python 3.5 编写它?
  • 我已经添加了一种不使用 f-strings 的方法,您也可以使用 .format() 来代替,但我发现 f-strings 比其他选择要好得多。
  • 这也适用于 python 3.5 及之前版本:lambda obj: "%s + (+ %s €)" % (obj, obj.price)
猜你喜欢
  • 2011-02-18
  • 1970-01-01
  • 2011-11-29
  • 2022-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-12
相关资源
最近更新 更多