When using inline formsets with form wizards, where does formset form validation go? - inline-formset

I am creating an example to learn more about using inline formsets with SessionWizard. Eventually, I want to integrate dynamic formsets in order to add and delete individual forms via the template before submitting. However, when data is absent from the second form, it fails to validate unlike like a regular ModelForm.
Is there a method within SessionWizard that needs to be overridden? Is it something that is inherently handled within Django?
Guidance and examples would be greatly appreciated.
models.py
class Parent(models.Model):
name = models.CharField(max_length=256)
def __unicode__(self):
return name
class Child(models.Model):
name = models.CharField(max_length=256)
parent = models.ForeignKey(Parent)
def __unicode__(self):
return name
urls.py
test_forms = [['parent', ParentForm],['child', ChildFormSet]]
urlpatterns = patterns('example.views',
url(r'^$', TestWizard.as_view(test_forms)),
)
forms.py
class ParentForm(ModelForm):
class Meta:
model = Parent
class ChildForm(ModelForm):
class Meta:
model = Child
exclude = ('parent',)
ChildFormSet = inlineformset_factory(Parent, Child, extra=1)
class TestWizard(SessionWizardView):
"""
This WizardView is used to create multi-page forms and handles all the
storage and validation stuff using sessions.
"""
#template_name = ''
# def get_template_names(self):
# """
# Returns a list of template names to be used for the request.
# Overridden TemplateResponseMixin for specifying template for step.
# """
# return 'survey/forms/wizard_form.html'
#
# def get_context_data(self, form, **kwargs):
# context = super(TestWizard, self).get_context_data(form=form, **kwargs)
# return context
#
# def get_form_initial(self, step):
# """
# Returns dict (list of key, values) for initial form data.
# Useful for populating form fields with data from prior form, with extra
# logic for dealing with formsets.
# """
# return self.initial_dict.get(step, {})
#
# def get_form(self, step=None, data=None, files=None):
# """
# Constructs the form for a given step - overridden to add extra arguments
# """
# form = super(TestWizard, self).get_form(step, data, files)
# return form
def done(self, form_list, **kwargs):
return render_to_response('survey/thanks.html', {
'form_data': [form.cleaned_data for form in form_list],
})
wizard-form.html
{% extends "base.html" %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<p>DEFAULT WIZARD FORM Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}

Related

A django form containing sub forms not showing validation errors for sub forms

