Using Django FormPreview in the right direction
My goal
I have a django project with a form and I want to display a preview page before the user submits.
Problem
I can display the preview page using Django FormPreview , but not all form data is displayed correctly. In particular, if I have a field with choices
, the string values ββof those options are not displayed. I am also having problems with pattern filters on date fields. The end result is that some data is visible on the preview page, but other data is empty:
However, if I show the same data for messages that were actually sent, then everything is displayed correctly:
My code
models.py
class Game(models.Model):
# Game Choices
FOOTBALL = 0
BASKETBALL = 1
TENNIS = 2
OTHER = 3
GAME_CHOICES = (
(FOOTBALL, 'Football'),
(BASKETBALL, 'Basketball'),
(TENNIS, 'Tennis'),
(OTHER, 'Other')
)
game_id = models.AutoField(primary_key=True)
location = models.CharField(max_length=200, verbose_name="Location")
game = models.IntegerField(choices=GAME_CHOICES, default=FOOTBALL)
game_date = models.DateField(verbose_name='Game Date')
forms.py
class GameForm(ModelForm):
class Meta:
model = Game
fields = (
'location',
'game',
'game_date'
)
I'm sure the problem is in my view.py: I'm not sure if I'm handling the request POST
correctly to pass all the data to the preview page.
views.py
def form_upload(request):
if request.method == 'GET':
form = GameForm()
else:
# A POST request: Handle Form Upload
form = GameForm(request.POST) # Bind data from request.POST into a GameForm
# If data is valid, proceeds to create a new game and redirect the user
if form.is_valid():
game = form.save()
return render(request, 'games/success.html', {})
return render(request, 'games/form_upload.html', {
'form': form,
})
preview.py
class GameFormPreview(FormPreview):
form_template = 'games/form_upload.html'
preview_template = 'games/preview.html'
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
return HttpResponseRedirect('/games/success')
form_upload.html
...
<form method="post">
{% csrf_token %}
<ul><li>{{ form.as_p }}</li></ul>
<button type="submit">Preview your post</button>
</form>
...
preview.html
{% load humanize %}
...
<h1>Preview your submission</h1>
<div>
<p>Location: {{ form.data.location }}</p>
<p>Game Date: {{ form.data.game_date|date:"l, F d, Y" }}</p>
<p>Game Type: {{ form.data.get_game_display }}</p>
</div>
<div>
<form action="{% url 'form_upload' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.as_hidden }}
{% endfor %}
<input type="hidden" name="{{ stage_field }}" value="2" />
<input type="hidden" name="{{ hash_field }}" value="{{ hash_value }}" />
<!-- Submit button -->
<button type="submit">Submit your post</button>
<!-- Go back button -->
<button type="submit">
<a href="{% url 'form_upload' %}"
onClick="history.go(-1);return false;" >
Go back and edit your post
</a>
</button>
</div>
</form>
</div>
...
Two problems
Essentially, I am having the following two problems:
- String values ββfor are
choices
not displayed. If I use get_FOO_display () method in my templatepreview.html
it will be empty. However, if I use this on the page after posting the post, it displays correctly. - Date filter
humanize
doesn't work. If I apply filterhumanize
({{ form.data.game_date|date:"l, F d, Y" }}
) inpreview.html
, it also displays empty. Again, this works for the submitted posts.
My question is basically this: what's the correct way to use FormPreview
here?
source to share
form.data
has no attributes get_FOO_display
. When you access {{ form.data.get_game_display }}
in the template it fails and displays nothing.
get_FOO_display
are instance methods, so try that instead.
{{ form.instance.get_game_display }}
Whenever possible, you should access the data from form.cleaned_data
(which is validated and "cleared") instead of form.data
that raw data submitted to the form.
Filters don't work with form.data.game_date
because it's a raw string. They should work with form.cleaned_data.game_date
which has been converted to python date object.
Finally, you haven't implemented anything in your method done
, you just copied the comment from the docs. You can create a new game using the cleaned_data
following:
def done(self, request, cleaned_data):
game = Game.objects.create(**cleaned_data)
return HttpResponseRedirect('/games/success')
source to share