How do I perform a Django form validation for an unknown number of fields specified by the user? In my case, form allows a user to create a music album with any number of tracks and associate it to an artist. The view asks for how many tracks there are and then generates a form with that many input fields.
Form:
class NumberOfTracks(forms.Form):
track_no = forms.IntegerField()
class CustomAlbumAdmin(forms.Form):
artist = forms.CharField(max_length=150)
album = forms.CharField(max_length=150)
track_no = forms.IntegerField()
track = forms.CharField(max_length=150)
View:
def album_admin(request):
if request.GET.get('track_no'):
number_of_tracks = request.GET.get('track_no')
artists = Artist.objects.all()
return render(request, 'customadmin/album_admin1.html', {
'number_of_tracks': number_of_tracks,
'tracks': range(1, int(number_of_tracks) + 1),
'artists': artists,
})
elif request.method == 'POST':
form = CustomAlbumAdmin(request.POST)
print form
artist = request.POST['artist']
album = request.POST['album']
all_tracks = request.POST.getlist('track')
create_album = CreateAlbum(artist=artist, album=album, tracks=all_tracks)
create_album.save_album()
create_album.save_tracks()
form = NumberOfTracks()
return render(request, 'customadmin/no_tracks1.html', {
'form': form,
})
else:
form = NumberOfTracks()
return render(request, 'customadmin/no_tracks1.html', {
'form': form,
})
(Just so it's clear, I used if form.is_valid()
and form.cleaned_data
but to get this to work thus far, I've had to bypass that in favor of getting the raw POST data)
Part of what's confusing me is that I've customized my form template to add a number input fields with name="track" depending on user input (EX: create an album with 13 tracks). When I go into my view to print form = CustomAlbumAdmin(request.POST)
it gives a very simple table based on my form: one artist, one album, one track, and one track_no so validating against this will of course return False
unless I have an album with just one track.
Here's the template:
{% extends 'base.html' %}
{% block content %}
<form action="/customadmin/album1/" method="POST">{% csrf_token %}
<select name="artist">
{% for entry in artists %}
<option value="{{ entry.name }}">{{ entry.name }}</option>
{% endfor %}
</select>
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.album.errors }}
<label for="id_album">Album:</label>
<input id="id_album" maxlength="150" name="album" type="text" />
</div>
<div class="fieldWrapper">
{{ form.track.errors }}
<input type="hidden" name="number_of_tracks" value="{{ number_of_tracks }}">
{% for e in tracks %}
<label for="id_track">Track No. {{ forloop.counter }}</label>
<input id="id_track_{{ forloop.counter }}" maxlength="150" name="track" type="text" /></br>
{% endfor %}
</div>
<p><input type="submit" value="Save album" /></p>
</form>
{% endblock %}
The one way I was thinking of approaching this was to create a custom clean_track method that takes a list of all the tracks entered as I've done in the view with all_tracks = request.POST.getlist('track')
but not sure how to do that.
A related question I have is if I can customize validation based on POST data. The first way I approached this was to generate incremented inputs with name="track_1", name="track_2", etc.,. and then trying to validate based on that. However, I wouldn't be able to use request.POST.getlist('track')
in that case.