When a button is clicked, the function should be executed, but the page should not be refreshed - django-views

I have a problem with my project and I don't know how to solve it.
When the button is clicked the page returns with refresh the button should do the task but the page should not refresh what can I do it
My view:
post = Post.objects.get(id=pk)
liked =False
if post.like.filter(id=request.user.id).exists():
post.like.remove(request.user)
liked = False
else:
post.like.add(request.user)
liked = True
return redirect(request.META['HTTP_REFERER'])
Html
<a style="text-decoration: dashed; color: white;" type="submit" href="{% url 'like' n.pk %}">
<i style="font-size: x-large;" class="fa-regular fa-heart"></i>
</a>
Url
path('like/<int:pk>/',LikeView,name="like"),

Related

Pass POST data in form with no action url Django

I have here a form with two buttons in it and when submitting, it doesn't pass the data on another page.
<form method="post" id="form-id">
#some form inputs here...
<a class="btn btn-success" id="yourFunction()" href="{% #url_here %}">published</a>
<a class="btn btn-secondary" href="{% #url_here %}">save as draft</a>
</form>
Does anyone know how to pass a post data in tag in Django, if the form tag has no action URL?
thanks in advance!
If you have a multiple button inside a form where this button has a different location/URL to go you can use this Javascript property to modify or set the action to the different buttons inside the form.
function yourFunction() {
document.getElementById("form-id").action = "your url";
}

Single page design - loading new content on link click

I'm working on a django webpage using single page design approach. Generally what I'm trying to achieve is to have some new content being loaded (picture gallery) to my main webpage after clicking certain links.
What I have already achieved is that the new content is being loaded on a link click but unfortunately it seems like the whole page is being reloaded and not rendered correctly. My current implementation is based on having main index.html template and extension template, both using {% block content %} relation.
views.py
def index(request):
categories = Category.objects.all().order_by('name')
return render(request, 'index.html', {'categories': categories})
def gallery(request, id):
category = Category.objects.get(id=id)
return render(request, 'gallery.html', {'category': category})
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('view_gallery/<int:id>/', views.gallery, name='view_gallery')
]
index.html
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
{% for category in categories%}
<li class="nav-item">
<a class="nav-link" href="{% url 'view_gallery' category.id %}">{{ category.name }}</a>
</li>
{% endfor %}
</ul>
</div>
<div>
{% block content %}
{% endblock %}
</div>
gallery.html
{% extends 'index.html' %}
{% block content %}
<p>{{ category.name }}</p>
{% endblock %}
I hope I have explained clearly what I'm trying to achieve. Could you please point me in the right direction?
Requests and page changes in single page sites work quite differently than those for normal web pages. Single page sites use JavaScript to change the page content rather than requesting a new page from the server. Single page sites can request content from the server, but generally that content is just data while the structure (HTML) of the page is decided by the client in JavaScript. The only time a full HTML page is sent by the server is on the initial request, which should be responded to with index.html.
In your example, you could have this work by adding a script that requests content from the server and modifies the DOM when a link is clicked.
For example:
const a1 = document.querySelector("a.link1");
const a2 = document.querySelector("a.link2");
a1.addEventListener("click", () => {
setContent("<p>Content from link 1</p>")
});
a2.addEventListener("click", () => {
setContent("<p>Content from link 2</p>")
});
function setContent(content) {
const contentDiv = document.querySelector("div.content");
contentDiv.innerHTML = content;
}
a {
text-decoration: underline;
color: blue;
}
a:hover {
cursor: pointer;
}
<h1>My Page</h1>
<a class="link1">link 1</a>
<a class="link2">link 2</a>
<div class="content">
</div>
And the click event callbacks could request content from your server instead of having the content hard-coded as in this example. Note that then the server should respond with just a snippet of HTML rather than an entire new page.
For example, you could use the following function to get content for the div.content element:
function fetchData() {
const response = fetch("/gallery");
return response;
}
If you're new to single page sites, you might checkout a framework like React, Vue, or Angular to get started and gain a better understanding or even use for this project.

obtain django dropdown menu item queryset

I am trying to make a calendar html page, that has a dropdown button to select the different months. How to get to this calendar page is via the nav bar that is created at base.html
base.html - how to get to the calendar page.
....
....
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" data-target="scheduler_dropdown" href="#"><i class="fas fa-calendar"></i>Scheduler</a>
<div class="dropdown-menu" aria-labelledby="scheduler_dropdown">
<a class="dropdown-item" href="{% url 'view_schedule' %}"><i class="fas fa-calendar-alt"></i>View Schedule</a>
</div>
</li>
what i've build so far:
urls.py
urlpatterns = [
path('schedule/view-schedule/', views.view_schedule, name='view_schedule'),
path('schedule/view-schedule/?query=month<str:selected_month>', views.view_schedule,
name='view_schedule_selected_month'),
]
Views.py
def view_schedule(request, selected_month=None):
if request.method == 'POST':
print('post')
else:
current_month = date.today().month
current_year = date.today().year
# a = request.GET # How to get query set from dropdown menu???
# print(a)
args = {
'month_cal': monthcalendar(current_year, current_month),
'month_name': calendar.month_name[current_month],
'year_name': current_year,
}
return render(request, 'static/html/view_schedule.html', args)
view_schedule.html
<div class="card-header">
Schedule for {{ month_name }} {{ year_name }}
<form class="date-selector" method="post">
{% csrf_token %}
<div class="dropdown">
<button class="btn dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="far fa-caret-square-down"></i>
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" href={% url 'view_schedule_selected_month' selected_month=1 %}>Jan</a>
<a class="dropdown-item" href={% url 'view_schedule_selected_month' selected_month=2 %}>Feb</a>
<a class="dropdown-item" href={% url 'view_schedule_selected_month' selected_month=3 %}>Mar</a>
</div>
</div>
</form>
</div>
My problem is that, when I click on the drop down button and select the relevant month Jan, Feb, Mar, the url changes, but in my views.py, the query set doesn't appear. So I can't extract the query for processing.
Any thoughts?
Turns out I could have just done print(selected_month) and it would print the query result.. I got the idea when I was watching this video: https://www.youtube.com/watch?v=qmxoGYCFruM
Don't use urlpatterns to handle query strings. urlpatterns handles only the URL itself; query parameters are part of the GET data and are handled within the callback method. You'll need to change the way your HTML, urlpatterns, and the view work to accommodate this.
urlpatterns = [
path('schedule/view-schedule/', views.view_schedule, name='view_schedule'),
]
In your HTML, you'll want a form with a dropdown that GETs the data to the URL above. You can use the select tag for this.
And then in the view, you can extract GET data from request.GET. Specifically, if you used the select tag as suggested above, then the user's choice will be in request.GET[NAME] where NAME is the name of the select tag.
There are other ways to go about this, depending on aesthetic preferences, etc., but the method I've explained above is likely to be the easiest.
Also, query set (or QuerySet) has a very specific meaning in Django. It refers to a type of object used in database queries as explained here. The results of an HTML form are not "query sets."

preventing hardcoded url to delete in django

I have a link and button inside it that deletes a record associated with the button
<a href="{% url 'parts:stock_delete' stock.id %}">
<button class="delete" type="submit" name="delete_btn" style="position: absolute; right: 40px;" >Delete</button>
</a>
but I want the record to be deleted only when the button is pressed but not when URL is typed in the address bar like:
http://127.0.0.1:8000/parts/stock_delete/16/
what can be done for this?
You should really use DeleteView!
It's a generic class-based view that - when viewed (GET) will display a confirmation page asking if you really want to delete the record. When you confirm (POST) the record will be deleted.
#sasuke's answer doesn't take into consideration another large security hole, CSRF. You'll want to study this page very closely if you want to ensure that your application is secure.
You can do as Kye said, use DeleteView via post to exclude the registry. There is a concern with CSRF, but Django already provides a way for AJAX requests to bring the token. You can check in the documentation:https://docs.djangoproject.com/en/1.11/ref/csrf/#setting-the-token-on-the-ajax-request
You can use post method. And when the button is pressed, you can pass some flag, say flag1.
<form action="{% url 'parts:stock_delete' stock.id %}" method="post" >
<input type="hidden" name="flag1" value="True" >{% csrf_token %}
<button class="delete" type="submit" name="delete_btn" style="position: absolute; right: 40px;" >Delete</button></a>
</form>
and in the backend you can write like this in your function in views.py.
# delete view
....
if request.method == 'POST':
if 'flag1' in request.POST:
... # deleted your object here

