How to render template after failed form validation? - django

urls.py:
urlpatterns = [
path('employee/add_employee/', views.add_employee, name='add-employee'),
path('employee/add_employee/add/', views.add_employee_action, name='add-employee-action'),
]
I have add-employee page and some forms to fill there.
views.py:
def add_employee(request):
personal_form = PersonalEmployeeForm()
history_form = EmployeeHistoryForm()
return render(
request,
'sections/add_employee.html',
context={
'personal_form': personal_form,
'history_form': history_form,
}
)
def add_employee_action(request):
if request.method == "POST":
personal_form = PersonalEmployeeForm(request.POST)
history_form = EmployeeHistoryForm(request.POST)
if personal_form.is_valid() and history_form.is_valid():
# here is some logic with models
return redirect('add-employee')
else:
personal_form = PersonalEmployeeForm()
history_form = EmployeeHistoryForm()
return render(
request,
'sections/add_employee.html',
context={
'personal_form': personal_form,
'history_form': history_form,
}
)
template:
<form id="a-submit-form" action="add/" method="POST">
{% csrf_token %}
<div class="column-wrapper">
<div class="column">
<div class="form-wrapper">
{% for field in personal_form.visible_fields %}
{% include "elements/forms/form_line.html" %}
<br>
{% endfor %}
</div>
</div>
<div class="column">
<div class="form-wrapper">
{% for field in history_form.visible_fields %}
{% include "elements/forms/form_line.html" %}
<br>
{% endfor %}
</div>
</div>
</div>
<div class="button-bar-wrapper">
<div class="button_bar">
<a class="a-button positive" id="submit">Добавить</a>
<a class="a-button" href="{% url 'employee' %}">Сотрудники</a>
<a class="a-button" href="{% url 'index' %}">На главуную</a>
</div>
</div>
</form>
Submitting by <a> element is tested and worked well with jQuery script.
The problem is after submitting invalid forms I have a page with blah-blah/employee/add_employee/add/ URL. And if I try to submit forms again I have a page with blah-blah/employee/add_employee/add/add/ URL, which is incorrect. How can I render the page with blah-blah/employee/add_employee/ URL and show all error messages?

This is likely because you have written a relative URL in the <form> tag of the sections/add_employee.html template. The template thus contains something like:
<form method="post" action="add/">
...
</form>
You can use a URL with the {% url … %} template tag [Django-doc]:
<form method="post" action="{% url 'add-employee-action' %}">
...
</form>
Furthermore one usually uses the same path to handle both the GET and the POST request. So in fact you might simply remove the 'add-employee' path.

Related

How to prevent redirect_to_login from targeting LOGIN_REDIRECT_URL

So in general when a user logs in, my app redirects him to dashboard-overview:
# settings.py
# Login and Logout target routes
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'dashboard-overview'
LOGOUT_REDIRECT_URL = '/'
However for a specific case I want the user to be redirected to 'calculator-page' using a helper function which doesn't work as the user is again redirected to the value of LOGIN_REDIRECT_URL.
# views.py
def render_calculator(request, slug):
"""
Renders the calculator page
"""
# Check for the user being authenticated
if not request.user.is_authenticated:
return redirect_to_login('calculator-page')
# routes to http://127.0.0.1:8000/login/?next=calculator-page which looks fine,
# but then it fails after login to re-route as specified and sends to
# 'dashboard-overview' again
else:
club_obj = Offer.objects.get(club__slug=slug)
# Create Context
context = {
'club_obj': club_obj,
}
return render(request, 'core/calculator.html', context)
# urls.py
from django.urls import path, include
from django.contrib.auth import views
from applications.user import views as user_views
urlpatterns = [
# Login View
path('login/', views.LoginView.as_view(template_name='user/login.html'), name="login"),
path('logout/', views.LogoutView.as_view(), name="logout"),
# Sign Up
path('signup/', user_views.signup, name='signup'),
]
# template user/login.html
{% extends 'user/user_frame.html' %}
{% load static %}
<!-- Styles -->
{% block styles %}
<link href="{% static 'css/user_frame.css' %}" rel="stylesheet" type="text/css">
{% endblock styles %}
{% block form %}
<div class="logo-container"><img src="{% static 'images/logo_negative_text.svg' %}" class="logo-image"></div>
<div class="form-container">
<!-- form headline -->
<h1 class="form-headline">Login to your Farena Account</h1>
<!-- form -->
<form class="form" method="post" action=".">
{% csrf_token %}
<div class="error-wrapper">
<div class="errors">{{ form.non_field_errors }}</div>
</div>
<div class="email-wrapper field-wrapper">
<div class="tag name-tag">{{ form.username.label }}*</div>
<div class="input">{{ form.username }}</div>
</div>
<div class="password-wrapper field-wrapper">
<div class="tag">{{ form.password.label }}*</div>
<div class="input">{{ form.password }}</div>
<div class="forgot-password">Forgot Password?</div>
</div>
<!-- submit button -->
<button class="login-button" type="submit">Log In</button>
</form>
</div>
<div class="account-info">No account yet? Sign Up!</div>
{% endblock form %}
The issue is with your template for the login view - you're missing the next form field that tells Django where to redirect the user after they have logged in. See the documentation for sample template code for this view.
Specifically, you need to add this inside your form:
<input type="hidden" name="next" value="{{ next }}">
That's missing right now which means Django will always just default to LOGIN_REDIRECT_URL.

