Django + AJAX comments - javascript

I'm trying to implement a comment system without reloading the page, but it doesn't work. Comments are inserted only after the page is reloaded, but AJAX does not work.
<!-- COMMENT -->
<div class="comment-section">
<form action="{% url 'love:comment_urls' %}" method="POST" class='comment-form'>
{% csrf_token %}
<input type="hidden" name="post_id" value={{i.pk}}>
{{ comment_form }}
<button type="submit" name="submit_c_form">
Опубликовать
</button>
</form>
</div>
<hr>
<div class="comment_set">
{% if i.quotes_comment.all %}
{% for com in i.quotes_comment.all %}
<b>{{ com.user }}:</b>
{{ com.body }}
{% endfor %}
{% endif %}
</div>
</div>
JS code
// COMMENT
$(document).ready(function () {
$('.comment-form').submit(function () {
event.preventDefault();
console.log($(this).serialize());
var url = $(this).attr('action')
$.ajax({
type: 'POST',
url: url,
data: $(this).serialize(),
dataType: 'html',
success: function (response) {
console.log('Заработало')
$('.comment-section').html(response['form']);
},
error: function (rs, error) {
console.log(rs, error)
}
})
})
})
#csrf_exempt
def create_comment(request):
profile = Profile.objects.get(user=request.user)
data = json.loads(request.body)
post_id = data['post_id']
body = data['body']
comment_form = CommentModelForm(data)
if comment_form.is_valid():
print('Form is valid')
instance = comment_form.save(commit=False)
instance.user = profile
instance.quotes = QuotesHonors.objects.get(
id=post_id)
instance.save()
comment_form = CommentModelForm()
return JsonResponse({
'content': body
})
return redirect("love:home_urls")
As I said, comments are inserted only after a reboot, but when I click publish, nothing happens. If you change the dataType to 'json', the parser error pops up. Can you explain what I'm doing wrong?

