'django dynamic forms setting max_length property

I am creating a 'Forms Management' system for my application.

I am creating a forms dynamically using a custom form 'factory' method.

Form data is in a json file.

I can create a forms.CharField and set the label, required, initial and help_text properties.

When I try to set the max_length property I do not get any error message, but the resulting HTML does not contain the max_length attribute.

In static (class) forms defined as

class SearchAccountForm(forms.Form):
    provider_code = forms.CharField(
        label='Provider:', 
        max_length=100, 
        required=True, 
        widget=forms.TextInput(attrs={'class': 'form-control'}))

The resulting HTML contains the max_length attribute.

<label for="id_provider_code">Provider:</label>
</th><td><input type="text" name="provider_code" class="form-control" maxlength="100" required id="id_provider_code">

So what's up with max_length??

Json file

{
    "form1": [
        {
            "fld_name": "customer_name",
            "fld_type": "CharField",
            "fld_label": "Cust Name",
            "fld_required": "False",
            "fld_maxLength": 5,
            "initial": "Dr John"
        },
        {
            "fld_name": "customer_number",
            "fld_type": "CharField",
            "fld_label": "Cust #",
            "fld_required": "True",
            "fld_maxLength": 15,
            "help_text": "Enter account number"
        },
        {
            "fld_name": "customer_type",
            "fld_type": "CharField",
            "fld_label": "Customer Type",
            "fld_required": "False"
        }
    ]
}

and the forms.py factory method

from django import forms
import json

def dynfrm():
    f = open('blog/frmJson/frm1.json')

    data = json.load(f)
    fields = {}
    for i in data['form1']:     ## form1 = form name in json file
        print(i)
        ## add to fields list
        if i['fld_type'] == 'CharField':
            fields[i["fld_name"]] = forms.CharField()
            if 'fld_label' in i:
                fields[i["fld_name"]].label = i["fld_label"]
            if 'fld_required' in i:
                if i["fld_required"] == 'False':
                    fields[i["fld_name"]].required = False
                else:
                    fields[i["fld_name"]].required = True
            if 'initial' in i: fields[i["fld_name"]].initial = i["initial"]
            if 'help_text' in i: fields[i["fld_name"]].help_text = i["help_text"]
## next line not working
            if 'fld_maxLength' in i: fields[i["fld_name"]].max_length = i["fld_maxLength"]

            fields[i["fld_name"]].widget = forms.TextInput()

    return type('DynForm',  # form name is irrelevant
                (forms.BaseForm,),
                {'base_fields': fields})    

my view

def vdynfrm(request):
    if request.method == 'POST':
        form = dynfrm(request.POST)
        if form.is_valid():
            pass
            ## all good
    else:
        form = dynfrm()

    ##return render(request, "blog/dfrm.html",{'form': form})
    return render(request, "blog/searchAccount.html",{'form': form})

and the resulting HTML

<div class="form-group">
   <form action="/searchAccount/" method="post">
      <table>
         <tr>
            <th><label for="id_customer_name">Cust Name:</label></th>
            <td><input type="text" name="customer_name" value="Dr John" id="id_customer_name">/td> 
         </tr>
         <tr>
            <th><label for="id_customer_number">Cust #:</label></th>
            <td><input type="text" name="customer_number" required id="id_customer_number"><br> 
                <span class="helptext">Enter account number</span></td>
            </tr>
            <tr>
               <th><label for="id_customer_type">Customer Type:</label></th>
               <td><input type="text" name="customer_type" id="id_customer_type"></td>
            </tr>    
      </table>
         
      <input type="submit" value="Submit">
   </form>
</div>


Solution 1:[1]

The max_length property only works when you send the context correctly to your template file.

your forms.py

class SearchAccountForm(forms.Form):
    provider_code = forms.CharField(
        label='Provider:',
        max_length=100,
        required=True,
        widget=forms.TextInput(attrs={'class': 'form-control'}))

With function based view:


def home(request):
    if request.method == 'POST':
        form = SearchAccountForm(request.POST)
        if form.is_valid():
            provider_c= form.cleaned_data['provider_code']
            print('Provider Code :',provider_c)
            return HttpResponseRedirect('/thanks/')
    else:
        form = SearchAccountForm()

    return render(request, 'home/index.html', {'form': form})

def thanks(req):
    return render(req, 'home/thanks.html')

If you forget to give else condition for get request method, so you will not receive django's inbuild error messages as well as max_length etc.

With Class based view it can be handled easily:

from django.views.generic.edit import FormView

class Home(FormView):
    template_name = 'home/index.html'
    form_class = SearchAccountForm
    success_url = '/thanks/'

    def form_valid(self, form):
        print(form)
        print('Provider Code : ', form.cleaned_data['provider_code'])
        return super().form_valid(form)

def thanks(req):
    return render(req, 'home/thanks.html')

From both the examples above max_length property is working properly because its get request got handled.

Check your views.py, it may help.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sunderam Dubey