Correct <form action=" " URL for a SessionWizardView - django

I have been having some trouble getting a Django SessionWizardView to submit data to my database and I am trying to isolate the issue.
I note from the Form documentation
As well as its elements, a form must specify two things:
where: the URL to which the data corresponding to the user’s input should be returned
how: the HTTP method the data should be returned by
and
Form data sent back to a Django Web site is processed by a view,
generally the same view which published the form. This allows us to
reuse some of the same logic.
Currently I am using <form action="/surveyone/" method="post"> which I believe is correct.
The issue is that my view is called class SurveyWizardOne(SessionWizardView): but if I try to use this in the form action I get an error as soon as I click Next on the first page of the survey.
Question: Based on the below is action="/surveyone/" correct?
Thanks
urls.py
url(r'^surveyone/$', SurveyWizardOne.as_view([
SurveyFormIT1,
SurveyFormIT2,
Start,
SurveyFormA,
SurveyFormB,
SurveyFormC,
SurveyFormD,
SurveyFormE,
SurveyFormSpike1,
SurveyFormF1,
SurveyFormF2,
SurveyFormF3,
SurveyFormDV1,
SurveyFormF4,
SurveyFormF5,
SurveyFormF6,
SurveyFormSpike2,
SurveyFormDV2,
SurveyFormF7,
SurveyFormF8,
SurveyFormF9,
SurveyFormDV3,
SurveyFormDV4,
SurveyFormDV5,
SurveyFormG,
SurveyFormH,
SurveyFormI
])),
views.py
class SurveyWizardOne(SessionWizardView):
def get_context_data(self, form, **kwargs):
context = super(SurveyWizardOne, self).get_context_data(form, **kwargs)
step = int(self.steps.current)
....
....
return context
def done(self, form_list, **kwargs):
return render(self.request, 'Return_to_AMT.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 %}
<div class="main_content">
<p>Page: {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="/surveyone/" 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>

Since your form is submitting to the same url, you can simply use action="". If you prefer, you can use action="/surveyone/"
If you don't want to hardcode the url in your template, then you need to name your url patterns:
url(r'^surveyone/$', SurveyWizardOne.as_view([
SurveyFormIT1,
SurveyFormH,
...
]), name="survey_one"),
You can then use the url tag in your template:
action="{% url 'survey_one' %}"

Related

Django 2.1 NoReverseMatch error Using post method in Class Based Views

Url :
re_path(r'^detail/(?P<slug>\w+)/$', ProblemDetail.as_view(), name='problem_detail'),
View :
class ProblemDetail(View):
template_name='problem/problem_detail.html'
form_class=AnswerForm
def get(self,request,slug):
context={'problem':Problem.objects.get(slug=slug),'form':self.form_class()}
return render(request,self.template_name,context)
def post(self,request,slug):
bound_form=self.form_class(request.POST)
obj=Problem.objects.get(slug=slug)
real_answer=obj.answer
if bound_form.is_valid():
if bound_form.cleaned_data['answer'] == real_answer:
return render(request,
'problem/Answerstatus.html',
{'message':'Good Job !'})
else:
return render(request,
'problem/Answerstatus.html',
{'message':'Wrong ! Try Again !'})
Template :
{% extends "problem/base_problem.html" %}
{% block content%}
<h2>{{problem.p_name}}</h2>
<h3>{{problem.difficulty}}</h3>
<p>{{problem.p_description}}</p>
<form action= "{% url 'problem_detail' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" > Check </button>
</form>
{% endblock %}
The above template is a rough idea for testing (Its not Final template,needs tons of changes, we know that.)
I get the following error :
Reverse for 'problem_detail' with no arguments not found. 1 pattern(s) tried: ['detail/(?P<slug>\\w+)/$']
You should add slug argument when you are returning in post method. Try returning HttpResponseRedirect and reverse to your url together with slug in args list.
return HttpResponseRedirect(reverse('problem_detail', args=[slug]))
It looks as if the problem is occuring in a {% url %} tag in your problem/Answerstatus.html, but we can't be sure because you haven't included that template in your question.
It looks as if you need to include the problem in your template whenever you render the template, for example,
return render(request, 'problem/Answerstatus.html', {'problem': obj, 'message':'Good Job !'})
and then in the template, include the slug in the {% url %} tag:
{% url 'problem_detail' problem.slug %}
Add get_absolute_url method to your model like this :
def get_absolute_url(self):
return reverse('problem_detail',kwargs={'slug':self.slug})
Also in your templates :
{% block body-block %}
<h2>{{problem.p_name}}</h2>
<h5>{{problem.difficulty}}</h5>
<p>{{problem.p_description}}</p>
<form action= "{{ problem.get_absolute_url }}" method="post"> # Pay Attention here
{% csrf_token %}
{{ form.as_p }}
<button type="submit" > Check </button>
</form>
{% endblock %}
This will fetch the url and the regex will match .

How to Create a UpdateForm with TemplateView?

I need to create a UpdateForm with a TemplateView. Why with TemplateView? Because, I has a attribute what is geo_location, and I'm using LeafLet maps, and LeafLet maps doesn't work with generic.UpdateView or others the same type.
Here my views from Update:
class UpdateStore(LoginRequiredMixin, TemplateView):
template_name = 'store_form'
success_url = reverse_lazy('register:store_list')
def post(self, request, *args, **kwargs):
store_id = kwargs['store']
store = get_object_or_404(Store, pk=store_id)
form = StoreForm(request.POST, on_edit=True)
if form.is_valid():
form.save()
return redirect(reverse('register:store_list'))
else:
context = self.get_context_data()
context['data_form'] = form
return render(request, self.template_name, context)
return self.get(request)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
store_id = self.kwargs['store']
store = get_object_or_404(Store, pk=store_id)
data = {
'name': store.name,
'description': store.description,
'address': store.address,
'geo_loc': store.geo_loc,
'opened': store.opened
}
context['editing'] = True
context['data_form'] = StoreForm(initial=data, on_edit=True)
context['store'] = store
return context
Here is my template code:
{% extends 'base.html' %}
{% load bootstrap3 %}
{% load leaflet_tags %}
{% block extra_css %}
{% leaflet_css plugins="forms" %}
{% endblock %}
{% block body %}
<h1> Update Store </h1>
<form method="POST">
{% csrf_token %}
{{ form }}
{% buttons %}
<button type="submit">
{% bootstrap_icon "star" %} Save
</button>
{% endbuttons %}
</form>
{% endblock %}
{% block extra_js %}
{% leaflet_js plugins="forms" %}
{% endblock %}
I trying this, but in my template, the Forms doesn't load, and my template are blanked :(. Someone knows why? I need another method for get anything else?
Thanks.
The problem with your code is that you place the form in the data_form key of the context:
context['data_form'] = StoreForm(initial=data, on_edit=True)
and then on the template you try to use it with {{form}} instead of {{data_form}}. After that the form should be rendered.

Using redirect sends me to /tag/?search=input instead of /tag/input (Django URL argument from form)

I have a page where there is a path /tag/name_of_tag and you can see all posts tagged with that tag.
Inside the page, you can also select another tag in a form and go to that tag.
The problem is that instead of going to /tag/searched_tag, it goes to /tag/?search=searched_tag
How can I change it doesn't leave the ?search= part?
urls.py:
url(r'tag/(?P<input_tag>\w+)$', views.tag_view, name='tag'),
views.py:
def tag_view(request, input_tag):
form = TagSearchForm()
if request.method == 'GET':
form = TagSearchForm(request.GET)
if form.is_valid():
input = form.cleaned_data['search']
print(input)
return redirect('fortykwords:tag_view', input)
else:
form = SearchForm()
latest_post_list = Post.objects.filter(tags=input_tag, status__exact="published")
paginator = Paginator(latest_post_list, 3)
page = request.GET.get('page')
posts = paginator.get_page(page)
context = {'latest_post_list': latest_post_list, 'page_tag': input_tag, 'form': form}
return render(request, 'fortykwords/tag.html', context)
forms.py:
class TagSearchForm(forms.Form):
search = tagulous.forms.SingleTagField(
tag_options=tagulous.models.TagOptions(
autocomplete_view='fortykwords:post_tags_autocomplete'
),
label='Tags',
required=True,
help_text=_('Filter by lead tags. You can organize leads by any tag you want.'),
)
tag.html:
{% extends "base_generic.html" %}
{% block content %}
<form action="." method="get">
{{ form }}
<input type="submit" value="Submit" />
</form>
<h3>Posts with the tag {{ page_tag }}</h3>
{% if latest_post_list %}
<ul>
{% for post in latest_post_list %}
<li> {{ post.author }} {{ post.pub_date }}
<br>
{{ post.title }}</li>
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}
</ul>
{% else %}
<p>No posts are available.</p>
{% endif %}
{% endblock %}
You need to provide the argument input to redirect method as input_tag=input.
Example:
return redirect('fortykwords:tag_view', input_tag=input)
It's showing as /tag/?search=searched_tag because your form is submitting by GET but never getting to the redirect. It seems is_valid() is returning False.
I've tested a very similar version of your code and don't think it's a bug in tagulous, but would still be interested to know what had gone wrong (I wrote tagulous). Spotted a couple of places you can streamline your code a bit, so try::
def tag_view(request, input_tag):
# Can't see any POSTs in your example, so you can pass the form GET here
# Might also be nice to pass the original tag in so it shows in the form
form = TagSearchForm(request.GET, initial={'search': input_tag})
# The form already has the GET, so you can go straight into the is_valid
if form.is_valid():
input = form.cleaned_data['search']
print('Valid: ', input)
return redirect('fortykwords:tag_view', input)
else:
print('Invalid: ', form.errors, form.non_field_errors)
# You can remove the else for if not GET, which would never be reached
# on to pagination as before
(although fwiw I'd recommend ipdb instead of print)

How to change the value of ENDLESS_PAGINATION_PER_PAGE from template side?

I use Django Endless Paginator plugin.
I try to change the value of ENDLESS_PAGINATION_PER_PAGE from template side.
As default is 10.
I want to realize some dropbox where the user can change between (Example: 10,20,50,100 Objects).
Thanks for answer, but something not work property.
1. I have view.py
def adv_search(request):
objects = None
if request.GET.get('key'):
form = AdvancedSearchForm(request.GET)
if form.is_valid():
repo = Repository()
objects = list(repo.find_objects('*'+form.cleaned_data['key'] +'*', type=FileObject, chunksize=20))
return render(request, 'templates/adv_search.html', {'form': form, 'objects': objects })
return render(request, 'templates/adv_search.html', {'form': AdvancedSearchForm(), 'objects': objects})
2. Then forms.py
class AdvancedSearchForm(forms.Form):
key = forms.CharField(max_length=500, label="", widget= forms.TextInput(attrs={'class': 'label'}))
show_props = forms.MultipleChoiceField(choices = (("pid", "pid"),("title", "title"),("type","type"),("source","source"),("date","date"),("publisher","publisher"),("subject","subject"),("label","label"),("cDate","cDate"),("mDate","mDate")),required=False, widget=forms.CheckboxSelectMultiple(attrs={'class': 'my-class'}))
paginator = forms.ChoiceField(choices =(('10', '10'),('20','20'), ('50','50'), ('100','100')) ,required=False, )
3. adv_search.html
{% paginate paginator objects %}
{% for obj in objects %}
...
<div class="paginator">
{% get_pages %}
{{ pages.first_as_arrow }}
{% show_pages %}
{{ pages.last_as_arrow }}
<form method="get">
{{ form.paginator }}
<input type="submit" Value="Go">
</form>
{{ pages.total_count }} total objects
</div>
Everything works except select
Thanks Andrey
You can have a form which will set pagination per page and then use that submitted value in template. Here is an example:
View
from django.shortcuts import render
from .models import Entry
def endless_view(request):
pagination_per_page = request.GET.get('per_page', 10)
entries = Entry.objects.all()
return render(request, 'some_template.html', {'pagination_per_page':
pagination_per_page, 'entries': entries})
Template
{% extends 'base.html' %}
{% load endless %}
{% block content %}
{# #}
<form action="" method="get">
<select name="per_page">
<option value="10">10 per page</option>
<option value="20">20 per page</option>
<option value="50">50 per page</option>
<option value="100">100 per page</option>
</select>
<input type="submit" Value="Go">
</form>
{% paginate pagination_per_page entries %}
{% for entry in entries %}
{# your code to show the entry #}
{% endfor %}
{% show_pages %}
{% endblock content %}
Reference
http://django-endless-pagination.readthedocs.org/en/latest/templatetags_reference.html

Django Include Not Including Content

I want to use {% include %} to include a second form on a page, but the form is empty..
views.py:
def year_form(request):
thing_list = Thing.objects.all()
if request.method == 'POST':
form = YearBrowseForm(request.POST)
if form.is_valid():
year = form.cleaned_data['year']
return HttpResponseRedirect(reverse('browse_years', kwargs={'year':year}))
else:
form = YearBrowseForm()
return render(request, 'browse-year.html', {'form':form, 'thing_list':thing_list})
forms.py:
class YearBrowseForm(forms.Form):
year = forms.ChoiceField(choices=YEARS_EMPTY, widget=forms.Select(attrs={'onchange': 'this.form.submit();'}))
url:
url(r'^browse/years/(?P<year>\d+)/$', 'my_app.views.browse.year_form', name='browse_years'),
html:
{% extends 'base.html' %}
{% block content %}
{% include 'browse-year.html' %}
{% endblock %}
browse-year.html:
<form action='' method='post' enctype='multipart/form-data'>
{{ form.as_p }}
{% csrf_token %}
</form>
This renders an empty form (i.e. the source shows that a form is being included, but it is empty of content). What am I missing here? This is a continuation of this question that I am trying to solve in a different method by using {% include %}. Thanks for your ideas!
This happens because you are rendering browse-year.html:
return render(request, 'browse-year.html', {'form':form, 'thing_list':thing_list})
You should be rendering the other html, the one you didn't gave a name, but starts with
{% extends 'base.html' %}
only by rendering that one you extend the base, and the other content appears.