Django Regex Validator Not Working - Error 500 Deployment Server

Viewed 225

Take the following example:

class ProjectForm(forms.Form):
    client = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Client Name (lowercase)'}),validators=[RegexValidator(r'^[a-z]+$', 'Enter a valid client name (only lowercase letters)')])
    stage = forms.ChoiceField(choices=TYPES)
    folder = forms.ChoiceField(choices=FOLDERS, required=False, label='')
    purpose = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Project Purpose (lowercase; digit optional)'}),validators=[RegexValidator(r'^[a-z-]+[1-9]?$', 'Enter a valid purpose (only lowercase letters, with a single optional digit)')])
    computeapi = forms.BooleanField(required=True, initial=True, label='Compute Engine')
    deploymentmanapi = forms.BooleanField(required=False, label='Deployment Manager')
    storagecompapi = forms.BooleanField(required=False, label='Storage Components')
    monitorapi = forms.BooleanField(required=False, label='Monitoring')
    loggingapi = forms.BooleanField(required=False, label='Logging')

    def clean(self):
        form_data = self.cleaned_data
        projectname = form_data['client'] + "-" + form_data['stage'] + "-" + form_data['purpose']
        client = form_data['client']
        purpose = form_data['purpose']
        for projectdict in projectdicts:
            if projectname == projectdict['name']:
                raise ValidationError(projectname + " already exists! Please try another name.")
        
        if not re.match("^[a-z-]+[1-9]?$", purpose):
            raise ValidationError(purpose + " does not comply with the Regex parameters.")
        
        if not re.match("^[a-z]+$", client):
            raise ValidationError(client + " does not comply with the Regex parameters.")
        return form_data

The ValidationError for projectname works perfectly fine as expected. However, it's not working for Regex, and instead keeps throwing a 500 error. Is there something we need to do specifically to get the RegexValidator to work as expected within the class variables? I don't believe my additional code in the clean is needed (and if it is, it's not working as it should).

Any help with this would be appreciated.

Edit 28/08/2020: Traceback, as requested:

Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\user\OneDrive - Company\GCP App Engine\Test-Django-Terraform-Deployments - Copy\webscripts\views.py", line 30, in projectcreation
    if form.is_valid():
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\forms\forms.py", line 185, in is_valid
    return self.is_bound and not self.errors
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\forms\forms.py", line 180, in errors
    self.full_clean()
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\forms\forms.py", line 382, in full_clean
    self._clean_form()
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\forms\forms.py", line 409, in _clean_form
    cleaned_data = self.clean()
  File "C:\Users\user\OneDrive - Company\GCP App Engine\Test-Django-Terraform-Deployments - Copy\webscripts\projectform.py", line 90, in clean
    projectname = form_data['client'] + "-" + form_data['stage'] + "-" + form_data['purpose']
KeyError: 'client'

The projectdicts dictionary is a global variable generated from a GET request to Google Cloud to generate a list of names of all projects:

try:
    global projectdicts
    projectdicts = cloudresmanv1.projects().list().execute()
    projectdicts = projectdicts.get('projects')
except Exception as e:
    logging.error(e)
2 Answers

You are not calling clean of super class so your form.clean_data is empty

def clean(self):
    form_data = super(ProjectForm, self).clean()
    projectname = form_data['client'] + "-" + form_data['stage'] + "-" + form_data['purpose']
    client = form_data['client']
    purpose = form_data['purpose']
    for projectdict in projectdicts:
        if projectname == projectdict['name']:
            raise ValidationError(projectname + " already exists! Please try another name.")
    
    if not re.match("^[a-z-]+[1-9]?$", purpose):
        raise ValidationError(purpose + " does not comply with the Regex parameters.")
    
    if not re.match("^[a-z]+$", client):
        raise ValidationError(client + " does not comply with the Regex parameters.")
    return form_data

Based on the traceback you've provided;

  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\forms\forms.py", line 409, in _clean_form
    cleaned_data = self.clean()
  File "C:\Users\user\OneDrive - Company\GCP App Engine\Test-Django-Terraform-Deployments - Copy\webscripts\projectform.py", line 90, in clean
    projectname = form_data['client'] + "-" + form_data['stage'] + "-" + form_data['purpose']
KeyError: 'client'

The form_data hasn't got a value for client so it's not present in the dictionary. So it's failing when setting projectname and never reaching your regex validators.

You need to be more defensive in your validation to ensure that data exists before trying to use it. By which I mean do something like this;

    def clean(self):
        cleaned_data = super().clean()
        data_invalid = False

        # Setting required attrs on the fields should ensure the browser 
        # enforces validation, but this will double check before you cause a server error

        for field in (
                'client',
                'purpose',
                'stage',
        ):
            if not cleaned_data.get(field):
                self.add_error(field, 'This field is required')
                data_invalid = True

        if data_invalid:
            # Return early because data is missing
            return cleaned_data


        client = cleaned_data.get('client')
        purpose = cleaned_data.get('purpose')
        stage = cleaned_data.get('stage')

        # Once you know the data is there, you can then go on to use it &
        # add your validators
        
        if client and purpose and stage:
            projectname = client + "-" + stage + "-" + purpose
Related