Django模型表单中如何显示额外字段的值

3

当在管理界面中显示一个(模型)表单时,如何设置与表单相关但不直接基于同一模型的字段的值?以下是我所拥有的简化版本(实际应用/模型等更为复杂):

  • building 拥有许多 room
  • room 拥有许多 equipment

模型:

#spaces/models.py
from django.db import models    

class Building(models.Model):
    name=models.CharField(max_length=32)
    def __unicode__(self):
      return self.name

class Room(models.Model):
    number=models.CharField(max_length=8)
    building=models.ForeignKey(Building)
    def __unicode__(self):
        return self.number

class Equipment(models.Model):
    tech = models.CharField(max_length=64)
    room = models.ForeignKey(Room)
    def __unicode__(self):
     return self.tech

在添加或编辑设备时,如果能够通过建筑物来限制选择的“房间”选项,将会很方便。 我可以这样做:我向ModelForm添加一个“建筑物”字段,然后当用户更改它时,模板文件中的一些JQuery代码动态更新“房间”的列表。 “admin.py”部分如下:

#spaces/admin.py
from demo.spaces.models import Building, Room, Equipment
from django.contrib import admin
from django import forms


class EquipmentForm(forms.ModelForm):
     class Meta:
      model = Equipment
     building = forms.ModelChoiceField(Building.objects)

class EquipmentAdmin(admin.ModelAdmin):
     form = EquipmentForm

admin.site.register(Equipment, EquipmentAdmin)
admin.site.register(Building)
admin.site.register(Room)

当添加新的设备时,这一切都很顺利——我的JQuery代码显示-----,直到选择建筑物,此时room字段的下拉菜单将启用并填充适用于该建筑物的相应rooms。 问题在于:当显示现有的设备(demo.com/admin/spaces/equipment/12/)或者添加表单未通过验证时,Room字段会正确显示room,但是building字段被清空了。虽然equipment.room外键值存在于表单实例中,管理员会自动处理设置room字段的值,但是我需要自己设置building字段的值。
我可以轻松地获取这个值,因为在表单的init中检查它是否为实例,然后向上遍历以获取该值非常容易。
def __init__(self, *args, **kwargs):  
     super(EquipmentForm, self).__init__(*args, **kwargs)  
     if 'instance' in kwargs:
        building_value = kwargs['instance'].room.building

但是我该如何使用这个值来使字段显示它呢?我尝试的唯一一件事就是在我的模板中添加更多的代码,使用Jquery获取该值并在最后设置它,但我不想让JavaScript在页面上访问数据库来显示该值,因为这似乎应该在渲染表单时完成...

有什么想法吗?感谢您的帮助!

4个回答

1

检查表单中Fieldinitial参数here。也许将初始设置为Building对象会有所帮助。


此外,您真的需要将选择限制在一个建筑物内吗?我只需修改Room__unicode__方法即可,这样它就可以显示BuildingRoom编号。
def __unicode__(self):
    return "Room nr %s in %s" % (self.number, self.building)

然后你会得到一个所有房间的列表,但很容易找出它们属于哪栋建筑...


我更希望能够限制建筑物的选择,因为A)将有几百个房间,B)我想在其他地方使用相同的过程来限制其他外键。 - mightyhal
field.initial无法帮助,因为我想为建筑设置的值是动态的(取决于实例)。Form.initial看起来很有前途,在__init__中设置self.initial ['building'],并且打印self.initial会返回所有字段及其值的字典...但是当它呈现时,它不会设置建筑物的值(尽管它看起来应该这样做)。 - mightyhal

1
这个应该可以工作:

class EquipmentForm(ModelForm):
    def __init__(self, *args, **kwargs):
        kwargs['initial']=kwargs.get('initial',{})
        if kwargs['instance']:
            kwargs['initial']['building']=kwargs['instance'].building
        super(EquipmentForm, self).__init__(*args, **kwargs)  

在 views.py 文件中:
instance.building="some_building"
form = EquipmentForm(request.POST, instance=instance)

0

这个怎么样?

def __init__(self, *args, **kwargs):  
 super(EquipmentForm, self).__init__(*args, **kwargs)  
 if 'instance' in kwargs:
    self.data['building']=kwargs['instance'].room.building

在__init__中修改self.data['building']没有效果。 (在__init__中删除print self.data会给我一个{},所以也许我需要覆盖除__init__之外的其他东西来影响self.data,或者self.data不是正确的要修改的东西) - mightyhal

0
这对我有效:
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList, label_suffix=':',
                 empty_permitted=False, instance=None):
        if instance:
            initial = initial or {}
            initial['field_name'] = instance.some_value

        super(MyCustomForm, self).__init__(data, files, auto_id, prefix,initial,
                error_class, label_suffix,empty_permitted, instance)

我故意不使用 *args, **kwargs,因为我没有检查这个构造函数在代码中的其他位置是如何调用的(实例和初始化可能在 *args 或 **kwargs 中)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接