Home > Enterprise >  Django ModelForm non-required CharField giving errors
Django ModelForm non-required CharField giving errors

Time:02-01

I have a ModelForm for a model that has a couple of files and with every file, a type description (what kind of file it is). This description field on the model has CHOICES. I have set these file uploads and description uploads as hidden fields on my form, and not required. Now the file upload is working, but the description is giving field errors, the placeholder in the dropdown is not a valid choice, it says. That's true, but since it is not required, I would like it to just be left out of the validation and I am stuck on how.

My codes, shortened them up a bit to keep it concise.

models.py

class Dog(models.Model):
    FILE_TYPE_CHOICES = [
      ('SB', 'Stamboom'),
      ('RB', 'Registratiebewijs'),
      ('SJP', 'SJP-diploma'),
      ('CDD', 'CDD-diploma'),
      ('WT', 'Workingtestdiploma'),
      ('MISC', 'Overig'),
    ]
    dog_id = models.UUIDField(unique=True, default=uuid.uuid4)
    file_1 = models.FileField(upload_to=user_directory_path, blank=True, validators=[extension_validator])
    desc_1 = models.CharField(choices=FILE_TYPE_CHOICES, blank=True, max_length=5)

forms.py (I excluded all these fields from the model-code above to keep it more clear, but this is to demonstrate that these fields are not required. If I print the required attribute of 'desc_1' in the view, it also says false

class DogForm(ModelForm):
    class Meta:
        model = Dog
        fields = ('stamboomnummer', 'stamboomnaam', 'date_of_birth', 'breed',
                  'sex', 'sire', 'dam', 'microchip', 'breeder', 'owner',
                  'file_1', 'desc_1')
        required = ('stamboomnummer', 'stamboomnaam', 'date_of_birth', 'breed',
                   'sex', 'sire', 'dam', 'microchip', 'breeder', 'owner')
        widgets = {'file_1': forms.HiddenInput(),
                   'desc_1': forms.HiddenInput(),
                  }

views.py

@login_required
def newdog(request):
    file_opties = Dog.FILE_TYPE_CHOICES
    if request.method == 'POST':
        form = DogForm(request.POST, request.FILES)
        if form.is_valid():
            dog = form.save(commit=False)
            if request.FILES.get('file_1'):
                dog.file_1 = request.FILES['file_1']
            dog.user = request.user
            dog.save()
            assign_perm('accounts.view_dog', request.user, dog)
            assign_perm('accounts.change_dog', request.user, dog)
            assign_perm('accounts.delete_dog', request.user, dog)
            return redirect('accounts:profile')
    else:
        form = DogForm()
    return render(request, 'accounts/add_dog.html', {'form': form,
                                                       'buttontitle': 'Opslaan',
                                                       'formtitle': 'Hond toevoegen',
                                                       'file_type': file_opties})

My form template:

{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block title %}Title{% endblock %}

{% block content %}

  <h2>{{ formtitle }}</h2>
  <form id='hond' method="post" enctype="multipart/form-data">
    {% csrf_token %}
{% for field in form.visible_fields %}
<div >
        {{ field.errors }}
       {{ field.label }}{% if field.field.required %}*{% endif %} <br>{{ field }}
</div>
{% endfor %}
{% if form.instance.file_1 %}
    <a href="{{ form.instance.file_1.url }}">{{ form.instance.get_desc_1_display }}</a><br>
    {% endif %}
      <div >
 <input  type="file" name="file_1" id="id_file_1">
  <select  name="desc_1" id="id_desc_1">
  <option selected>Type bestand</option>
      {% for item in file_type %}
  <option value={{ item.0 }}>{{ item.1 }}</option>
      {% endfor %}
</select>
</div>

     
    <button  type="submit">{{ buttontitle }}</button>
  </form>
    <button  style="margin-top:20px" onclick="history.back()">Annuleren</button>
{% endblock %}

This is my form HTML, with the file input and description input right on the bottom

<form id="hond" method="post" enctype="multipart/form-data">
    <input type="hidden" name="csrfmiddlewaretoken" value="gxmQEruF9vTKQshK1M4YnRRg4r0X1SkzEJwbAOwQcbq1mmGvBRSC89DwmZfT6Sv7">

<div >
        
       Stamboomnummer* <br><input type="text" name="stamboomnummer"  maxlength="20" required="" id="id_stamboomnummer">
</div>

<div >
        
       Stamboomnaam* <br><input type="text" name="stamboomnaam"  maxlength="60" required="" id="id_stamboomnaam">
</div>

<div >
        
       Geboortedatum* <br><input type="text" name="date_of_birth"  required="" id="id_date_of_birth">
</div>

<div >
        
       Ras* <br><select name="breed"  id="id_breed">
  <option value="FR" selected="">Flatcoated Retriever</option>

  <option value="CBR">Chesapeake Bay Retriever</option>

  <option value="CCR">Curly Coated Retriever</option>

  <option value="GR">Golden Retriever</option>

  <option value="LR">Labrador Retriever</option>

  <option value="NSDTR">Nova Scotia Duck Tolling Retriever</option>

  <option value="MISC">Ander ras, geen retriever</option>

</select>
</div>

<div >
        
       Geslacht* <br><select name="sex"  required="" id="id_sex">
  <option value="" selected="">---------</option>

  <option value="REU">Reu</option>

  <option value="TEEF">Teef</option>

</select>
</div>

<div >
        
       Vader* <br><input type="text" name="sire"  maxlength="60" required="" id="id_sire">
</div>

<div >
        
       Moeder* <br><input type="text" name="dam"  maxlength="60" required="" id="id_dam">
</div>

<div >
        
       Chipnummer* <br><input type="text" name="microchip"  maxlength="30" required="" id="id_microchip">
</div>

<div >
        
       Fokker* <br><input type="text" name="breeder"  maxlength="50" required="" id="id_breeder">
</div>

<div >
        
       Eigenaar* <br><input type="text" name="owner"  maxlength="50" required="" id="id_owner">
</div>


      <div >
 <input  type="file" name="file_1" id="id_file_1">
  <select  name="desc_1" id="id_desc_1">
  <option selected="">Type bestand</option>
      
  <option value="SB">Stamboom</option>
      
  <option value="RB">Registratiebewijs</option>
      
  <option value="SJP">SJP-diploma</option>
      
  <option value="CDD">CDD-diploma</option>
      
  <option value="WT">Workingtestdiploma</option>
      
  <option value="MISC">Overig</option>
      
</select>
</div>


    <button  type="submit">Opslaan</button>
  </form>

Now, form.is_valid() is False, and the form errors are:

desc_1: Selecteer een geldige keuze. Type bestand is geen beschikbare keuze.

Which means something like: Select a valid option, Type bestand (which is the placeholder text in my form) is not a valid option.

I have tried to override the clean-function but the field is not in the cleaned_data (which is logical I guess, since it's not a valid option) and I do not know how to get in before. I think I might have to define a custom field, but I cannot seem to find how to do that on a modelform with a hidden input :/ Any help would be greatly appreciated :)

CodePudding user response:

Your form is submitting desc_1, so there's a an input with name="desc_1" that has a populated value of Type bestand somewhere in your template. blank=True means the value can be left empty. Since your field has choices and blank=True, the submitted value can be either empty or one of the FILE_TYPE_CHOICES.

You're saying that Type bestand is the placeholder for this field, but if you rendered this field as a hidden input ({{ form.desc_1 }} since you have a widget overridden) it would not and should not have a placeholder.

A regular form equivalent would be:

<input type="hidden" name="desc_1" value="">

Where the value attribute is either left empty or populated with one of the existing options, nothing else.

I will also add that if you're displaying desc_1 field as a <select> element, the placeholder option's value also has to be empty.

Edit

You have an option element that is selected by default. Add the disabled attribute to stop the form from submitting the placeholder text as a value for desc_1:

<option value="" selected disabled>Type bestand</option>
  •  Tags:  
  • Related