首页 文章

Django:从模板中的表单小部件访问模型字段

提问于
浏览
2

我有一个ModelForm,它使用CheckboxSelectMultiple小部件来表示来自ManyToManyField的数据 . 默认情况下,这将呈现一个复选框列表,其标签设置为相关模型的 __str__ 方法的返回值 . 但是,除了窗口小部件提供的choice_label之外,我还希望显示相关模型中其他字段的数据 . 例如,考虑以下实现:

models.py

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey('Category', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

class Order(models.Model):
    items = models.ManyToManyField('Item')

forms.py

from django import forms
from .models import Order

class OrderForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ['items']
        widgets = {'items': forms.widgets.CheckboxSelectMultiple}

template.html

{% for item in form.items %}
    <tr>
        <td>{{ item.tag }}</td>
        <td>{{ item.choice_label }}</td>
        <td>{{ item.category }}</td>
    </tr>
{% endfor %}

这里的问题是template.html的 {{ item.category }} 部分 . 使用 {% for item in form.items %} 循环遍历所有项目复选框时, item 的类型为 BoundWidget ,而不是 Item . 这就是提供 tagchoice_label 属性以呈现表单的复选框 . 我的问题是 BoundWidget 似乎没有任何关于复选框所代表的 Item 实例的引用,这将允许我访问任何其他模型数据,例如项目所在的类别 . 有什么方法可以解决这个问题吗?

编辑:

我能想到的唯一方法是:

templatetags / app_tags.py

from django import template
from models import Item

register = template.Library()

@register.inclusion_tag('app/order_item_table.html')
def show_item_table(items):
    item_objs = []
    for item in items:
        id = item.data['value']
        obj = Item.objects.get(id=id)
        item_objs.append((item, obj))
    return {'items': item_objs}

order_item_template.html

{% for item, obj in items %}
    <tr>
        <td>{{ item.tag }}</td>
        <td>{{ item.choice_label }}</td>
        <td>{{ obj.category }}</td>
    </tr>
{% endfor %}

template.html

{% load app_tags %}

{% show_item_table form.items %}

但这看起来令人难以置信的hacky,我真的想知道是否有更好的方法来做到这一点?

1 回答

  • 0

    我发现了一个不同的解决方案(我必须重新实现一个小部件供我自己使用)

    我也改变了撕裂,在你的情况下 get_context ovverridign可能就足够了)

    class MyWidget(forms.widgets.CheckboxSelectMultiple):
        template_name = 'web_admin/partial/checkbox.html'
        qs = None
    
        def get_context(self, name, value, attrs):
            ctx = super().get_context(name, value, attrs)
            ctx.update(dict(qs=self.qs))
            return ctx
    

    注意 get_context 如何使用 self.qs 更新上下文

    现在, form

    class MyForm(ModelForm):
        class Meta:
            model = MyModel
            fields = ['my_field']
            widgets = {
                'my_field': MyWidget,
    
            }
    
        def __init__(self, qs, *args, **kwargs):
            # this is set in get_form_kwargs of the view
            super().__init__(*args, **kwargs)
            self.fields['my_field'].queryset = qs
            self.fields['data_controller'].widget.qs = qs
    

    self.fields['my_field'].queryset = qs 我需要它来过滤可能的选项,你需要的是 self.fields['data_controller'].widget.qs = qs

    在视图中(我使用基于类的视图)

    class ConsentCreationDC(CreateView):
        template_name = "web_admin/consent_creation_new.html"
        form_class = MyForm
    
        def get_form_kwargs(self):
            ctx = super().get_form_kwargs()
            ctx.update({'qs': THE_QUERY_SET})
            return ctx
    

    现在在窗口小部件模板(html文件)中,您可以访问 qs 对象

    我做过滤器的地方 {% with obj_item=qs|index:widget.index %}

    @register.filter def index(List, i): return List[int(i)]

    在你的情况下,它应该 {% with obj_item=qs|index:item.index %} (不确定项目是否有 index

    希望它的帮助

相关问题