简体   繁体   中英

Django: How to verify a form whose fields are generated from a queryset in the forms __init__ method?

I have some trouble coming up with a good solution to validating a form whose fields are set in its init method.


Quickly explaining my models: Every Product is supposed to have Variations, with each variation in turn having multiple VariationOptions.

  • Every Product can have multiple Variations, like 'Color' or 'Material'
  • Variations can have multiple VariationOptions (ManyToMany-Relation), like 'red' or 'concrete'

When rendering the AddToCartForm, every Variation corresponds to a ChoiceField with the VariationOptions being used as choices for said ChoiceField. (see forms.py)


Since every product can have different variations and therefore different fields, the AddToCart Form must be generated dynamically from a queryset containing the products variations (views.py), or at least thats what I'm thinking right now.

My Questions:

  • How can I validate this kind of form?
  • Do you see a better way of achieving the same idea?

I would very much appreciate your help. Thanks in advance!


Here is my simplified code:

MODELS.PY

class Product(models.Model):
    ..

class Variation(models.Model):
    name     =  models.CharField()
    product  =  models.ForeignKey(Product, on_delete=models.CASCADE, related_name='variations')
    options  =  models.ManyToManyField(VariationOption, blank=True)

class VariationOption(models.Model):
    value = CharField()

FORMS.PY

class AddToCart(forms.Form):
    quantity    = forms.PositiveIntegerField()

    # for every variation get all its choices and add new ChoiceField to form
    def __init__(self, variations, *args, **kwargs):
        super(AddToCart, self).__init__(*args, **kwargs)
        for variation in variations: 
            CHOICES = []
            choices_list = list(variation.options.all())
            for choice in choices_list:
                CHOICES.append(tuple((choice.value, choice.value)))
            self.fields[variation.name] = forms.ChoiceField(choices=CHOICES)

VIEWS.PY

def render_product(request):
    ...
    variations = list(Variation.objects.prefetch_related('options').filter(product=product))
    add_to_cart_form = AddToCartForm(variations=variations)
    return render(...)

@require_POST
def add_to_cart_view(request):
    form = AddToCartForm(request.POST) # This isn't working. I see why, but know no other way.
    if form.is_valid():
        ....

UPDATE

In case anyone comes across this in the future and has the same question: Apparently you can instantiate a form and pass it POST data in the same step.

With a form like this:

FORMS.PY

class SomeForm(forms.Form):
    field    = forms.PositiveIntegerField() #optional

    def __init__(self, variable, *args, **kwargs):
        super(AddToCart, self).__init__(*args, **kwargs)
        # do something with variable, generate fields or choices etc etc

This will work:

form = SomeForm(variable=something, data=request.POST)
if form.is_valid():
    # This will work when POST is actually valid

The form will pass the variable to its __init__method, does whatever logic is implemented and is then bound to the POST data. Validation now works as you would expect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM