The links contained in the form normally display correctly not in the form but in the form every time I click the button I get a forbidden access error. Is the HTML correct? Is there a way around this?
{% for v in owner_obj %}
<div class="container">
<form action="{{ v.Link }}" method="POST" type="submit">
<button style="border: none;">
{% csrf_token %}
<input type="hidden" name="tshirt_id" value="{{ v.id }}" />
<a href="{{ v.Link }}" rel="noopener noreferrer">
<img src="{{ v.Images }}" width="150" height="150">
</a>
</button>
</form>
<figcaption> {{ v.Titles }} </figcaption>
<figcaption> <b>{{ v.Prices }}</b></figcaption>
</div>
{% endfor %}
Added views.py
if request.method == 'POST':
T_shirt.objects.filter(id=request.POST.get('tshirt_id')).update(views=F('views') + 1)
You can't do that if the external website has the validation of csrf_token, because is insecure if anyone is able to do a post to your url from outsite of your website. The only way to do that is if the external website disable the csrf_token validation.
With ajax should be something like this:
<html>
<head>
<script>
$(function () {
$(".counter-button").on("click", function (event) {
event.preventDefault();
var data_id = $(this).attr("data-id");
var url = $(this).attr("href");
$.ajax({
type: "POST",
url: "{{ request.path }}",
data: {
'tshirt_id': data_id
},
success: function () {
window.location.replace(url);
}
});
return false;
});
});
</script>
</head>
<body>
{% for v in owner_obj %}
<div class="container">
<a href="{{ v.Link }}" class="counter-button" data-id="{{ v.id }}">
<img src="{{ v.Images }}" width="150" height="150">
</a>
<figcaption> {{ v.Titles }} </figcaption>
<figcaption> <b>{{ v.Prices }}</b></figcaption>
</div>
{% endfor %}
</body>
</html>
You have to include Jquery before the javascript script.
And then in the added views.py you should do something like this:
import json
from django.http import JsonResponse
def added(request):
if request.method == 'POST':
post_data = json.loads(request.body.decode('UTF-8'))
T_shirt.objects.filter(
id=post_data.get('tshirt_id')
).update(views=F('views') + 1)
return JsonResponse({"message": "success"})
I'm sorry if this doesent work but you might have to put
{{ v.Link }}
Related
I have a follow/unfollow button. Currently my Jquery is working on the first submit but when I toggle the button again, the Jquery doesn't change any of the elements.
html
{% for user in followers %}
<div class="flist" id="flist-{{ user.profile.pk }}">
<article class="media comment-section">
<a class="mr-2" href="{% url 'user-posts' user %}">
<img class="rounded-circle comment-img" src="{{ user.profile.image.url }}"></a>
<div class="media-body mt-1">
{% if user.profile in following %}
<div class="remove-follower-div-{{ user.profile.pk }}">
<form method="POST" action="{% url 'remove-follower-js' user.profile.pk %}" class="remove-follower-form" id="{{ user.profile.pk }}">
{% csrf_token %}
<button class="btn btn-unfollow unfollow-link" type="submit" style="float:right;" id="unfollow-button-{{ user.profile.pk }}"><span>Following</span></button>
</form>
</div>
{% else %}
<div class="add-follower-div-{{ user.profile.pk }}">
<form method="POST" action="{% url 'add-follower-js' user.profile.pk %}" class="add-follower-form" id="{{ user.profile.pk }}">
{% csrf_token %}
<input type="hidden" name="profile_id" value="{{ user.profile.pk }}">
<button class="btn btn-follow btn-light side-link mr-4" type="submit" style="float:right;" id="follow-button-{{ user.profile.pk }}">Follow</button>
</form>
</div>
{% endif %}
.js file
$( document ).ready(function() {
$('.add-follower-form').on("submit", function(e) {
e.preventDefault();
const profile_id = $(this).attr('id');
console.log(profile_id)
const url = $(this).attr('action');
console.log(url)
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
'profile_id':profile_id,
},
dataType: 'json',
success: function(response) {
console.log('success')
console.log(response)
console.log(profile_id)
console.log(url)
$(`.add-follower-div-${profile_id}`).find('.add-follower-form').attr('action', "/profile/"+ profile_id + "/followers/remove/js");
$(`.add-follower-div-${profile_id}`).find('.add-follower-form').attr('class', "remove-follower-form");
$(`.add-follower-div-${profile_id}`).attr('class', `remove-follower-div-${profile_id}`);
$(`#follow-button-${profile_id}`).attr('class',"btn btn-unfollow unfollow-link");
$(`#follow-button-${profile_id}`).empty();
$(`#follow-button-${profile_id}`).append('<span>Following</span>');
$(`#follow-button-${profile_id}`).attr('id',"unfollow-button-"+profile_id);
},
error: function(response) {
console.log('error', response);
}
});
});
$('.remove-follower-form').on("submit", function(e) {
e.preventDefault();
const profile_id = $(this).attr('id');
console.log(profile_id)
const url = $(this).attr('action');
console.log(url)
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
'profile_id':profile_id,
},
dataType: 'json',
success: function(response) {
console.log('success')
console.log(response)
console.log(profile_id)
console.log(url)
$(`.remove-follower-div-${profile_id}`).find('.remove-follower-form').attr('action', "/profile/"+ profile_id + "/followers/add/js");
$(`.remove-follower-div-${profile_id}`).find('.remove-follower-form').attr('class', "add-follower-form");
$(`.remove-follower-div-${profile_id}`).attr('class', `add-follower-div-${profile_id}`);
$(`#unfollow-button-${profile_id}`).attr('class',"btn btn-follow btn-light side-link mr-4");
$(`#unfollow-button-${profile_id}`).find('span').remove();
$(`#unfollow-button-${profile_id}`).text("Follow");
$(`#unfollow-button-${profile_id}`).attr('id',"follow-button-"+profile_id);
},
error: function(response) {
console.log('error', response);
}
});
});
});
views.py
#AJAX RESPONSE FOR FOLLOWERS.HTML, FOLLOWING.HTML
def add_follower_js(request, *args, **kwargs):
if request.method == 'POST':
pk = request.POST.get('profile_id')
profile = Profile.objects.get(pk=pk)
profile.followers.add(request.user)
data = {
'success': '1',
}
return JsonResponse(data, safe=False, status=200)
return redirect(request.META.get('HTTP_REFERER', 'redirect_if_referer_not_found'))
def remove_follower_js(request, *args, **kwargs):
if request.method == 'POST':
pk = request.POST.get('profile_id')
profile = Profile.objects.get(pk=pk)
profile.followers.remove(request.user)
data = {
'success': '1',
}
return JsonResponse(data, safe=False, status=200)
return redirect(request.META.get('HTTP_REFERER', 'redirect_if_referer_not_found'))
urls.py
path('profile/<int:pk>/followers/add/js', user_views.add_follower_js, name='add-follower-js'),
path('profile/<int:pk>/followers/remove/js', user_views.remove_follower_js, name='remove-follower-js'),
Example: If the button is currently (showing Following):
<div class="remove-follower-div-{{ user.profile.pk }}">
<form method="POST" action="{% url 'remove-follower-js' user.profile.pk %}" class="remove-follower-form" id="{{ user.profile.pk }}">
{% csrf_token %}
<button class="btn btn-unfollow unfollow-link" type="submit" style="float:right;" id="unfollow-button-{{ user.profile.pk }}"><span>Following</span></button>
</form>
</div>
I click it, the button submits the form, view returns sucessfully, and button html is changed to (showing Follow):
<div class="add-follower-div-{{ user.profile.pk }}">
<form method="POST" action="{% url 'add-follower-js' user.profile.pk %}" class="add-follower-form" id="{{ user.profile.pk }}">
{% csrf_token %}
<input type="hidden" name="profile_id" value="{{ user.profile.pk }}">
<button class="btn btn-follow btn-light side-link mr-4" type="submit" style="float:right;" id="follow-button-{{ user.profile.pk }}">Follow</button>
</form>
</div>
But when I click it again, the html doesn't change back to showing Following. The url and all the html stays the same.. what am I doing wrong..
I spent a few minutes looking through your code to see what the error might be but honestly I think there's a fundamentally better way of doing this that isn't so repetitive and cumbersome, JS-wise. I would suggest having a single view and url for toggling whether someone is a follower or not. It will make everything so much simpler. See below:
# template - a single form, the if-statement controls the text only
<form method="POST"
class="follower-form"
action="{% url 'toggle-following' user.profile.pk %}">
{% csrf_token %}
<button class="btn" type="submit" style="float:right;">
{% if user.profile in following %}
Following
{% else %}
Follow
{% endif %}
</button>
</form>
Then we let the view to do the lifting, note the name change. I also changed the URL but I'm sure you know how to change this. We have a simple if-statement that checks whether someone is already following or not, it sets the action accordingly and determines the response. I have changed the JSON response to a simple HTTP response because you don't need JSON here:
def toggle_follower(request, pk):
if request.method != 'POST':
return redirect(... # truncated
profile = Profile.objects.get(pk=pk)
if request.user not in profile_followers.all():
profile.followers.add(request.user)
return HttpResponse("Following") # JSON response is unnecessary
else:
profile.followers.remove(request.user)
return HttpResponse("Follow")
As we are determining the value on the server and we only have a single form that handles both follow/unfollow functions, your Jquery then becomes far more concise:
$('.follower-form').on("submit", function(e) {
e.preventDefault();
const url = $(this).attr('action');
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken': ... // truncated
},
success: function(response) {
// simply set the button text to the response
e.target.innerText = response
},
...
});
});
Is there a way to design a form template that is used by content pages with various parameters? Something like this:
_layouts/form.html
---
layout: default
---
<form method="POST" action="/info/{{ page.action }}">
{% for question in page.questions %}
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="question{{ question.id }}">
<label class="form-check-label" for="question{{ question.id }}">{{ question.text }}</label>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<script>
$("form").submit(function(event) {
var inputElems = $("input"),
count = 0;
for (var i=0; i<inputElems.length; i++) {
if (inputElems[i].type === "checkbox" && inputElems[i].checked === true){
count++;
}
}
if (count > {{ page.count }}) {
return;
} else {
event.preventDefault();
alert("Alert");
}
});
</script>
And a content page could consist of only a front matter that defines variables used by the template:
---
layout: form
title: Title
action: /results
questions: site.data.questions1
count: 10
---
The nicer solution would be to create some YAML file with variables definitions that are injected into the template accounting for the permalink of the form page. This file would be used to generate the end form pages.
Do you know about includes files in Jekyll?
Rather than determining the layout of a page so its hard to add stuff to it, the includes functionality is like a function or piece of reusable code. Which you can use on pages or layouts.
And it works well with parameters.
From the docs
_include/image.html
<figure>
<a href="{{ include.url }}">
<img src="{{ include.file }}" style="max-width: {{ include.max-width }};"
alt="{{ include.alt }}"/>
</a>
<figcaption>{{ include.caption }}</figcaption>
</figure>
Call like
---
---
{% include image.html url="http://jekyllrb.com"
max-width="200px" file="logo.png" alt="Jekyll logo"
caption="This is the Jekyll logo." %}
Or pass variable names instead of values, as defined in frontmatter or data file. Be careful not to override URL on the page with an image url or it can break things.
---
my_image:
url: '...'
alt: '...'
---
{% include image.html url=page.my_image.url alt=page.my_image.html ... %}
From _data/gallery.yaml
{% include image.html url=site.data.gallery.my_image.url
... %}
Or pass an object with attributes on it.
---
my_image:
url: '...'
alt: '...'
---
{% include image.html image_attributes=page.my_image %}
I had tried several times to fix this but not successfull, likes count are all updating when I use the admin panel but not in html template..
views.py
from common.decorators import ajax_required
#ajax_required
#login_required
#require_POST
def like_post(request):
# image_id = get_object_or_404(Post, id=request.POST.get('id'))
image_id = request.POST.get('id')
action = request.POST.get('action')
if image_id and action:
try:
image = Post.objects.get(id=post_id)
if action == 'like':
image.likes.add(request.user)
else:
image.likes.remove(request.user)
return JsonResponse({'status':'ok'})
except:
pass
return JsonResponse({'status':'error'})
post_view.html
{% extends 'base.html' %}
{% load static %}
{% block title %}Users Posts{% endblock %}
{% block content %}
<div class="container-fluid">
<form method="post" enctype="multipart/form-data">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Post">
</form>
</div>
{% for post in posts %}
<div class="w3-container w3-card w3-white w3-round w3-margin"><br>
<img src="{% if post.user.profile.photo %} {{post.user.profile.photo.url}}
{% else %}{% static 'img/user.png' %}{% endif %}" alt="Avatar" class="w3-left w3-circle w3-margin-right" style="width:60px">
<span class="w3-right w3-opacity">{{ post.created }}</span>
<h4>{{ post.user|title }}</h4><br>
<hr class="w3-clear">
<p>{{ post.title }}</p>
{% if post.image %}
<div style="max-width:100%;height:auto;object-fit: cover;" class="img-fluid">
<img src="{{ post.image.url }}" class="img-fluid" style="max-width:100%;height:auto;object-fit: cover;">
</div>
<p>{{ post.description }}</p>
{% endif %}
{% with total_likes=post.likes.count users_like=post.likes.all %}
<div class="image-info">
<div>
<span class="count">
<span class="total">{{ total_likes }}</span>
like{{ total_likes|pluralize }}
</span>
<a href="#" data-id="{{ post.id }}" data-action="{% if request.user in users_like %}un{% endif %}like" class="like button mb-3">
{% if request.user not in users_like %}
Like
{% else %}
Unlike
{% endif %}
</a>
</div>
</div>
{% endwith %}
</div>
{% endfor %}
{% include 'post/pagination.html' with page=posts %}
{% endblock %}
{% block domready %}
$('a.like').click(function(e){
e.preventDefault();
$.post('{% url "post:like" %}',
{
id: $(this).data('id'),
action: $(this).data('action')
},
function(data){
if (data['status'] == 'ok')
{
var previous_action = $('a.like').data('action');
// toggle data-action
$('a.like').data('action', previous_action == 'like' ?
'unlike' : 'like');
// toggle link text
$('a.like').text(previous_action == 'like' ? 'Unlike' :
'Like');
// update total likes
var previous_likes = parseInt($('span.count .total').text());
$('span.count .total').text(previous_action == 'like' ?
previous_likes + 1 : previous_likes - 1);
}
}
);
});
{% endblock %}
base.html
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/js.cookie.min.js' %}"></script>
<script>
var csrftoken = Cookies.get('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$(document).ready(function(){
{% block domready %}
{% endblock %}
});
</script>
urls.py
from django.urls import path
from post import views
from django.conf import settings
from django.conf.urls.static import static
app_name = 'post'
urlpatterns = [
path('',views.post_list,name='post_list_view'),
path('like/',views.like_post,name='like'),
]
This the entire code when I tried to modify the ajax function the like button changes and then it will change all other likes present in the post_view.html, where it contains the all other posts in the same page..
Hello Developers I am using Python Flask , Knockout.js and Google App Engine. Now I am making the basic program which is poster I am just posting some stuff using poster. But It seems that Google App Engine Datastore does not takes the values from request.json['...']
when form.validate_on_submit() is also in if condition of the view.
Here is my Flask View code
#app.route('/post/new',methods=['POST','GET'])
def post_it():
form = CreatePost(request.form)
use_db = ndb.Key(model.User, current_user.name)
if **form.validate_on_submit()** and request.method=='POST':
posting = model.Post(
name = use_db,
poster = request.json['post'],
postbody = request.json['postbody'],
posturl = request.json['posturl'],
)
try:
posting.put()
flash("Poster has been populated", category='info')
return jsonify({ "post": request.json['post'],"postbody": request.json['postbody'], "posturl": request.json['posturl'] })
except CapabilityDisabledError:
flash('Error Occured while posting')
return redirect(url_for('post_it'))
return render_template('poster.html', form=form, use_db = use_db, post_db = post_db)
#app.route('/posts',methods=['GET'])
def all_posts():
post_db = model.Post.query()
return jsonify(post_db=post_db)
My poster.html code
<form id="new_postform" action="{{ url_for('post_it') }}" data-bind="submit: addPost" class="navbar-form form-inline" method="post" >
{{ form.csrf_token }}
<fieldset>
{{ form.hidden_tag() }}
<div class="control-group">
<p>Post</p>
<div class="controls">
<p>
{{ form.poster(id='post',class='form-control required ',placeholder="My Post", autocomplete=True , size=3,**{'data-bind': 'value: post'})}}</p>
{% if form.poster.errors %}
<ul class="errors">
{% for error in form.poster.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
<div class="control-group">
<p>postbody<p>
<div class="controls">
<p>{{ form.postbody(id='postbody',class='form-control required ',placeholder="Post Description", autocomplete=True , size=3, **{'data-bind': 'value: postbody'} )}}</p>
</div>
</div>
<div class="control-group">
<p>post url</p>
<div class="controls">
<p>
{{ form.posturl(id='posturl',class='form-control required ',placeholder="My Post", autocomplete=True , size=3, **{'data-bind': 'value: posturl'} )}}</p>
{% if form.posturl.errors %}
<ul class="errors">
{% for error in form.posturl.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
<div class="control-group">
<div class="controls">
<br><input type="submit" id="postit" name="postit" value"poster" class="btn btn-primary" /><br><br>
</div>
</div>
</fieldset>
</form>
<ul data-bind="foreach: posts, visible: posts().length > 0">
<p data-bind="text: post"></p>
<p data-bind="text: postbody"></p>
<p data-bind="text: posturl"></p>
</ul>
<div class="controls">
</div>
{% block tail_script %}
<script src="{{ url_for('static', filename='bootstrap/js/jquery.validate.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap/js/additional-methods.js') }}"></script>
<script type="text/javascript">
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function Post(data) {
this.post = ko.observable(data.post);
this.postbody = ko.observable(data.postbody);
this.posturl = ko.observable(data.posturl);
}
function PostViewModel() {
var self = this;
self.posts = ko.observableArray([]);
self.post = ko.observable();
self.postbody = ko.observable();
self.posturl = ko.observable();
console.log(self.post)
self.addPost = function() {
self.save();
self.post("");
self.postbody("");
self.posturl("");
};
$.getJSON('/posts', function(postModels) {
var t = $.map(postModels.posts, function(item) {
return new Post(item);
});
self.posts(t);
});
self.save = function() {
return $.ajax({
url: '/post/new',
contentType: 'application/json',
type: 'POST',
data: JSON.stringify({
'post': self.post(),
'postbody': self.postbody(),
'posturl': self.posturl(),
}),
success: function(data) {
console.log("Pushing to post array");
self.posts.push(new Post({ post: data.post, postbody: data.postbody, posturl: data.posturl }));
return;
},
error: function() {
return console.log("Failed");
}
});
};
}
// Activates knockout.js
ko.applyBindings(new PostViewModel());
</script>
{% endblock %}
{% endblock %}
is there any way to run form.validate_on_submit() with request.json['....'] , I am unable to figure where I am going wrong.
I'm using an AJAX request to handle some part of my app that deals with managing photos...the user can click on a '<' or '>' button to change the ordering of the photos. All works well, but only for the first time I click the button...subsequent clicks do not trigger anything.
Main template:
<script type="text/javascript">
$(function() {
$(".manage_photo").click(function(event) {
event.preventDefault();
var id = $(this).attr("id");
var action = $(this).attr("name");
var data = { id: id, action: action };
$.ajax({
type: "POST",
url: "{% url managePhotos %}",
data: data,
success: function(results) {
$("#list").html(results);
},
});
})
})
</script>
....
{% if photos %}
<p><strong>{{ photos.count }} photo(s) added</strong></p>
<div class="highslide-gallery">
<div id="list">
{% include "ajax/photos.html" %}
</div>
</div>
<div class="cleaner"></div>
{% else %}
<h5>Photos not yet added</h5>
{% endif %}
Code for ajax/photos.html:
{% for photo in photos %}
<div class="vehicle_photo">
<button class="manage_photo" name="incr" id="{{ photo.id }}"
{% if forloop.first %} disabled="disabled"{%endif %} style="float: left">
<
</button>
<button class="manage_photo" name="decr" id="{{ photo.id }}"
style="float: right">
>
</button>
<br />
<a href="{{ photo.original_image.url }}" class="highslide"
onclick="return hs.expand(this)">
<img class="ui-corner-all" src="{{ photo.thumbnail_image.url }}" />
</a>
<br />
{{ photo.position_number }}
</div>
{% endfor %}
My view returns a render_to_response version of photos.html after changing the ordering for the selected photo, with results containing the queryset for all photos in that photo's set, and a status message ie. success, failed:
return render_to_response('ajax/photos.html', results)
What could be my issue? I tried the suggestions at: this SO question, but none work out for me. Any insight would be very much appreciated since I've been at this since yesterday.
when you do the $(function...) things that are in the DOM get bound, but when you replace stuff with other stuff, the new stuff is not bound. You could use the .live command to make it work for old and new stuff, or you could bind the new stuff again (run the $(".manage_photo").click(...) again after the ajax.
BTW, you could replace the block of ajax with a simple $("#list").load("{% url managePhotos %}")