Search code examples
pythondjangoformsmodels

Django: creating dynamic forms


I have these models and I want to build forms based on the data:

class Location(models.Model):
    name=models.CharField(max_length=255)
    location_id=models.CharField(max_length=255)
    organization=models.CharField(max_length=255)

    def __unicode__(self):
        return self.name

class Beverage(models.Model):
    name=models.CharField(max_length=255)
    location=models.ForeignKey(Location)
    fill_to_standard=models.IntegerField(max_length=10)
    order_when_below=models.IntegerField(max_length=10)

    def __unicode__(self):
        return self.name

class Inventory(models.Model):
    location=models.ForeignKey(Location)
    beverage=models.ForeignKey(Beverage)
    units_reported=models.IntegerField(max_length=10)
    timestamp=models.DateTimeField(auto_now=True)

Here is what I want to happen, when a user goes to update inventory for a particular location, I want them to get a form that lists out all the possible beverages for that location (the beverages differ per location) and then create an Inventory form for it that will create a new line in the Inventory table for each Beverage for that Location. Each one needs a timestamp so we can have a history. I think I get that I need formsets, but I have not had any success figuring out how to implement them for this. So, this is kind of a two-parter:

  1. Is my model design right for this problem?
  2. How can I make forms that depend on the number of beverages to build themselves?

Solution

  • Question 1: model design - I would drop the location field from the Inventory model, since it is already in the Beverage model, and is redundant. Otherwise, they look good to me.

    Question 2: formsets...

    forms.py

    from django import forms
    from my_project.my_app.models import Beverage
    
    class InventoryForm(forms.ModelForm):
        units_reported = forms.IntegerField()
        class Meta:
            model = Beverage
            fields = ('name', 'id')
    

    views.py

    from django.forms.models import modelformset_factory
    from my_project.my_app.models import Beverage, Inventory
    
    def update_inventory(request, location_id):
    
        InventoryFormSet = modelformset_factory(Beverage, form=InventoryForm)
        qs = Beverage.objects.filter(location=location_id)
        formset = InventoryFormSet(queryset=qs)
        if request.method == 'POST':
            formset = InventoryFormSet(request.POST)
            if formset.is_valid():
                for form in formset:
                    beverage = form.save(commit=False)
                    units_reported = form.cleaned_data['units_reported']
                    Inventory(beverage=beverage, units_reported=units_reported).save()
        ...
    

    modelformset_factory will create a form for every object (Beverage) in the queryset which is filtered by the desired location, and has an extra field in the customized ModelForm to report units.