In my page in django application it has multiple forms such that main form has sub forms .
When submitting I check for all the forms to be valid and if any of the form is invalid , it return the form_invalid function containing the main form .
But the problem is if any of the subform is invalid , it doesn't show any error and the fields are reset as well .
Here is the code when form is submit .
if player_form.is_valid() and stats_form.is_valid() and contacts_form.is_valid() and extra_stats_form.is_valid():
player = player_form.save(commit=False)
stats_form = PlayerStatsForm(request.POST, instance=player.stats)
stats = stats_form.save()
contacts_form = PlayerContactsForm(request.POST, instance=player.contacts)
contacts = contacts_form.save()
extra_stats_form = PlayerExtraStatsForm(request.POST, instance=player.extra_stats)
extra_stats = extra_stats_form.save()
return redirect(self.get_success_url())
else:
return self.form_invalid(player_form)`
The get_context_data function which adds sub_forms into the main player form .
context.update({
'object_title_plural': 'Players',
'list_headings': self.list_headings,
'form_panel_title': 'Add Player',
'save_button_text': 'Add Player',
'search_form': self.form_defaults(PlayerSearchForm()),
'sub_forms': (self.form_defaults(self.get_form(PlayerStatsForm)),
self.form_defaults(self.get_form(PlayerContactsForm)),
self.form_defaults(self.get_form(PlayerExtraStatsForm)))
})
Here is the template in which sub forms are rendered .
{% block display_subforms %}
{% if sub_forms %}
{% include "dashboard/includes/sub_form.html" %}
{% endif %}
{% endblock %}

How to send a success message with get_success_url() in Django FormView

I've got FormView that redirect to a previous page if the form was valid. That's works fine but how can I tell a user that the information has been posted? I want him to see a success message in a modal window after redirect.
I've tried to do it through request.session in get_success_url but it doesn't fit to my goals because a user can submit the form multiple times. So how can I return any message with redirect in get_success_url in FormView?
My FormView
class CatPhotoUploadFormView(FormView):
template_name = 'blank.html'
form_class = CatPhotoForm
def get_success_url(self):
self.request.session['success_message'] = 'Everything is fine'
return reverse('cat:detail_cat', args=(self.kwargs['pk'],))
def form_valid(self, form):
cat = Cat.objects.filter(id__exact=self.kwargs['pk'])
for each in form.cleaned_data['attachments']:
print('****', each, '****', type(each))
Photo.objects.create(photo_path=each, photo_author=self.request.user, photo_cat = cat[0])
return super(CatPhotoUploadFormView, self).form_valid(form)
Use Django messaging framework for this purpose, change get_success_url with message.
from django.contrib import messages
def get_success_url(self):
messages.add_message(self.request, messages.INFO, 'form submission success')
return reverse('cat:detail_cat', args=(self.kwargs['pk'],))
In your template, something like this ( NOTE: remember to pass messages )
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
In fact Django has a ready to use mixin, SuccessMessageMixin, which can be use in class based views to achieve the same purpose.
Like so:
from django.contrib.messages.views import SuccessMessageMixin
class CatPhotoUploadFormView(SuccessMessageMixin, FormView):
template_name = 'blank.html'
form_class = CatPhotoForm
success_message = 'Everything is fine'
...
Very clean and straightforward.
def get_success_url(self):
from django.contrib import messages
messages.success(self.request, 'Yes it works!')
This will do the work
def form_valid(self, form):
print(self.request.POST)
form.save()
messages.add_message(
self.request,
messages.SUCCESS,
'User registered sucessfully'
)
return super().form_valid(form)

django-crispy-forms : help_text_inline of FormHelper not working as expected

For the following model:
class MyModel(models.Model):
name = models.CharField(max_length=110, help_text="Some sample help text.")
def __unicode__(self):
return u'%s' % (self.name)
And the following modelform:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-myModelForm'
self.helper.form_class = 'form-horizontal'
self.helper.form_action = 'my_model_form_url'
self.helper.form_error_title = 'Form Errors'
self.helper.help_text_inline = True
class Meta:
model = MyModel
Using the following template:
{% extends "base.html" %}
{% block content %}
{% load crispy_forms_tags %}
{% crispy form %}
{% endblock %}
The help_text defined in the model does not get rendered at all. It does get rendered if I change to self.helper.help_text_inline = False instead of self.helper.help_text_inline = True, but that's not what I want.
How do I get the help_text to show with self.helper.help_text_inline = True?
The base.html is all proper with bootstrap files all included.
This is due to the fact that help_text_inline and error_text_inline cannot be set to the same value in order to work. If you set help_text_inline to True, you need to set error_text_inline to False. If you don't do it, help text messages are not displayed, in order to show form errors in case they happen.
I hadn't thought about this in detail until now. So probably the best would be to add a logging warning, telling the user to be careful with this. Maybe automatically overriding the default behavior of the other flag, in case one is set. I'm open to suggestions.

Django registration form throws - Exception Value:The given username must be set

I am stuck on a simple registration form in django. I get this error when i complete my registration form:
ValueError at /register/
The given username must be set
Request Method:POST
Request URL:http://127.0.0.1:8000/register/
Django Version:1.4
Exception Type:ValueError
Exception Value:The given username must be set
Here is my views.py:
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {'form': form
})
return render_to_response(
'registration/register.html',
variables
)
and this is my register template:
{% extends "base.html" %}
{% block title %}User Registration{% endblock %}
{% block head %}User Registration{% endblock %}
{% block content %}
<form method="post" action=".">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="register" />
</form>
{% endblock %}
and my url patterns:
urlpatterns = patterns('',
(r'^$', main_page),
(r'^user/(\w+)/$', user_page),
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', logout_page),
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': site_media}),
(r'^register/$', register_page),
(r'^register/success/$', direct_to_template,
{'template': 'registration/register_success.html'}),
)
I cannot find anything about this issue. I would appreciate some guidance, i am new in Django.
The problem was in my forms.py file:
def clean_username(self):
username = self.cleaned_data['username']
if not re.search(r'^\w+$', username):
raise forms.ValidationError(u'Username can only contain '
'alphanumeric characters and the underscore.')
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(u'Username is already taken.')
After going through the code and indending correctly the problem is fixed. As i said i am new in Django and python
thanks

django render_to_string not working

I am trying to implement an ajax view to create an object and then return it and insert it into the template. It is almost working except I cannot seem to get render_to_string() to work to render the html to insert. The object is being created and html is being returned and inserted into the template however the variables are not included in the html. My view is below.
def tr_create_xhr(request, person, slug):
if request.method == "POST":
form = TopicResourceForm(request.POST)
if form.is_valid():
try:
r = Resource.objects.get(url=form.cleaned_data['resource'])
except Resource.DoesNotExist:
r = Resource.objects.create(url=form.cleaned_data['resource'], rtype=form.cleaned_data['rtype'])
r.save()
obj = form.save(commit=False)
obj.resource = r
try:
topic = Topic.objects.get(person__user=request.user, slug__iexact=slug)
except Topic.DoesNotExist:
return Http404
obj.topic = topic
objs = obj.save()
html = render_to_string('includes/tr_inc.html',{"r":objs, "topic":topic})
res = {'html':html}
if request.is_ajax():
return HttpResponse(simplejson.dumps(res), mimetype="application/json")
else:
return HttpResponseRedirect("../..")
return Http404
This is the template "includes/tr_inc.html":
{% load markup %}
{% load people_tags %}
<li>
<h5>{{ r.title }}</h5>
<p><a class="tru" href={{ r.resource.url }}>{{ r.resource.url|sliceit:70 }}</a></p>
<span class="oc"><p>Added {{ r.added }}{% if r.rtype %} |<a href={% url resource_type_detail r.rtype.slug %}>{{ r.rtype }}</a>{% endif %}
| Delete Edit
{{ r.note|markdown }}</span>
</li>
The html string that is returned is the template without any variables inserted.
Model method 'save' doesn't return an object. So you 'objs' variable is empty. You should write
html = render_to_string('includes/tr_inc.html',{"r":obj, "topic":topic})
I had exact the same problem today. It is quite easy. Please check the Django document for this method, there is actually a third optional parameter: context_instance=RequestContext(request). Therefore your render_to_string should be like this:
html = render_to_string(
'includes/tr_inc.html',{"r":obj, "topic":topic},
context_instance=RequestContext(request))
Then everything should work properly.

Resources