Show more comments using ajax with Django - python-3.x

With Django I made a Show more comments button using ajax. This system works, but the problem is that there are form fields in the comments I brought in this way, and when I click the more button, csrf_token does not appear in this comment field. As such, I get a csrf_token error when I submit the form. I leave my codes below.
To solve this problem, I ran the form with the get method, but as such, the function in the views directs me to the page with JsonResponse. Another solution was to give the header information csrf_token, but that didn't solve the problem either.
blog_detail.html
<script>
$(document).ready(function(){
$('#load_form').submit(function(e){
e.preventDefault();
var limit = $(this).attr('limit')
var page = document.getElementById('pagination')
var blog_comment_list = $('#blog-comment-list')
var serializedData = $(this).serialize();
$.ajax({
type:'GET',
url: "{% url 'load-more-comments' %}",
data : serializedData,
success: function(response){
blog_comment_list.html(response.comment_html)
if (page.value >= limit){
$('#submit_button').hide()
}
page.value = parseInt(page.value)+1
},
})
})
})
</script>
<div id="blog-comment-list">
{% include 'front_end/blog/comment/partial-comment.html' %}
</div>
<form method="GET" id="load_form" limit="{{num_pages}}">
<input type="hidden" name="pk" value="{{details.id}}">
<input type="hidden" name="page" value="2" id="pagination">
<input type="submit" name="load" value="Load More" id="submit_button">
</form>
partial-comment-html
<div class="media-holder mt-5">
<h3 class="title mb-3">All Comments</h3>
{% for item in comments %}
<div class="infinite-container">
<div class="media mb-5">
<img class="img-fluid rounded-circle box-shadow mr-4" alt="image" src="{{item.owner.get_image_or_default}}" style="width: 100px;height: 100px;">
<div class="media-body">
<h6>{{item.owner.name}} {{item.owner.surname}}</h6>
<br>
<small><span style="font-size:14px;" class="stars-container stars-{% widthratio item.rate.rate 1 20 %}" id="stars">★★★★★</span></small>
<div class="comment-date"> <span class="date">{{item.created_date|naturaltime}}</span>
</div>
<p>{{item.content}} </p>
<div align="center">
{% if item.comments.all %}Cevapları Görüntüle ({{item.comments.count}}){% else %}Cevap Ver{% endif %}
</div>
<div class="generic-comment" id="generic-comment-id-{{item.id}}" style="display:none;">
<!-- -->
<div class="infinite-generic-comment">
<form action="{% url 'comment-answer' %}" method="POST" id="answer_form">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{item.id}}">
<div class="row">
<div class="col-9">
<input type="text" placeholder="Cevap Ver" name='generic_comment' class="form-control">
</div>
<div class="col-2">
<input type="submit" value="Cevap Ver" class="btn btn-outline-primary">
</div>
</div>
</form>
{% for comment in item.comments.all %}
<div class="media mb-2">
<img class="img-fluid rounded-circle box-shadow mr-4" alt="image" src="{{item.owner.get_image_or_default}}" style="width: 100px;height: 100px;">
<div class="media-body">
<h6>{{comment.owner.name}} {{comment.owner.surname}}</h6>
<div class="comment-date"> <span class="date">{{comment.created_date|naturaltime}}</span>
</div>
<p>{{comment.content}} </p>
</div>
</div>
{% endfor %}
</div>
<!-- -->
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<script>
var aTag = document.getElementsByClassName('show-answers')
for (let i = 0; i< aTag.length; i++){
aTag[i].addEventListener('click', (event) => {
var generic = document.getElementById(`generic-${event.target.id}`)
if (generic.style.display == 'none'){
$(`#generic-${event.target.id}`).slideDown('slow')
}
else{
$(`#generic-${event.target.id}`).slideUp('slow')
}
})
}
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(document).ready(function(){
$('#answer_form').submit(function(e){
e.preventDefault()
var serializedData = $(this).serialize()
var url = $(this).attr('action')
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
},
//headers: { "X-CSRFToken": getCookie("csrftoken") }
});
$.ajax({
type:'POST',
url: url,
data:serializedData,
success: function(response){
if (response.success){
$('#answer_form').trigger('reset')
Swal.fire(
'Başarılı!',
'Cevap onaya gönderildi',
'success'
)
}
}
})
})
})
</script>
views.py
def answer_comment(request):
if request.method=='POST':
comment = Comment.objects.get(id=request.POST.get('comment_id'))
comment.comments.create(owner=request.user, content=request.POST.get('generic_comment'))
return JsonResponse({'success':True},status=200)
return JsonResponse({'success':False}, status = 403)
Note: I tried the csrf_exempt decorator function, but still could not solve the problem.
The methods I use are available in the script tag in partial-comment-html. How can I solve this problem. As I said, when I press the load more button, the necessary form fields to respond to a comment are just below these comments and when load more with ajax, the csrf_token information is deleted in these form fields, and nothing happens in new comments.
Note 2: When {{csrf_token}} is entered, I added the hidden input field with javascript myself, but Django realized that I added it and prevented me.

Related

NodeJS Route not working. "Assignment to constant variable." Error

I am trying to set up a Shop page for my website. I have a navigation bar with 7 items. Each item works. They all work via a RestFUL API (Node server, routes, and controllers...). So I have it set up exactly the same for all pages. But for the shop page it won't work, not sure why. Thanks for all input.
Route
`
router.post('/addshopitem', encodedParser, function(req, res) {
console.log("okay step 2");
const name = req.body.name;
const desc = req.body.desc;
const price = req.body.price;
const stock = req.body.stock;
const createdby = req.body.createdby;
console.log("okay step 2 almost");
ShopController.add_item(req,res,name,desc,price,stock,createdby);
console.log("okay step 2 done");
});
Controller
exports.add_item = (req, res, name, desc, price, stock, createdby) => {
const Item = require('../models/item');
const item = new Item(1, name, desc, price, stock, createdby);
const response = item.save();
if (response) {
res.render('shopitemadded');
} else {
res.render('error');
}
}
NAV-Item
<div class='dropdown'>
<button class='dropbtn' id='shop' onclick="window.location.href='/shop';">Shop</button>
<div class='dropdown-content'></div>
</div>
`
Shop.ejs (View)
`
<body>
<div class='nav-div'>
<%- include('./partials/nav.ejs') %>
</div>
<div class='content' id='content-one'>
<div class="grid">
<%- include('./partials/item.ejs') %>
</div>
</div>
<div class='content' id='content-two'>
</div>
<div class='content' id='content-three'>
</div>
</body>
`
Item.ejs (Partial View Element)
`
<% items.forEach(function(item) { %>
<div class="card"><img class="card__img" src="" alt="Canyons" />
<div class="card__content">
<h1 class="card__header"><%=item.name%></h1>
<p class="card__text"><%=item.desc%></p>
<form action="/shop/addtobasket" method="post">
<input type="text" name="iditem" value="2" style="display: none;">
<label for="amount">2
<input type="text" name="amount" value="3">
</label>
<p>Anzahl an Lager: <%=item.stock%></p>
<p>Stückpreis: <%=item.price%></p>
<input class="card__btn" type="submit" name="submit" value="Dem Warenkorb hinzufügen">
</form>
</div>
</div>
<% })%>
`
I am trying to set up a shop page. I did it the exact same way as all the other views (with a route and a controller). Only thing different: The ejs View has a partial (item.ejs) with a for each inside. Maybe I messed something up in there. Not sure.

Find a specific word in text, and add link(Vuejs)

In res.data.tweets i am fetching tweets from server and storing values for every tweet in tweetData obj. In tweet_text, i would like to add a link to the tagged twitter users(clickin on tag will redirect to twitter profile). I figured out how do i find tagged users but i am not able to add a link either with .link or .href attribbute.
axios.post(API_URL, null, { params }).then(res => {
this.currentPage = res.data.page
this.numberOfPages = res.data.numberOfPages
res.data.tweets.forEach(tweet => {
const tweetData = {
id: tweet.id,
tweet_text: tweet.tweet_text,
twitter_name: tweet.twitter_name,
twitter_username: tweet.twitter_username,
added_at: moment(String(tweet.added_at)).format('MM/DD/YYYY hh:mm'),
}
this.tweets.push(tweetData)
// Below this line i am trying to achieve something by adding a link
this.tweets.forEach(el => {
el.tweet_text.split(' ').forEach(word => {
if (word.includes('#')) {
// word = 'this is tag'
word.link('https://twitter.com')
console.log(word.link)
}
})
})
})
})
This is component where data is shown
<div class="tweets-container">
<div
v-for="tweet in tweets"
:key="tweet.id"
>
<div class="tweet-card">
<div class="username-time">
<div class="user-info">
<p class="name">
{{ tweet.twitter_name }}
</p>
<p class="user-info-p">
#
</p>
<p class="username">
<a
:href="'https://twitter.com/' + tweet.twitter_username"
class="twitter_link"
target="_blank"
>
{{ tweet.twitter_username }}
</a>
</p>
</div>
<div class="time">
<p>{{ tweet.added_at }}</p>
</div>
</div>
<div class="text">
<p>{{ tweet.tweet_text }}</p>
</div>
</div>
</div>
</div>
I don't think it's going to work this way.
You should use a computed property which will append the username to the twitter base link. Then, in your template, you can link the href property to the computed property.
computed: {
userLink: function () {
return "http://twitter.com/" + this.tweet.twitter_username
}
}
You can now use this "userLink" directly in your template.
<p class="username">
<a
:href="userLink"
class="twitter_link"
target="_blank"
>
{{ tweet.twitter_username }}
</a>
</p>
I didn't test the code I wrote, feel free to let me know if you need further advice ;)

