How to delete arguments from url - django

I need to generate keys by clicking button and to show generated ones.
Here my view:
#user_passes_test(lambda u: u.is_superuser)
def get_key(request):
if(request.GET.get('create_new_key')):
for_whom = request.GET.get('for_whom')
create_key(for_whom)
created_keys = RegistrationKey.objects.all()
return render(request, 'registration/get_key.html', {
'created_keys':created_keys,
})
And template:
<!DOCTYPE html>
<html>
{% include "core/header.html" %}
<body>
<form action="#" method="get">
<input type="text" name="for_whom"/>
<input type="submit" class="btn" value="Create" name="create_new_key">
</form>
<ul>
{% for created_key in created_keys %}
<p>{{created_key.key}}</p>
<p>{{created_key.for_whom}}</p>
{% endfor %}
</ul>
</body>
{% include "core/footer.html" %}
</html>
Now when I'm clicking the button on page http://127.0.0.1:8000/get_registration_key/ the key is generating but now I'm at http://127.0.0.1:8000/get_registration_key/?for_whom=&create_new_key=Create#, so refreshing this page would generate more keys.
I really need to cut this arguments from url but don't understand how.

You should modify your view to redirect to the same view when a new key is created to clear the querystring, and render only when there is no for_whom argument:
#user_passes_test(lambda u: u.is_superuser)
def get_key(request):
if request.GET.get('create_new_key'):
for_whom = request.GET.get('for_whom')
create_key(for_whom)
return redirect('get_key') # Use the name in your urls.py
else:
created_keys = RegistrationKey.objects.all()
return render(request, 'registration/get_key.html', {
'created_keys':created_keys,
})

Related

How to render template after failed form validation?

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.

add multiple forms to page on button click

I am working on developing a permitting app using django. This is my first django project so bear with me here...
we have a default utility permit that contains some basic info like property owner and address. Then from that you can attach a sewer, or water or row or any combination of related tables to the permit. Basically I am looking for a way to return a page with the default utility permit then have a series of links or buttons to add more forms to that page.
I made some model forms for each of the models and can display them individually on the page
forms.py
class UtilityPermitForm(forms.ModelForm):
class Meta:
model = UtilityPermit
fields = ['...']
class SewerPermitForm(forms.ModelForm):
class Meta:
model = SewerPermit
fields = ['...']
class WaterPermitForm(forms.ModelForm):
class Meta:
model = WaterPermit
fields = ['...']
I successfully added them to a list and could iterate through and get them to add
views.py
class BuildForms(View):
permits = []
utility_form = UtilityPermitForm
sewer_form = SewerPermitForm
water_form = WaterPermitForm
permits.append(utility_form)
permits.append(sewer_form)
permits.append(water_form)
template_name = 'engineering/UtilityPermitForm2.html'
def get(self, request, *args, **kwargs):
out_permits = []
for form in self.permits:
out_permits.append(form())
return render(request, self.template_name, {'form': out_permits})
def post(self, request, *args, **kwargs):
if request.GET.get('testButton'):
return HttpResponse("I guess")
form = self.utility_form(request.POST)
return render(request, self.template_name, {'form': form})
def add_permit(self, request, permit):
# need to get a thing to add a permit to the list
pass
.html
{% block content %}
<div>
<form class="site_form" action={% url 'engineering:utility_permit' %} method="post">
{% csrf_token %}
{% for item in form %}
{{ item }}
<hr>
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
{% endblock content %}
so again, my problem is I want to start with a one permit and then have links or buttons to add each form as needed. I'm a bit at a loss here and any help would be greatly appreciated.
EDIT:
so I have this base permit that comes up when a user navigates to it like so, and I want to have a user click the add sewer permit button or link or whatever
and then the corresponding permit will come up
you can create multiple same form in one page dynamically using formset
see Documentation
and maybe this tutorial is exactly what you are looking for.
EDITED
if I understand your question correctly, how about this:
first, it would be better to separate your form with dictionaries instead of list in your views.py
context = {
'utility_form': self.utility_form,
'sewer_form': self.sewer_form,
'water_form': self.water_form
}
return render(request, self.template_name, context)
then in your .html file,
if you want to add one form each time you click the button, my trick is:
show your base permit form first (said utility_form), button to add other form, and hide your other form first.
<div class="form-container">
<form class="site_form" action={% url 'engineering:utility_permit' %} method="post">
{% csrf_token %}
{{ utility_form }}
<div id="additional-forms"></div> <!-- notice this div -->
<hr>
<input type="submit" value="Submit">
</form>
</div>
<button class="add-sewer-form">Sewer Permit</button>
<div id="sewer-form-template" style="display: none;">
<div class="sewer-form-container">
{{ sewer_form }}
</div>
</div>
and then using jquery to add onclick listener, clone that hidden form, then insert it after base form (actually inside div with id additional-forms).
$('.add-sewer-form').click(function(){
let sewer_form = $('#sewer-form-template .sewer-form-container:first').clone(true);
$(sewer_form).appendTo($('#additional-forms'))
});
I haven't test it yet, but when you click the add button, it should be give result like this:
<div class="form-container">
<form class="site_form" action={% url 'engineering:utility_permit' %} method="post">
{% csrf_token %}
{{ utility_form }}
<div id="additional-forms">
<div class="sewer-form-container">
{{ sewer_form }}
</div>
</div>
<hr>
<input type="submit" value="Submit">
</form>
</div>
<button class="add-sewer-form">Sewer Permit</button>
<div id="sewer-form-template" style="display: none;">
<div class="sewer-form-container">
{{ sewer_form }}
</div>
</div>
Hope it can answer your question :)
First add the button
<button><button>
Then add onclick attribute to it which will help react on click
<button onclick='do'><button>
Then create script that contain the function to display the other form
<script>
function do() {
document.getElementById('form').innerHTML ='add your form here'
}
</script>
all together
<button onclick='do'><button>
<script>
function do() {
document.getElementById('form').innerHTML ='add your form here'
}
</script>

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="">

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.

