Django form with inputs populated from database

1.7k Views Asked by At

I have a simple Django model structure with Products and Orders, linked by an intermediary table that determines the quantity of each product within a given order:

models.py:

class Product(models.Model):
    name = models.CharField()

class Order(models.Model):
    id = models.IntegerField()
    products = models.ManyToManyField(Product, through='OrderProduct')

class OrderProduct(models.Model):
    order=models.ForeignKey(Order, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveSmallIntegerField()

I'd like to design a form in my forms.py file that 1) pulls a list of every product in the database; 2) gives the user the option to adjust the quantity of each product ordered (e.g., in a CharField input), and 3) creates new Order and OrderProduct objects that store a record of the quantity of each Product ordered by the user.

However, I can't figure out a good way to create a form that will automatically pull all Products from the database and add fields to represent the quantity ordered of the given Product. Ideally the result would look something like this, but coded automatically:

forms.py:

class OrderForm(forms.Form):
    product_1_quantity = forms.IntegerField()
    product_2_quantity = forms.IntegerField()
    product_3_quantity = forms.IntegerField()

....etc., for each product. What's the best way to do this? Does it involve a 'for' loop that creates a new field for each product found in the database?

3

There are 3 best solutions below

2
devdob On BEST ANSWER

I think Django Formsets might be useful for you. You can create sets of the same form over and over depending based on number you decide, hence your products. Check it out here . You can do stuff such as in the below example.

class ArticleForm(forms.Form):
   title = forms.CharField()

from django.forms import formset_factory
ArticleFormSet = formset_factory(ArticleForm, extra=2)

You can also pre-populate it with data such as,

formset = ArticleFormSet(initial=[
      {'title': 'Django is now open source'}
   ])

Hope this helps!

1
Mauricio Cortazar On

You can use ModelChoiceField to show all the products without do a for loop:

class OrderForm(forms.Form):
    product = forms.ModelChoiceField(queryset=Product.objects.all()) # with queryset you can show a checkbox with the query
    quantify = forms.IntegerField()

Now your views should take care of save your data. If for example you need a button "Add new" you can use ajax otherwise you can use formset

2
Sam On

Elaborating on the answer below from user3140312 — Django's formsets provided the solution!

models.py:

class Product(models.Model):
    name = models.CharField()

class Order(models.Model):
    id = models.IntegerField()
    products = models.ManyToManyField(Product, through='OrderProduct')

class OrderProduct(models.Model):
    order=models.ForeignKey(Order, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    quantity = models.PositiveSmallIntegerField()

forms.py:

class ProductForm(forms.Form):
    product_id = forms.IntegerField(widget=forms.HiddenInput)
    quantity = forms.IntegerField()

views.py:

from .forms import ProductForm
from django.forms import formset_factory
from .models import Product

# Show a form asking for the user's product quantities
def product_form(request):

    # If this form is being processed...
    if request.method == 'POST':

        ProductFormSet = formset_factory(ProductForm)

        # Grab the form from the POST variable
        formset = ProductFormSet(request.POST)

        # If the form is valid...
        if formset.is_valid():

            # Do whatever you want with the results

    # If the form is not being processed, create a new ProductFormSet and render it
    else:

        product_list = []

        for product in Product.objects.all():
            product_list.append({'product_id': product.id, 'quantity': 0})

        ProductFormSet = formset_factory(ProductForm, extra=0)
        formset = ProductFormSet(initial=product_list)
        return render(request, 'template.html', {'formset': formset})

template.html:

<form action="{% url '' %}" method="post">
    {% csrf_token %}
    {{ formset.as_table }}
    {{ formset.management_form }}
    <input type="submit" value="Submit" />
</form>