views:

280

answers:

4

I have a form with an email property. When using {{ form.email }} in case of some validation error, django still renders the previous value in the input tag's value attribute:

<input type="text" id="id_email" maxlength="75" class="required" value="[email protected]" name="email">

I want to render the input tag myself (to add some javascript code and an error class in case of an error). For example this is my template instead of {{ form.email }}:

<input type="text" autocomplete="on" id="id_email" name="email" class="email {% if form.email.errors %}error{% endif %}">

However this does not display the errorneous value ("[email protected]" in this example) to the user. How do I get the field's value in the template?

A: 
{{form.fields.email}}
shanyu
This gives: <django.forms.fields.RegexField object at 0x083235B0>
Eran Kampf
You can write a filter to fetch the value.
shanyu
there's no built-in way to get it?
Eran Kampf
None that I know of.
shanyu
+2  A: 

Hi Eran,

I have a simple solution for you!

{{ form.data.email }}

I tried this and it worked. This requires your view to populate the form class with the POST data.

Very simple example:

def your_view(request):
  if request.method == 'POST':
    form = YourForm(request.POST)
    if form.is_valid():
      # some code here
  else:
    form = YourForm()

  return render_to_response('template.html', {'form':form})

Hope that helps you. If you have any questions please let me know.

Jens
The cleaned data will not contain the email field if it couldn't be cleaned during validation.
shanyu
You are right. You won't be able to access cleaned_data if is_valid() fails. I'm looking for another solution.
Jens
I updated my code. There was a simple solution. Instead of cleaned_data simply use data. That worked even if the validation fails!
Jens
data doesn't work for me...{{ form.description }} gives a control with correct value{{ form.data.description }} is empty (in fact, {{ form.data }} returns {})
Eran Kampf
btw, I'm initializing the form by doing: form_details = forms.UserDetailsForm(instance=request.user)
Eran Kampf
+1  A: 

The solution proposed by Jens is correct. However, it turns out that if you initialize your ModelForm with an instance (example below) django will not populate the data:

def your_view(request):   if request.method == 'POST':
    form = UserDetailsForm(request.POST)
    if form.is_valid():
      # some code here   
    else:
      form = UserDetailsForm(instance=request.user)

So, I made my own ModelForm base class that populates the initial data:

from django import forms 
class BaseModelForm(forms.ModelForm):
    """
    Subclass of `forms.ModelForm` that makes sure the initial values
    are present in the form data, so you don't have to send all old values
    for the form to actually validate.
    """
    def merge_from_initial(self):
        filt = lambda v: v not in self.data.keys()
        for field in filter(filt, getattr(self.Meta, 'fields', ())):
            self.data[field] = self.initial.get(field, None)

Then, the simple view example looks like this:

def your_view(request):   if request.method == 'POST':
    form = UserDetailsForm(request.POST)
    if form.is_valid():
      # some code here   
    else:
      form = UserDetailsForm(instance=request.user)
      form.merge_from_initial()
Eran Kampf
Nice work Eran.
Jens
I don't see why django isn't doing this by default... I think I'll submit a patch and see what people say about it.
Eran Kampf
A: 

{{ form.fields.email.initial }} seems to work.

Rich