How to include an ejs in the script tag

I want to render ejs using the forEach loop inside the script tag but I get product is not defined error. I cant pass variable into ejs.render() function correctly.
Here is my ejs template for product card:
<div class="card mb-3" style="max-width: 540px">
<div class="row no-gutters">
<div class="col-md-4">
<img
src="/images/<%= product.image_groups[0].images[0].link%>"
class="card-img"
alt="<%= product.image_groups[0].images[0].alt%>"
/>
</div>
<div class="col-md-8">
<div class="card-body d-flex flex-column">
<h5 class="card-title"><%= product.name %></h5>
<p class="card-text">
<%- product.short_description %>
</p>
<div class="card-footer bg-transparent border-dark mt-auto">
<div class="row">
<div class="col sm-6">
<p>Price:<%= product.currency %> <%= product.price %></p>
</div>
<div class="col sm-6">
More Info
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Here is my script:
<script>
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
// get values
const productName = form.productname.value
const searchResultsEl = document.getElementById('searchResults')
try {
const res = await fetch("/search", {
method: "POST",
body: JSON.stringify({ productName }),
headers: { "Content-Type": "application/json" },
})
const foundProducts = await res.json()
foundProducts.data.forEach(product => {
let html = ejs.render("<%- include('../product/productCard.ejs') %>",{product:product})
searchResultsEl.innerHTML += html
})
} catch (err) {
console.log(err)
}
});
</script>
Error: product is not defined
I can print products by using console.log(product) so there are products. I cant figure out what is the problem. Any help?
Image of Error:
Try
let html = ejs.render('<%- include("../product/productCard.ejs") %>',{product:product})

Ajax function not working properly for delete and update comments

