Python – Add data to Django form class using modelformset_factory

djangodjango-formspython

I have a problem where I need to display a lot of forms for detail data for a hierarchical data set. I want to display some relational fields as labels for the forms and I'm struggling with a way to do this in a more robust way. Here is the code…

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

class Item(models.Model):
  category = models.ForeignKey('Category')
  name = models.CharField(max_length=160)
  weight = models.IntegerField(default=0)

  class Meta:
    ordering = ('category','weight','name')

class BudgetValue(models.Model):
  value = models.IntegerField()
  plan = models.ForeignKey('Plan')
  item = models.ForeignKey('Item')

I use the modelformset_factory to create a formset of budgetvalue forms for a particular plan. What I'd like is item name and category name for each BudgetValue. When I iterate through the forms each one will be labeled properly.

class BudgetValueForm(forms.ModelForm):
item = forms.ModelChoiceField(queryset=Item.objects.all(),widget=forms.HiddenInput())
plan = forms.ModelChoiceField(queryset=Plan.objects.all(),widget=forms.HiddenInput())

category = "" < assign dynamically on form creation >
item = "" < assign dynamically on form creation >
class Meta:
    model = BudgetValue
    fields = ('item','plan','value')

What I started out with is just creating a dictionary of budgetvalue.item.category.name, budgetvalue.item.name, and the form for each budget value. This gets passed to the template and I render it as I intended. I'm assuming that the ordering of the forms in the formset and the querset used to genererate the formset keep the budgetvalues in the same order and the dictionary is created correctly. That is the budgetvalue.item.name is associated with the correct form. This scares me and I'm thinking there has to be a better way. Any help would be greatly appreciated.

Best Solution

Well, I figured out the answer to my own question. I've overridden the init class on the form and accessed the instance of the model form. Works exactly as I wanted and it was easy.

class BudgetValueForm(forms.ModelForm):
  item = forms.ModelChoiceField(queryset=Item.objects.all(),widget=forms.HiddenInput())
  plan = forms.ModelChoiceField(queryset=Plan.objects.all(),widget=forms.HiddenInput())

  def __init__(self, *args, **kwargs): 
    super(BudgetValueForm, self).__init__(*args, **kwargs) 
    self.itemname = self.instance.item.name 
    self.categoryname = self.instance.item.category.name 

  class Meta:
    model = BudgetValue
    fields = ('item','plan','value')
Related Question