AttributeError at /mobile/7/delete/ 'tuple' object has no attribute '_meta'

I am trying to delete an item from my project but it gives error. interestingly it is deleting the item but gives error mention above.
views.py:
def delete_mobile(request, mobile_id):
mobile1 = get_object_or_404(Mobile, pk=mobile_id).delete()
form = MobileForm(instance=mobile1)
if form.is_valid():
form.delete()
return redirect('mobile')
return render(request, 'device/delete_mobile.html', {'mobile': mobile1})
delete_mobile.html:
{% extends 'device/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form action="{% url 'delete_mobile' mobile.instance.id %}" method="POST">
{% csrf_token %}
<h3>Delete {{ header }}:</h3>
{{ mobile|crispy }}
<button class="btn btn-success" type="submit">Delete</button>
</form>
{% endblock %}
urls.py:
path('mobile/<str:mobile_id>/delete/', views.delete_mobile, name='delete_mobile'),
actually, the problem was occurring due to repetition of .delete() method in my delete view.
now I have fixed it and working properly.
def delete_mobile(request, mobile_id):
mobile1 = get_object_or_404(Mobile, pk=mobile_id)
form = MobileForm(instance=mobile1)
if form.is_valid():
form.delete()
return redirect('mobile')
return render(request, 'device/delete_mobile.html', {'mobile': mobile1})

django view 'GET' does not work, 'POST' does work

I have no Idea why, but if I try to 'GET' this view I get this error:
django.urls.exceptions.NoReverseMatch: Reverse for 'profile-update-name' with arguments '('',)' not found. 1 pattern(s) tried: ['ce_profiles/profile\\-update\\-name/(?P<pk>[0-9]+)/$']
But if submit a 'POST' request, everything is fine. ???
Here is the work flow:
First I access this view and template:
def customer_profile(request, pk):
name = get_object_or_404(CEName, id=pk)
return render(
request,
'customer/customer_profile.html',
{'profile_name': name}
)
{% block content %}
{% include 'ce_profiles/name_header.html' %}
<div id="name" class="container d-flex justify-content-between pt-1">
{% include 'ce_profiles/profile_name.html' %} <!-- displays the profile_name -->
<button id="update_button" action="{% url 'ce_profiles:profile-update-name' profile_name.id %}" class="bold btn btn-main btn-sm button-main">UPDATE</button>
</div>
<div id="div_NameForm" class="container">
</div>
{% endblock %}
{% block script %}
<script src="{% static 'ce_profiles/ce_profiles.js' %}"></script>
{% endblock %}
When I click the update button it should AJAX in the form to be modified. I've tried substitution of the variable to just a '1', but it still doesn't work. I have tests to verify that the view by itself doesn't work with the 'GET', but does with the 'POST', apart from the previous view.
urls.py
app_name='ce_profiles'
urlpatterns = [
path('profile-update-name/<int:pk>/', views.profile_update_name,
name='profile-update-name'
),
]
view
def profile_update_name(request, pk):
name = get_object_or_404(CEName, id=pk)
if request.POST:
name_form = NameForm(data=request.POST, instance=name)
if name_form.has_changed() == False:
name_form.add_error(None, _('No changes made.'))
if name_form.is_valid():
name_form.save()
updated_name = get_object_or_404(CEName, id=name.id)
profile_name_json = render_to_string(
'ce_profiles/profile_name.html',
{'profile_name': updated_name,}
)
return JsonResponse({'profile_name': profile_name_json})
else:
return JsonResponse({'error': name_form.errors})
else:
name_form = NameForm(instance=name)
get_NameForm_json = render_to_string(
'ce_profiles/update_NameForm.html',
{'NameForm': name_form}
)
return JsonResponse({'NameForm': get_NameForm_json})
Here is the template. Is something missing?
update_NameForm.html
<div id="div_NameForm" class="container">
<hr size="3px">
<form id="NameForm" method="POST" action="{% url 'ce_profiles:profile-update-name' profile_name.id %}">
{% csrf_token %}
{{ NameForm.as_p }}
<div id="NameForm_errors"></div>
<br>
<button id="save_changes" type="submit" class="btn btn-main button-main btn-block">Save Changes</button>
</form>
</div>
Instead of using render_to_string, you may use render(), and you forgot to send the variable profile_name: to the templates.
def profile_update_name(request, pk):
name = get_object_or_404(CEName, id=pk)
if request.POST:
''' code '''
# no need to use else statement
# since 'return' breaks the 'if request.POST' statement
name_form = NameForm(instance=name)
context = {
profile_name:name,
'NameForm': name_form,
}
return render(request,'ce_profiles/update_NameForm.html',context)
Note that you can you leave action='' empty in the form which will mean the same view
<form id="NameForm" method="POST" action="">

Django templates {% block content %} can contain only two form fields?

The question is: Can we send in the form into the template with more than 2 fields(I'm sure it should be possible). Please, advice what might be the issue:
I've created the form with 3 fields:
class Email(forms.Form):
owner = forms.ChoiceField(choices=['Tyler', 'Aleks'])
title = forms.CharField(max_length=150, label='Name the stuff')
file = forms.FilePathField(path=path, label='Enter the path')
Afterward, operated it in view, where I'm sending form object to the template:
def mail_distribution(request):
if request.method == 'POST':
form = Email(request.POST)
if form.is_valid():
render(request, 'general/email.html', {'form': form})
else:
form = Email()
return render(request, 'general/email.html', {'form': form})
Here is the template itself:
{% extends 'general/base.html' %}
{% block content %}
<form class="form-vertical" action="mail_distrib" role="form" method="post">
{% csrf_token %}
<div class="form-group" style="display: inherit">
<center>
{{form.title}}
</center>
</div>
<div class="form-group" style="display: inherit">
<center>
{{form.owner}}
</center>
</div>
<div class="form-group" style="display: inherit">
<center>
{{form.file}}
</center>
</div>
<center><input type="submit" value="OK"></center>
</form>
{% endblock %}
Afterwards I get the error:
*Error during template rendering
In template E:\Tyler\Projects\Web_dev\dj_api\Scripts\distribution\general\templates\general\email.html, error at line 13
too many values to unpack (expected 2)
line13 - 13 {{form.owner}}*
Change choices attribute in owner fields like this
owner = forms.ChoiceField(choices=[('Tyler','Tyler'), ('Aleks', 'Aleks')])

When moving from one view to another the latter one is not processed

So i am having a dlist view that displays a list of fields with checkboxes from which to choose. The chosen field returns the entire row that is sent via a submit button to the edit template for further processing. Here is the dlist view and its form:
def dlist(request):
...
if "_edit" in request.POST:
print "You pressed update button in list template"
form = selectForm2(request.POST)
if form.is_valid(): # All validation rules pass
print "selectForm VALID!"
delete_items = form.cleaned_data['select_fields']
for item in delete_items:
instance = model_class.objects.get(**{field_list[2]:item})
instance_list.append(item)
messages.success(request, 'Selected fields updated')
return render(request, 'Directories/edit.html', {"field_names": field_names, "instance_list": instance_list, ...})
else:
return HttpResponse('ERROR in POST -- Return to form submission')
class selectForm2(forms.Form):
select_fields = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=fields())
# the choices for the form. It is a list of field ('descr_en') values.
def fields():
m_values = Attributes.objects.values_list('descr_en', flat=True)
for val in m_values:
field_classes.append((val, val))
return field_classes
and its template
*list.html*
...
<form action="" method="post"> {% csrf_token %}
<tbody>
{% with field_names|slice:"1:" as sliced_fnames %}
<tr>
{% for f_name in sliced_fnames %}
{% if f_name = sliced_fnames.0 %}
<td>
{{form.as_p}}
</td>
{% endif %}
{% endfor %}
{% endwith %}
</tbody>
</table> <br />
<input type="submit" value="Update selected items" name="_edit" />
</form>
Now when the "_edit" submit button is pressed I should be redirected to the edit template and the modelEdit view and see the first message printed as seen below:
def modelEdit(request):
print "seems like u pressed the edit button!"
updated_item = request.POST.get('select_fields')
print "updated_item", updated_item
...
return render(request, 'Directories/edit.html')
However this does not happen and nothing is displayed instead. Still, the dictionaries passed from the dlist view (field_names, instance_list) through selectForm are rendered in the edit.html:
*edit.html*
<div id="content" align="center">
<h3> Update entry</h3>
<br />
{{m_tb_name}}
{% if instance_list %}
{% for instance in instance_list %}
{{instance}}
{% endfor %}
{% endif %}
<form action="" method="post"> {% csrf_token %}
{% for f_name in field_names %}
{% if not forloop.first %}
{% for instance in instance_list %}
{{f_name}}: <input id="edit-{{f_name}}" value="{{instance}}" type="text" name={{f_name}} /><br />
{% endfor %}
{% endif %}
{% endfor %}<br />
<input type="submit" name="_alter" value="Update" />
<input type="reset" name="Clear" value="Clear" />
</form>
</div>
And lastly, here is my urls.py file:
*urls.py*
from django.conf.urls import patterns, url
from Directories import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^list$', views.dlist, name='list_models'),
url(r'^edit$', views.modelEdit, name='edit_models'),
...
)
So when I manually edit the URL in my browser as Directories/edit I see the printed message from my view but when I try to reach the edit.html throught the "_edit" button in list.html it renders the edit template but does not process the view (modelEdit) for some reason.
You are simply rendering the Directories/edit.html, not invoking the view. You can verify that using a print statement in the modelEdit.
Instead what you might need is
return HttpResponseRedirect('/edit')
This will invoke your views.