I'm using Ajax in a comments section in my blog project. It was working fine however clearly I've changed something because now when I go to edit a comment it won't work and when I try to delete, it does destroy the comment but it also gets rid of a load of other comments until I refresh the page when everything looks fine. The only Ajax function now working is create new comment.
Here's my show page code for my comments section:
<!--================== COMMENTS DISPLAY SECTION ====================================================================-->
<div id="comments">
<% blog.comments.forEach(function(comment){ %>
<div class="comment-container">
<div class="jumbotron comment">
<div class="row">
<div class="col-md-1">
<img class="comment-ico" src = "<%=comment.author.image%>">
</div>
<div class="col-md-7">
<h4><%=comment.author.username%></h4>
</div>
</div>
</div>
<div><p><%=comment.text%></p></div>
<!--=================EDIT COMMENT FORM =========================================================================-->
<form id="edit-comment-form" action = "/blogs/<%= blog._id %>/comments/<%=comment._id%>?_method=PUT" method = "POST" id="newComment">
<textarea class = "form-control" rows="4" name = "comment[text]"><%=comment.text%></textarea>
<button class = "btn btn-lg btn-primary btn-block">Submit</button>
</form>
<!--==================================================================================================================-->
<!-- if current user is the same as author -->
<% if(currentUser && currentUser.username == comment.author.username) { %>
<div class="row">
<div class="col-md-1 choice">
<a class="edit">Edit</a>
</div>
<div class="col-md-1">
<form id = "delete-form" action = "/blogs/<%= blog._id %>/comments/<%=comment._id%>?_method=DELETE" method = "POST">
<input type = "submit" class = "button-delete" value = "Delete"></form>
</div>
<% } %>
<% if(currentUser && currentUser.username == comment.author.username) { %>
<div class="col-md-1 choice-report">
<a class="report">Report</a>
</div>
<% } else { %>
<div class="col-md-1 choice-no-user">
Report
</div>
<% } %>
</div>
<br>
<hr class = "style-three">
<% }) %>
</div>
</div>
</div>
<!--==================================================================================================================-->
<% if(currentUser){ %>
<div class = "container-form">
<form action = "/blogs/<%= blog._id %>/comments" method = "POST" id="newComment">
<div class="row">
<div class="col-md-2">
<img class="newComment-ico" src = " <%=currentUser.image%>">
</div>
<div class="col-md-10">
<label for="comment">Add comment</label>
</div>
</div>
<textarea class = "form-control" rows="4" placeholder = "Type comment here..." name = "comment[text]"></textarea>
<button class = "btn btn-lg btn-primary btn-block">Submit</button>
</form>
</div>
<% } %>
And my Ajax code:
// update comment
$('#comments').on('submit', '#edit-comment-form', function(e){
e.preventDefault();
// get info from form
var formData = $(this).serialize();
var formAction = $(this).attr('action');
var $originalItem = $(this).parent('.comment-container');
$.ajax({
url: formAction,
data: formData,
type: 'PUT',
originalItem: $originalItem,
success: function(data) {
var blog_id = location.pathname.replace("/blogs/", "");
this.originalItem.html(
`
<div class="comment-container">
<div class="jumbotron comment">
<div class="row">
<div class="col-md-1">
<img class="comment-ico" src = "${data.author.image}">
</div>
<div class="col-md-7">
<h4>${data.author.username}</h4>
</div>
</div>
</div>
<div><p>${data.text}</p></div>
<form id="edit-comment-form" action = "/blogs/${blog._id}/comments/${data._id}?_method=PUT" method = "POST" id="newComment">
<textarea class = "form-control" rows="4" name = "comment[text]">${data.text}</textarea>
<button class = "btn btn-lg btn-primary btn-block">Submit</button>
</form>
<div class="row">
<div class="col-md-1 choice">
<a class="edit">Edit</a>
</div>
<div class="col-md-1">
<form id = "delete-form" action = "/blogs/${blog._id}/comments/${data._id}?_method=DELETE" method = "POST">
<input type = "submit" class = "button-delete" value = "Delete"></form>
</div>
<div class="col-md-1 choice-report">
<a class="report">Report</a>
</div>
</div>
<br>
<hr class = "style-three">
`
);
}
});
});
And here's the update comments route:
// comment update route
router.put("/:comment_id", function(req, res){
Comment.findByIdAndUpdate(req.params.comment_id, req.body.comment, {new: true}, function(err, updatedComment){
if(err) {
res.redirect("back");
} else {
if(req.xhr);
res.json(updatedComment);
// } else {
// res.redirect("/blogs/" + req.params.id);
// }
}
})
})
My destroy Ajax code:
// delete comments asynchonously
$('#comments').on('submit', '#delete-form', function(e){
e.preventDefault();
var confirmResponse = confirm('Are you sure you want to delete this comment?');
if(confirmResponse){
var actionURL = $(this).attr('action');
$itemToDelete = $(this).closest('.comment-container');
$.ajax({
url: actionURL,
type: 'DELETE',
itemToDelete: $itemToDelete,
success: function(data){
this.itemToDelete.remove();
}
})
} else {
$(this).find('input').blur();
}
})
And destroy route:
// comments destroy route
router.delete("/:comment_id", function(req, res){
Comment.findByIdAndRemove(req.params.comment_id, function(err, comment){
if(err) {
res.redirect("back");
} else {
res.json(comment);
}
})
})
UPDATE:
I noticed a couple of errors on my Ajax code whereby I had referenced ${blog._id} rather than ${blog_id}. I've updated as follows:
// update comment
$('#comments').on('submit', '#edit-comment-form', function(e){
e.preventDefault();
// get info from form
var formData = $(this).serialize();
var formAction = $(this).attr('action');
var $originalItem = $(this).parent('.comment-container');
$.ajax({
url: formAction,
data: formData,
type: 'PUT',
originalItem: $originalItem,
success: function(data) {
var blog_id = location.pathname.replace("/blogs/", "");
this.originalItem.html(
`
<div class="jumbotron comment">
<div class="row">
<div class="col-md-1">
<img class="comment-ico" src = "${data.author.image}">
</div>
<div class="col-md-7">
<h4>${data.author.username}</h4>
</div>
</div>
</div>
<div><p>${data.text}</p></div>
<form id="edit-comment-form" action = "/blogs/${blog_id}/comments/${data._id} method = "POST" id="newComment">
<textarea class = "form-control" rows="4" name = "comment[text]">${data.text}</textarea>
<button class = "btn btn-lg btn-primary btn-block">Submit</button>
</form>
<div class="row">
<div class="col-md-1 choice">
<a class="edit">Edit</a>
</div>
<div class="col-md-1">
<form id = "delete-form" action = "/blogs/${blog_id}/comments/${data._id}?_method=DELETE" method = "POST">
<input type = "submit" class = "button-delete" value = "Delete"></form>
</div>
<div class="col-md-1 choice-report">
<a class="report">Report</a>
</div>
</div>
<br>
<hr class = "style-three">
`
);
}
});
});
Now the comment will update asynchronously, however until the page is refreshed, again it's kicking off a load of other comments.Essentially it's getting rid temporarily of all comments beneath the one being edited. When you refresh though, all comments re-appear in the correct order including the edits made to the comment in question