Delete via AJAX results in CSRF token missing or incorrect

Django 1.10
DetailView renders "detail.html".
In "detail.html" I place "Delete" button. In other words, I want users to be able to press "Delete" exactly from "detail.html" when they see the object itself.
So, on pressing "Delete" button I call FrameDelete via AJAX.
Could you help me understand why I get the error: CSRF token missing or incorrect.
class Frame(models.Model):
title = models.CharField(max_length=100,
null=False,
blank=False,
default="",
unique=True,
verbose_name="Заголовок")
def get_delete_url(self):
return reverse("frame:frame_delete", kwargs=self.get_kwargs())
class FrameDelete(ActionDeleteMixin, DeleteView):
model = Frame
success_url = reverse_lazy("empty")
template_name = 'general/ajax/ajax_confirm_delete.html'
class ActionDeleteMixin():
def get_context_data(self, **kwargs):
context = super(ActionDeleteMixin, self).get_context_data(**kwargs)
context["action"] = self.object.get_delete_url()
return context
js
function post_delete(){
$.ajax({
method: 'POST',
url: "{{ object.get_delete_url }}",
success: redirect_to_frame_list,
error: fail
});
}
function fail(jqXHR, textStatus, errorThrown){
debugger;
}
ajax_confirm_delete.html
{% block content %}
<form action="{{ action }}" method="post">{% csrf_token %}
<p>Are you surely want to delete "{{ object }}"?</p>
<input id="confirm" type="submit" value="Confirm" />
</form>
{% endblock %}
In the browser when inspecting element in Chrome:
<form action="/frame/18/delete/" method="post"><input type="hidden" name="csrfmiddlewaretoken" value="bHZxf62Oa9WrapuacCm8gLVNlY2nJHllfwqPsAHoPO0RS8z8NnhMSv5tnIFQZKPP">
<p>Are you surely want to delete "18: Michael's news"?</p>
<input id="confirm" type="submit" value="Confirm">
</form>
**
In the browser when stop at the debugger breakpoint in fail function
"
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="robots" content="NONE,NOARCHIVE">
<title>403 Forbidden</title>
<style type="text/css">
html * { padding:0; margin:0; }
body * { padding:10px 20px; }
body * * { padding:0; }
body { font:small sans-serif; background:#eee; }
body>div { border-bottom:1px solid #ddd; }
h1 { font-weight:normal; margin-bottom:.4em; }
h1 span { font-size:60%; color:#666; font-weight:normal; }
#info { background:#f6f6f6; }
#info ul { margin: 0.5em 4em; }
#info p, #summary p { padding-top:10px; }
#summary { background: #ffc; }
#explanation { background:#eee; border-bottom: 0px none; }
</style>
</head>
<body>
<div id="summary">
<h1>Forbidden <span>(403)</span></h1>
<p>CSRF verification failed. Request aborted.</p>
</div>
<div id="info">
<h2>Help</h2>
<p>Reason given for failure:</p>
<pre>
CSRF token missing or incorrect.
</pre>
<p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
<a
href="https://docs.djangoproject.com/en/1.10/ref/csrf/">Django's
CSRF mechanism</a> has not been used correctly. For POST forms, you need to
ensure:</p>
<ul>
<li>Your browser is accepting cookies.</li>
<li>The view function passes a <code>request</code> to the template's <code>render</code>
method.</li>
<li>In the template, there is a <code>{% csrf_token
%}</code> template tag inside each POST form that
targets an internal URL.</li>
<li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
<code>csrf_protect</code> on any views that use the <code>csrf_token</code>
template tag, as well as those that accept the POST data.</li>
<li>The form has a valid CSRF token. After logging in in another browser
tab or hitting the back button after a login, you may need to reload the
page with the form, because the token is rotated after a login.</li>
</ul>
<p>You're seeing the help section of this page because you have <code>DEBUG =
True</code> in your Django settings file. Change that to <code>False</code>,
and only the initial error message will be displayed. </p>
<p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
</div>
</body>
</html>
"
You should add the csrf_exempt or generate the csrf token with ajax