Django display value of extra field in modelform
How do I set the value of a field (which is relevant to form, but not based directly on the same model) on a (model) form when it is displayed in the admin?
Here is a simplified version of what I have (my actual app/model/etc is more complicated):
- A
building
has manyroom
s - A
room
has many pieces ofequipment
Models:
#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
When adding or editing a piece of equipment, it would be nice to be able to limit the room
choices down (by building). This I can do: I add a building
field to the ModelForm, and then as it gets changed by the user, a bit of JQuery in the template file dynamically updates the list of room
s. The admin.py
part looks like:
#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)
When adding a new piece of equipment
, this all works fine--my JQuery code displays -----
until a building is selected, at which point the drop down for the room
field is enabled and populated with the appropriate room
s for that building.
The problem is: when displaying an existing piece of equipment (demo.com/admin/spaces/equipment/12/
) or if the add form doesn't validate,开发者_StackOverflow社区 the Room
field properly displays the room
--but the building
field is wiped out. The equipment.room
foreign key value exists in the form instance and the admin automagically takes care of setting the room
field to that value, but the value of the building
field needs to be set by me.
I can get the value without a problem, since it's easy in the init of the form to check for it being an instance, and then traversing back up to get the value:
def __init__(self, *args, **kwargs):
super(EquipmentForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
building_value = kwargs['instance'].room.building
But what do I do with that value to make the field display it? The only thing I haven't tried yet is adding more code in my template to use Jquery to get the value and set it at the end, but I'd rather not have to have the javascript in the page hit the database to figure display the value, since it seems like something that should be done when the form is rendered...
Any thoughts? Thank you for you help!
Check the initial
parameter of the Field
in forms here. Maybe setting building with initial set to a Building
object would help.
Also, do you really need to limit choices down to a building? I'd just found sufficient to modify the Room
's __unicode__
method, so it shows both Building
and Room
number:
def __unicode__(self):
return "Room nr %s in %s" % (self.number, self.building)
Then you'll get a list of all rooms, but easy to find out to which building they belong to...
This one should work:
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)
In the views.py:
instance.building="some_building"
form = EquipmentForm(request.POST, instance=instance)
What about this?
def __init__(self, *args, **kwargs):
super(EquipmentForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.data['building']=kwargs['instance'].room.building
This works for me:
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)
I intentionally don't use *args, **kwargs cause I didn't check how this constructor is called elswere in the code (instance and initial could be in *args or **kwargs).
精彩评论