Vue.js - Working with Components

I am new to Vue.js. Very new so this question might sound a lot like a first graders. Forgive me.
I have the App.vue and Character.vue setup. I wanted to create characters on the fly and add them to an array (in App.vue) and let the rendering of the look/feel of the characters be done using Character.vue. The characters are created and added to the array and can be retrieved properly. Only thing is...Character.vue doesn't render them properly because for some reason, the character it retrieves from the array is undefined.
Help me?
Attaching files
App.vue
<template>
<div>
<div class='gameHeader'>
<h1>{{ gameHeader }}</h1>
</div>
<div class='gameMessages'>
<div class='gameMessage' v-for="msg in gameMessages">
{{ msg }}
</div>
</div>
<div class='gameMain'>
<button #click="rollNewCharacter">Roll New</button>
<div class="playerParty">
<character v-for="p in playerParty"></character>
</div>
<div class="computerParty">
<character v-for="c in computerParty"></character>
</div>
</div>
<div class='gameFooter'>
{{ gameFooter }}
</div>
</div>
</template>
<script>
import Character from "./assets/components/Character.vue";
export default {
components: { 'character': Character },
data: function(){ return { gameHeader: 'Monster Attack', gameMessages:[], playerParty: [], computerParty: [], gameFooter: '' }; },
methods: {
rollNewCharacter() {
var c = Character;
c.name = 'Usman';
c.type = 'Warrior';
c.mana = 100;
c.health = 100;
c.totalHealth = 100;
c.totalMana = 100;
console.log(c.name);
this.playerParty.push(c);
console.log(this.playerParty[0].name);
//this.computerParty.push(chr2);
}
}
}
</script>
Character.vue
<template>
<div class="character">
<span class='name'>{{ name }}</span><span class='type'>({{ type }})</span>
<div class='health'><div class='total' :class="totalHealth"><div class='health' :class="health"></div></div></div>
<div class='mana'><div class='total' :class="totalMana"><div class='mana' :class="mana"></div></div></div>
<span class='damage'>{{ damage }}</span>
<div class="actions">
<button #click="attack">Attack</button>
<button #click="specialAttack">Special Attack</button>
<button #click="heal">Heal</button>
</div>
</div>
</template>
<script>
export default {
props: [ 'name', 'type', 'mana', 'health', 'damage' , 'totalHealth', 'totalMana' ],
methods: {
attack: function(){},
specialAttack: function(){},
heal: function(){ alert(this.name); console.log(this);}
}
}
</script>
<style>
</style>
You should pass a prop when using the character component:
<character :character="p" v-for="p in playerParty"></character>
I have updated the character to receive only one prop:
export default {
props: [ 'character' ],
methods: {
attack: function(){},
specialAttack: function(){},
heal: function(){ alert(this.name); console.log(this);}
}
}
And this is the character component template with the new prop:
<template>
<div class="character">
<span class='name'>{{ character.name }}</span><span class='type'>({{ character.type }})</span>
<div class='health'><div class='total' :class="character.totalHealth"><div class='health' :class="character.health"></div></div></div>
<div class='mana'><div class='total' :class="character.totalMana"><div class='mana' :class="character.mana"></div></div></div>
<span class='damage'>{{ character.damage }}</span>
<div class="actions">
<button #click="attack">Attack</button>
<button #click="specialAttack">Special Attack</button>
<button #click="heal">Heal</button>
</div>
</div>
</template>

Resources