Skip to main content
 首页 » 编程设计

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

2025年01月19日38lyj

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

模型.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') 

表单.py

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

模板.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 。这就是为呈现表单的复选框提供 tag 和 choice_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 %} 

模板.html

{% load app_tags %} 
 
{% show_item_table form.items %} 

但这看起来非常 hacky,我真的很想知道是否有更好的方法来做到这一点?

请您参考如下方法:

我找到了一个轻微不同的解决方案(我不得不重新实现一个小部件供我自己使用)

我已经为一个新的小部件创建了一个类(我也必须改变渲染,在你的情况下 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 更新上下文

现在,以形式

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

在 View 中(我使用基于类的 View )

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 索引(列表,我): 返回列表[int(i)]

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

希望对你有帮助