Hard to say what you are doing wrong overall there is code missing. But from what you shared, csrf_token is not in AJAX request header. And mainly, by using JQuery .serialize(), you are trying to pass data in URL-encoded notation and not as a JSON object.
This .serialize() output would be used to pass data as a query string by appending it to a base URL. And, at the view accessing it with request.GET.get('parameter'). Instead, what we want is to pass data as JSON and access it through request.body at view level.
Here is a full example:
models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
title = models.CharField(max_length=30)
body = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=timezone.now())
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
body = models.CharField(max_length=255)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments', blank=True)
created_at = models.DateTimeField(auto_now_add=timezone.now())
urls.py
from .views import post_detail, comment_create
app_name = 'love'
urlpatterns = [
path('post/<int:pk>/', post_detail, name='post-detail'),
path('comment/create/', comment_create, name='comment-create'),
]
Two views, one to render the template and another to handle the AJAX request. The tricky part was trying to find a way to format date in the same way as the template.
views.py
from django.utils.formats import date_format
from django.shortcuts import get_object_or_404
from django.http import JsonResponse
import json
def post_detail(request, pk):
post = get_object_or_404(Post, id=pk)
has_commented = post.comments.filter(user=request.user).exists()
context = {
'post': post,
'has_commented': has_commented
}
return render(request, 'post_detail.html', context)
def comment_create(request):
data = json.loads(request.body)
post_id = data['post_id']
comment_body = data['body']
post = get_object_or_404(Post, id=post_id)
comment = Comment.objects.create(
user=request.user,
body=comment_body,
post=post
)
created_at = date_format(comment.created_at, format='M. d, Y, h:m a', use_l10n=True)
return JsonResponse({
'user': comment.user.username,
'body': comment.body,
'created_at': created_at
})
In this first template part we just display data, note that {# % if not has_commented % #} is commented out, when first writing the code I limited the number comments to one per User.
post_detail.html (HTML & DTL):
{% extends 'base.html' %}
{% block content %}
<h1>{{post.title}}</h1>
<h4>{{post.created_at}}</h4>
<p>{{post.body}}</p>
<hr>
<div id="comment-div">
{# % if not has_commented % #}
<form action="{% url 'love:comment-create' %}" id="comment-form">
<input type="hidden" id="post-id" value="{{post.id}}">
<textarea id="comment-body" maxlength="255" rows="4" cols="50"></textarea>
<br>
<button type="submit" id="submit-comment">Comment</button>
</form>
<hr>
{# % endif % #}
</div>
<div id="comment-list">
<h2> Comments </h2>
{% if post.comments.all %}
{% for comment in post.comments.all %}
<p>At {{comment.created_at|date:"M. d, Y, h:m a"}} {{comment.user}} commented:</p>
<p>{{comment.body}}</p>
{% endfor %}
{% endif %}
</div>
This second part, containing the <script> is responsible for every event in the template after adding a new comment. The first function is used to retrieve csrftoken value from cookies.
Then, on submit click we collect data and send the AJAX request. Attention to a few lines. First, where csrf header name is set headers: {'X-CsrfToken': csrftoken}. Second, where data is converted to JSON string data: JSON.stringify({ post_id: post_id, body: body }).
post_detail.html (JS):
<script>
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// 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;
}
$('#submit-comment').click( function (e) {
e.preventDefault();
var url = $('#comment-form').attr("action");
var post_id = $('#post-id').attr("value");
var body = $('#comment-body').val();
const csrftoken = getCookie('csrftoken');
$.ajax({
method: "POST",
url: url,
headers: {'X-CsrfToken': csrftoken},
data: JSON.stringify({ post_id: post_id, body: body }),
success: function(comment) {
// $('#comment-div').prop("hidden", true);
$('#comment-body').val('');
$('#comment-list').append(
`<p>At ${comment.created_at} ${comment.user} commented:</p>
<p>${comment.body}</p>`
);
}
});
});
</script>
{% endblock %}
Lastly, on success, clear body input and update the template by appending data to <div id="comment-list">. To limit to one comment per user, uncomment $('#comment-div').prop("hidden", true);

Related

I want to add product in cart without refreshing page with AJAX and Django

Nothing will ne happened when I submit product in the the cart. I want to use AJAX without refreshing page.
When I submit the console message will be displayed.
I'm trying to use AJAX first.
Trying to add product in the cart without refreshing page.
I need help please :)
Views Django
def add_cart(request, product_id):
cart = Cart(request)
product = get_object_or_404(Product, id=product_id)
form = CartProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product,
quantity=cd['quantity'],
update_quantity=cd['update_qt']
)
return JsonResponse({'status': 'success'})
Form
from django import forms
from django.core.validators import MinValueValidator, MaxValueValidator
class CartProductForm(forms.Form):
quantity = forms.IntegerField(initial=1)
update_qt = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)
HTML Code
<form action="{% url "..." %}" method="post" data-id="{{ ... }}" class="form-order" id="form">
{{ cart_product_form }}
{% csrf_token %}
<a data-id="{{ ... }}" class="buy-product"><button>BUY</button></a>
</form>
JS Code
$(".buy-product").on('click', function(e){
e.preventDefault();
var product_id = $(this).attr('data-id')
var quantity = 1
console.log(product_id)
console.log(quantity)
data = {
'product_id': product_id,
'quantity': quantity
}
var point='/cart/add/'+product_id+'/'
$.ajax({
headers:{
'Content-Type':'application/json',
'X-CSRFToken':csrftoken,
},
url: point,
type: 'POST',
data: data,
success: function(data){
console.log('success')
console.log(csrftoken)
}
})
})
You need to add csrftoken in data: {csrfmiddlewaretoken':csrftoken}. Also I don't see when you initialize your variables, but I think is just example. Check this answer for more: link
You can prevent form from refreshig the page and then do whatever you need with your JS code:
<form action="{% url "..." %}" method="post" data-id="{{ ... }}" class="form-order" id="form" onsubmit="myFunction(event)">
...
</form>
<script>
function myFunction(event){
event.preventDefault();
}
</script>

Building a like post in flask using ajax and jquery fails

I am building a simple flask blog and there I am trying to implement like functionality using ajax and jquery. It works without ajax but the page reloads. So I need to use the ajax but when I am using ajax then I got my site redirected to the JSON file which is not expected. Please Help me with this. The code is
<div class="likers" id = 'result{{ slide.id }}'>
{% if current_user.has_liked_slide(slide) %}
<a class="unlike" id="unlike_{{slide.id}}" href="{{ url_for('like_action', slide_id=slide.id, action='unlike') }}">unlike</a>
{% else %}
<a class="like" id="like_{{slide.id}}" href="{{ url_for('like_action', slide_id=slide.id, action='like') }}">like</a>
{% endif %}
<div id = 'like-count'>
{{ slide.likes | count }}
</div>
</div>
#app.route('/like/<int:slide_id>/<action>', methods = ['GET', 'POST'])
#login_required
def like_action(slide_id, action):
slide = Slide.query.filter_by(id=slide_id).first_or_404()
if action == 'like':
current_user.like_slide(slide)
db.session.commit()
return jsonify({'slide_id': slide_id, 'action': action})
if action == 'unlike':
current_user.unlike_slide(slide)
db.session.commit()
return jsonify({'slide_id': slide_id, 'action': action})
$(document).ready(function(event) {
$('.like, .unlike').click(function(){
var id = this.id;
var split_id = id.split('_');
var text = split_id[0];
var slide_id = split_id[1];
$.ajax({
url: '/like/<int:slide_id>/<action>',
type: 'post',
data : {slide_id:slide_id, action:text},
dataType:'html',
success: function(data){
$("like_" + slide_id).html(data);
$('unlikes_' + slide_id).html(data);
}
});
});
});

My AJAX is working and I want to know why

In my app I made a favorite list that a user can populate using AJAX.
Is it working perfectly but I am not sure if I did it the right way and I would like to know how to do it the proper way. Or maybe somehow I did it right.
My html:
<div class='sub_button'>
<form class="add_btn" method='post'>{% csrf_token %}
<button class='added btn' value= '{{product.id }} {{ sub_product.id }}' ><i class=' fas fa-save'></i></button>
</div>
My AJAX:
$(".row").on('click', ".added", function(event) {
let addedBtn = $(this);
console.log(addedBtn)
event.preventDefault();
var product = $(this).val();
var url = '/finder/add/';
$.ajax({
url: url,
type: "POST",
data:{
'product': product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
addedBtn.hide();
}
});
});
My views.py
def add(request):
data = {'success': False}
if request.method=='POST':
product = request.POST.get('product')
user = request.user
splitted = product.split(' ')
sub_product = Product.objects.get(pk=(splitted[1]))
original_product = Product.objects.get(pk=(splitted[0]))
p = SavedProduct(username= user, sub_product=sub_product, original_product = original_product)
p.save()
data['success'] = True
return JsonResponse(data)
What is bugging me is the way I passed '{{product.id }} {{ sub_product.id }}'. Because I've made it after some tinkering.
There is a better way to do this.
ie, if you have only one product.id and sub_product.id in this page instance. Then in a script tag at the end of the HTML template for this page you add the following:
<script>
var product_id = {{ product.id }}
var sub_product_id = {{ sub_product.id }}
</script>
Then you can link the external JS file in which you have AJAX to this HTML template with another script tag.
Now your product_id and sub_product_id will be available in your external JS script file in which you have the AJAX code.
So that way, you don't have to pass it through the value attribute of the button tag and things look more clean.

Using Ajax to receive total amount due before submitting to database

Trying to use Ajax to calculate and display the sum before I submit anything to the database. At the moment this is what I have, but I can't seem to get anything to display. I'm trying to start small and see if I'm even able to display the values in the fields.. Is my view for final_price wrong? Or did I mess up somewhere in the Jquery? I'm still new to Ajax, so I'm not sure where I went wrong (following tutorials)
Book Price: $10 (input field)
Delivery charge: $3 (input field)
Delivery type: $5 (input field)
[calculate] [submit]
Final Result: $18 (result of adding the fields above when user clicks the calculate button)
calculate button will display $18
submit button will finalize the results and send the values to the database.
This is my model for the table
class book(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
book_price = models.IntegerField()
delivery_charge = models.IntegerField()
delivery_type = models.IntegerField()
final_result = models.IntegerField()
def save(self, *args, **kwargs):
self.final_result = self.book_price + self.delivery_charge + self.delivery_type
print(self.final_result)
super(book, self).save(*args, **kwargs)
views.py for the form
def final_price(request):
response_data = {}
if request.method == 'POST':
form = RequestForm(request.POST)
book_price = request.POST.get('book_price')
delivery_charge = request.POST.get('delivery_charge')
delivery_type = request.POST.get('delivery_type')
response_data['book_price'] = book_price
response_data['delivery_charge'] = delivery_charge
response_data['delivery_type'] = delivery_type
book.objects.create(book_price=book_price, delivery_charge=delivery_charge,delivery_type=delivery_type)
return JsonResponse(response_data)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
return redirect('final_price')
else:
form = RequestForm()
args = {'form': form}
return render(request, 'final_price.html', args)
Model for to allow me to edit the fields in my book module
class RequestForm(forms.ModelForm):
class Meta:
model = book
fields = (
'book_price',
'delivery_charge',
'delivery_type',
)
Html file
{% extends 'base.html' %}
{% block content %}
<form class="post" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" name="button">Submit</button>
</form>
<br>
<button onclick="myFunction()">Get Quote</button>
<script type="text/javascript">
function myFunction(){
$('.ajaxProgress').show();
$.ajax({
type: "POST",
url: "/final_price",
dataType: "json",
async: true,
data:{
csrfmiddlewaretoken: '{{ csrf_token }}',
book_p: $('#book_price').val(),
},
success: function(json){
$('#output').html(json.message);
$(.ajaxProgress).hide();
}
});
}
</script>
{% endblock %}
urls.py
urlpatterns = [
path('', views.home, name='home'),
path('final_price/', views.final_price, name='final_price'),
]

How to display the name that matches the ID without refreshing/submitting the form in Django?

I am trying to display a User's name on top of a box where they enter their Employee # in a form, without having to refresh the page.
For example, they enter their # and then after they click/tab onto the next field, it renders their name on top, which comes from the database, so the user knows they've entered the correct info. This name is stored in a separate model, so I try to retrieve it using the "id/number".
I am not too familiar with AJAX but after reading a few similar questions it seems like an AJAX request would be the most appropriate way to achieve this. I tried to make a function get_employee_name that returns the name of the person based on the way I saw another ajax request worked, but I'm not sure how to implement this so it displays after the # is entered.
My page currently loads, but there is never a call to the function/url that searches for the name to display it on the page (because there isn't one). I'm not sure where I might be missing the part that connects these two areas of the code or how to connect these, as I am not too familiar with html and Django, most of this has been trial and error.
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False)
station_number = models.ForeignKey(StationNumber, on_delete=models.SET_NULL, null=True, blank=True)
This is the model where the name is stored
alldata/models.py
class Salesman(models.Model):
slsmn_name = models.CharField(max_length=25)
id = models.IntegerField(db_column='number', primary_key=True)
I was reading I can add to the "attrs" in the widget an 'onchange' part, but I am not too familiar with how to approach this and tying it to the ajax request from forms and not the html.
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
views.py
def enter_exit_area(request):
form = WarehouseForm(request.POST or None)
if form.is_valid():
# Submission stuff/rules
return render(request, "operations/enter_exit_area.html", {
'form': form,
})
def get_employee_name(request):
employee_number = request.GET.get('employee_number')
try:
employee = Salesman.objects.get(id=employee_number)
except Salesman.DoesNotExist:
return JsonResponse({'error': 'Employee not found'}, status=404)
employee_name = employee.slsmn_name
return JsonResponse({'employee_name': employee_name})
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
path('get-employee-name/', views.get_employee_name, name='get_employee_name'),
]
The ajax request I tried to create is at the end of this html. I modified a similar request I found, but it does not actually display anything on the screen, not sure if I'm missing an area where the request is actually never being called, as I am not too familiar with how these types of requests work.
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<div>
<!-- Here is where I would want the name to render after the user enters their number and tabs out -->
{{ form.employee_number.help_text }}
{{ form.employee_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
</div>
</div>
</form>
<script>
$("#id_employee_number").change(function () {
var employee_number = $(this).val();
var url = $("#warehouseForm").data("employee-name");
$.ajax({
url: url,
type:'GET',
data: {
'id': employee_number
},
success: function (data) {
var employee_name = data['employee_name'];
$('#employee_name').text(employee_name);
},
error : function (data) {
var error_message = data['error'];
$('#employee_name').text(error_message);
}
});
});
</script>
{% endblock main %}
How can I call the function from the HTML? Could I do it in such a way that when the user enters 6 numbers it checks? (All employee numbers are 6 digits)
The url has to be the endpoint url which you defined in your url.py file
<script>
$("#id_employee_number").change(function (e) {
e.preventDefault();
var employee_number = $(this).val();
var url = $("#warehouseForm").data("employee-name");
$.ajax({
url: "/get_employee_name", // Here
type:'GET',
data: {
'id': employee_number
},
success: function (data) {
var employee_name = data['employee_name'];
$('#employee_name').text(employee_name);
},
error : function (data) {
var error_message = data['error'];
$('#employee_name').text(error_message);
}
});
});
</script>
You can append to your GET request a url parameter like: /get-employee-name/<your employee number here>
I recommend taking a look at URL Dispatcher to create url parameters within your django url definitions
I also recommend using underscores NOT dashes in your url definitions.
So a pseudo working config would be
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
path('get_employee_name/<int:employeeNum>', views.get_employee_name, name='get_employee_name'),
]
views.py
def get_employee_name(request, employeeNum): #You get the employeeNum variable from urls.py
try:
employee = Salesman.objects.get(id=employee_number)
except Salesman.DoesNotExist:
return JsonResponse({'error': 'Employee not found'}, status=404)
employee_name = employee.slsmn_name
return JsonResponse({'employee_name': employee_name})
script
<script>
$("#id_employee_number").change(function () {
var employee_number = $(this).val();
var employeeNum = $("#warehouseForm").data("employee-name");
$.ajax({
url: `/get_employee_name/${employeeNum}`,
type:'GET',
success: function (data) {
var employee_name = data['employee_name'];
$('#employee_name').text(employee_name);
},
error : function (data) {
var error_message = data['error'];
$('#employee_name').text(error_message);
}
});
});
</script>

Categories