learyjk
learyjk

Reputation: 739

Creating Django Forms from Querysets

I am working on a Django form that let's you order customs pizzas. When building the form to "Select Size" for instance, I want to pull from a database that has my menu item's available sizes in it, so that if the menu changes, the form will automatically know what sizes are available.

Right now I am having an issue with how the query set is returned as it shows up.

How should I alter the code such that

queryset=query_list.values_list('size').distinct()

shows up in the drop down as: Small, Medium

and not: (Small,), (Medium,)

Or is there maybe a way to pull directly from the SIZE_CHOICES tuple in the class?

Thank you!

Models.py:

class Pizza(MenuItem):
    STYLE_CHOICES = (
        ('Regular', 'Regular'),
        ('Sicilian', 'Sicilian'),
    )

    SIZE_CHOICES = (
        ('Small', 'Small'),
        ('Large', 'Large'),
    )

    style = models.CharField(max_length=16, blank=False, choices=STYLE_CHOICES)
    size = models.CharField(max_length=16, blank=False, choices=SIZE_CHOICES)
    num_toppings = models.IntegerField()
    toppings = models.ManyToManyField(Topping, blank=True)
    is_special = models.BooleanField(default=False)

    def toppings_list(self):
        return "\n".join([p.name for p in self.toppings.all()])

    def __str__(self):
        return f"{self.style} {self.size}"

forms.py

class PizzaForm(forms.Form):
    query_list = Pizza.objects.all()

    style = forms.ModelChoiceField(
        widget=forms.Select,
        queryset=query_list,
        empty_label="Select Size"
        )
    size_choices = forms.ModelChoiceField(
        widget=forms.Select,
        queryset=query_list.values_list('size').distinct(),
        empty_label="Select Size"
        )
    topping_choices = forms.ModelMultipleChoiceField(
        widget=forms.CheckboxSelectMultiple,
        queryset=Topping.objects.all(),
        required=False
        )

Upvotes: 2

Views: 5121

Answers (2)

Iain Shelvington
Iain Shelvington

Reputation: 32294

Anyone looking to create a choice field for a non-ModelForm where the choices come dynamically from a queryset but are not model instances, you can pass a callable to the choices parameter on a ChoiceField

def _make_choices():
    return [(c, c) for c in queryset.values_list('field', flat=True)]

choice_field = forms.ChoiceField(choices=_make_choices)

A Willem said, a ModelForm is the way to go for this problem.

Upvotes: 2

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477607

This is not a ModelChoiceField [Django-doc], since you do not select a model object, but a value. You thus can use a ChoiceField [Django-doc], and import the choices from the model:

class PizzaForm(forms.Form):
    style = forms.ChoiceField(
        widget=forms.Select,
        choices=Pizza.STYLE_CHOICES,
        empty_label="Select Size"
    )
    size_choices = forms.ChoiceField(
        widget=forms.Select,
        choices=Pizza.SIZE_CHOICES,
        empty_label="Select Size"
    )
    topping_choices = forms.ModelMultipleChoiceField(
        widget=forms.CheckboxSelectMultiple,
        queryset=Topping.objects.all(),
        required=False
    )

That being said, you might want to look into a ModelForm [Django-doc]. This can automatically construct a form based on a model, and furthermore remove a lot of boilerplate code to save/update a Pizza object in the database.

Upvotes: 2

Related Questions