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

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

Related

why first parameter is send as id to views in django [duplicate]

I'm trying to clone the Instagram web page using Django(version-3.1).
My Django project has an app called 'post'.
One of its template I have a form which is posting a comment to a post. The form post request should call the path('add_comment/',views.add_comment,name='add_comment'), but It's calling path('<slug:slug>/',views.post_details,name='post_details'), instead. And raising DoesNotExist at /post/add_comment error. I added print() statement at the beginning of both add_comment() and post_details() methods to find out which is running when the request is made. I have no idea what I have done wrong.
The project GitHub link - https://github.com/mirasel/Instagram_Clone
the post_details.html template is -
{% extends 'base.html' %}
{% load static %}
{% block title %} post {% endblock %}
{% block profilephoto %} {{ propic.url }} {% endblock %}
{% block body %}
<div>
<div>
<img src="{{post.image.url}}" alt="post" height="250px" width="250px">
</div>
<div>
<a href="{% url 'instagram:profile' post.uploader %}">
<img src="{{uploader.profile_pic.url}}" alt="{{uploader}}" style="border-radius: 50%;" height="24px" width="24px">
{{ post.uploader }}
</a><br>
<p>{{ post.date_published.date }}</p>
</div>
<div>
<p>{{ post.caption }}</p>
</div>
<div>
<form action="{% url 'post:add_comment' %}" id="comment_form" method="POST">
{% csrf_token %}
<textarea name="comment" id="comment" cols="30" rows="1" placeholder="Write a comment..."></textarea>
<input type="hidden" name="slug" id="slug" value="{{post.slug}}">
<!-- <input type="submit" style="display: none;" name="submit"> -->
</form>
<script>
$(function(){
$("#comment").keypress(function (e) {
if(e.which == 13 && !e.shiftKey) {
$(this).closest("form").submit();
e.preventDefault();
}
});
});
</script>
{% endblock %}
the views.py -
from django.shortcuts import render,redirect
from instagram.views import get_nav_propic,get_profile_details
from .models import UserPost,PostComment,PostLike
from django.http import JsonResponse
def get_post_likes(post):
likes = PostLike.objects.filter(post=post)
total_likes = len(likes)
likers = []
for l in likes:
likers.append(get_profile_details(l.liker))
return {'likers':likers,'total_likes':total_likes}
def get_post_comments(post):
comments = PostComment.objects.filter(post=post)
total_comments = len(comments)
commenter = []
comment = []
for c in comments:
commenter.append(get_profile_details(c.commenter))
comment.append(c.comment)
postcomment = zip(commenter,comment)
return {'post_comment':postcomment,'total_comments':total_comments}
def upload_post(request):
if request.method == 'POST':
image = request.FILES['post_img']
caption = request.POST['caption']
uploader = request.user
UserPost.objects.create(uploader=uploader,image=image,caption=caption)
return redirect('instagram:feed')
else:
context = {
'propic' : get_nav_propic(request.user)
}
return render(request,'post/upload_post.html',context)
def post_details(request,slug):
print('I am here in post details')
post = UserPost.objects.get(slug=slug)
context = {
'propic' : get_nav_propic(request.user),
'post' : post,
'uploader' : get_profile_details(post.uploader),
'LIKES' : get_post_likes(post),
'COMMENTS' : get_post_comments(post),
}
return render(request,'post/post_details.html',context)
def add_comment(request):
print('I am here in add comment')
if request.method == 'POST':
post_slug = request.POST.get('slug')
post = UserPost.objects.get(slug=post_slug)
user = request.user
comment = request.POST.get('comment')
PostComment.objects.create(post=post,commenter=user,comment=comment)
return redirect('post:post_details',slug=post_slug)
the urls.py -
from django.urls import path
from . import views
app_name='post'
urlpatterns = [
path('upload_post/',views.upload_post,name='upload_post'),
path('<slug:slug>/',views.post_details,name='post_details'),
path('add_comment/',views.add_comment,name='add_comment'),
]
The error - Error page
Solved
I had to make the URL path of add_comment as following-
#previous one
path('add_comment/',views.add_comment,name='add_comment'),
#modified one
path('comment/add_comment/',views.add_comment,name='add_comment'),
This is because the pattern for the slug URL and add comment URL were similar.
Because Django will process the urlpatterns sequentially, from docs:
Django runs through each URL pattern, in order, and stops at the first
one that matches the requested URL, matching against path_info.
And '/add_comment' is a valid slug <slug:slug>, so post_details will be called.
So you should keep the definition of the most generic url patterns at last:
urlpatterns = [
path('upload_post/',views.upload_post,name='upload_post'),
path('add_comment/',views.add_comment,name='add_comment'),
path('<slug:slug>/',views.post_details,name='post_details'),
]
Hopefully this will work for you.

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})

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.

How to delete arguments from url

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,
})

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')])