In Django, is it possible to access the current user session from context of parent template within a inclusion tag?

As we know, if we want to access user session from context within a inclusion tag, you can use takes_context argument and pass a request context in the view.
But in my project, it is more complicated:
The view is simple:
# views.py
def index(request):
form = PersonForm()
return render(request, 'add.html', {'form': form})
Templates:
# templates/add.html
<html>
<head>
<title>Add Person</title>
</head>
<body>
<form enctype="multipart/form-data" action="" method="post">
{{ form.as_p }}
</form>
{% render_attachments %}
...
</body>
</html>
# templates/list.html
{% load my_tags %}
<div class="attachments" style="margin:12px 0 12px 0;">
{% for attachment in attachments %}
<a href="{{ attachment.attachment_file.url }}">{{ attachment.filename }}
</a>
{% attachment_delete_link attachment %}
{% endfor %}
</div>
Here is my custom tags:
# my_tags.py
#register.inclusion_tag('attachments/list.html', takes_context=True)
def render_attachments(context):
session = context['request'].session
return {'attachments': session.get('attachments', [])}
#register.inclusion_tag('attachments/delete_link.html', takes_context=True)
def attachment_delete_link(context, attachment):
if context['user'] == attachment.creator:
return {
'delete_url': reverse('delete_attachment',
kwargs={'attachment_pk': attachment.pk})
}
return {'delete_url': None}
When i run my project, i got the following error:
KeyError at /person/
'user'
Request Method: GET
Request URL: http://localhost:8000/person/
Django Version: 1.5.1
Exception Type: KeyError
So, i print context out within two tags to find out what happened, it seemed that the request context does not passed into attachment_delete_link, how can i resolve this problem?
You are overwriting the whole context in render_attachments() you must return
def render_attachments(context):
# some code...
context['attachments'] = session.get('attachments', [])
return context
Same goes for attachment_delete_link().