I am trying to implement a like button to my blog post using Ajax, I am facing an error which I am unable to fix so I started from scratch more than once with the following steps:
Created Post model and Like model
Added the views which is currently working perfectly fine when I click like and unlike
I took the like section from the post_detail.html and added it in a separate html like_section.html
added the ajax with the correct values
Now my problem is that when I press the like button a new page is opened with ajax output
{"form": "....(html and ajax codes repeated)......"}
My question is how do I fix this error so that I can submit a like without refreshing the page?
Here are the models for Posts.py
class Post(models.Model):
---------other attributed like: title, content, author, date_posted, slug-----
likes = models.IntegerField(default=0)
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
Here are the views:
def like_post(request):
user = request.user
if request.method == 'POST':
post = request.POST.get('post_id')
post_obj = Post.objects.get(id=post)
current_likes = post_obj.likes
if user in post_obj.liked.all():
post_obj.liked.remove(user)
current_likes = current_likes - 1
else:
post_obj.liked.add(user)
current_likes = current_likes + 1
post_obj.likes=current_likes
post_obj.save()
like, created = Like.objects.get_or_create(user=user, post_id=post)
if not created:
if like.value == 'Like':
like.value = 'Unlike'
else:
like.value = 'Like'
like.save()
context = {
'post': post,
}
if request.is_ajax:
html = render_to_string('blog/like_section.html',context, request=request)
return JsonResponse({'form': html})
Here is the post details.html
<!-- Like -->
<div id="like-section">
{% include 'blog/like_section.html' %}
</div>
<!-- Like -->
Here are the like-section.html
<form action="{% url 'blog:like-post' %}" method="POST" class="like-form" id="{{post.id}}">
{% csrf_token %}
<input type="hidden" name="post_id" value='{{post.id}}'>
{% if user not in post.liked.all %}
<button id="like" value='{{post.id}}' class="bwhite sm-button" style="color: grey; background-color: Transparent; background-repeat:no-repeat; border: none; cursor:pointer; overflow: hidden; outline:none;">
<i class="far fa-thumbs-up" type="submit"></i>
</button>
{% else %}
<button id="like" value='{{post.id}}' class="bwhite sm-button" >
<i class="far fa-thumbs-up" type="submit"></i>
</button>
{% endif %}
<div class="like-count{{post.id}}">{{ post.num_likes }} Likes</div>
</form>
Here is the ajax:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript"></script>
<script>
$(document).ready(function(event){
$(document).on('click', '#like', function(event){
event.preventDefault();
var pk = $(this).attr('value');
$.ajax({
type: 'POST',
url: '{% url "blog:like-post" %}',
data: {'post_id':pk, 'csrfmiddlewaretoken': '{{ csrf_token }}'},
dataType: 'json',
success: function(response){
$('#like-section').html(response['form'])
console.log($('#like-section').html(response['form']));
},
error: function(rs, e){
console.log(rs.responseText);
},
});
});
</script>
Here is the urls.py
path('blogs/like', like_post, name='like-post'),
You are returning an HTML page as response from your view, I think that might be causing it. First just try returning something like {"msg","worked"} and see if its works
Related
I have a page where I load 2 files. After a click to the load button, this page reload to display a major part of the data, allowing the user to modified it. After a click to lunch button, I want to launch the process and I want to send to another page the path of results files to allow the user to download it.
My problem is after clicking on lunch button and send data to the other page. I have 2 versions :
The first one, send data to the result page, but I do not find a way to take back in the view the data modified by the user, the ajax seems to be ignored because of the type "summit" for the button launch :
<body>
<section class="bg-light py-5 border-bottom">
<div class="container px-5 my-5 px-5">
<div>
<h1 class="display-5 fw-bolder mb-2"> Convert to Dose </h1>
<br>
</div>
<form id="formCTD" action="{% url 'json:convertToDose' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
##here some fields to modified one of them following
<div class="row gx-5 justify-content-center">
<div class="col-lg-4 col-xl-4">
{% if factors %}
<input name="img_path" readonly
class="form-control" {{ pathAnalyseForm.img_path }}>
{% else %}
<input id="btn-submit-form-jsonfile"
class="btn btn-lg" {{ formImgFile.imgFile }}>
{% endif %}
</div>
</div>
<div class="text-center">
<div class="d-grid">
{% if factors %}
<button class="btn btn-primary btn-lg" id="launchButton" type="submit">
Launch
</button>
{% else %}
<button class="btn btn-primary btn-lg" id="submitButton" type="submit">
Load
</button>
{% endif %}
/div>
</div>
</form>
</div>
</section>
</body>
And the js block :
<script type="text/javascript">
$(document).ready(function () {
$("#launchButton").on('submit', function(e) {
e.preventDefault();
var form = $("#formCTD").get(0) //recup en html
// Est-ce que le formulaire est valide ?
console.log("valid? ")
if (form.checkValidity()) {
console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ")
nam = $("input[name='img_path']").val()
json = $("input[name='json_path']").val()
console.log(nam)
data = {"img_path" : nam,
"json_path": json}
console.log("bef ajax")
$.ajax({
url: "/filmchromique/convertToDose/",
type: "POST",
data: data,
beforeSend: function (xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", $('input[name="csrfmiddlewaretoken"]').val());
},
success: function (response) {
console.log("ok")
},
error: function(xhr, status, error) {
alert(xhr.responseText);
}
})
console.log("after ajax")
}
});
});
</script>
And the view :
def launchCtd(request):
if request.method == 'POST':
#2 after click load
if bool(request.FILES.get('calibrationFile', False)) == True and bool(request.FILES.get('imgFile', False)) == True :
#do some test ok
context = {
'factors': True,
'factorsCalib': factorsCalib,
'formCtd': formCtd,
'formJsonFile': formJsonFile,
'formImgFile': formImgFile,
'pathAnalyseForm': pathAnalyseForm,
'doseRect': doseRect,
'ctrlRect': ctrlRect,
}
return render(request, 'convertToDose.html', context)
after click lunch
else:
if request.is_ajax:
print ("here")#check
img_path = request.POST.get("img_path")
doseRectStr = request.POST.getlist('form[]')
json_pactrlRectth = request.POST.get("json_path")
method = request.POST.get("analyse_type")
if method == 'rb':
#init var
if method == 'multi':
#init var
img_out_path, json_pactrlRectth = functionTOLaunch()
context = {
'filename': img_out_path,
'protocol_file': json_pactrlRectth,
}
return render(request, 'result.html', context)
#1 load init
else:
formCtd = CtdForm()
formJsonFile = JsonFileForm()
formImgFile = ImgFileForm()
context = {
'factors': False,
'formCtd': formCtd,
'formJsonFile': formJsonFile,
'formImgFile' : formImgFile,
}
return render(request, 'convertToDose.html', context)
and the result page is a basic TemplateView.
In this first case, console.log in the ajax are not printed, I do not understand why and I supposed the function is not called (and so the ajax part)
in the second version, views are identical but I modified this on the html :
<button class="btn btn-primary btn-lg" id="launchButton" type="button">
Launch</button>
and this in the js part :
$("#launchButton").on('click', function(e) {....}
Data are sended to the view, I can read it but when I do the render to display the result page with out process data, nothing append... I supposed I have to implement something in the succes ajax part, but I do not understand what I have to do. I supposed I have to implement a new ajax request, to send the new context to the result page but how I take back the context sended by the render in the success ajax ... I'am totaly new and lost on this tech
Thanks for reading :)
I creating a django program using 'google-map api'.
'mapinit(json-data)' is read from 'map.js'
<body onload="mapinit('{{ data }}')">
<!-- create a map area with shop information -->
<div class="wrapper">
<div id="shopinfomation"></div>
<div id="map_canvas1" style="width: 500px; height: 500px"></div>
</div>
<!-- post action from html is ok! -->
<h3>category 1</h3>
<form class="specific_form" action="{% url 'index_search' '1A' %}" method="POST">
{% csrf_token %}
<input type="submit" class="btn-flat-border" value="ramen" /> search ramen shops
</form>
outer javascript have some action such as ajax, redirect.
Right now I am writing the url directly.
// get a shop detail
$.ajax({
type: "POST",
url: "/gmarker/search/detail/" + json.shops[i]['place_id'],
beforeSend: function(xhr, settings) {
xhr.setRequestHeader("X-CSRFToken", Cookies.get('csrftoken'));
}
}).done(function(data){
shopinfomation.innerHTML = 'ajax success. shop information...';
}).fail(function(){
shopinfomation.innerHTML = 'ajax error.';
});
I want using this.
console.log("{% url 'index_search' '2' %}")
I want.
/gmarker/search/2
but chrome output is ... Come is same one.
{% url 'index_search' '2' %}
urlpatterns = [
path('', views.index, name='index'),
path('search/<str:searchcode>', views.index, name='index_search'),
path('search/detail/<str:place_id>', views.searchdetail, name='detail_search'),
path('result/<str:searchcode>', views.index, name='index_result'),
]
outer javascript is can't read template tag??
Yes, external javascript cannot use template variables. What you need to do is to send data from internal javascript like. In mymap.html
<script>
var TemplateVar = {
my_url: "{% url 'index_search' '2' %}"
}
</script>
<script src="{% static 'js/map.js' %}"></script>
Now, in map.js
my_url = TemplateVar.my_url; // Taking json attribute from script of mymap.html
console.log(my_url); // You should get what you are expecting
Extra: You may need to escape variables to prevent some attacks
I have a form that keeps track of when people enter/leave different areas. Whenever there is a discrepancy, for example, someone forgets to "leave" an area before entering a new one, the user is prompted an "estimate" of the time they believe they left the previous area at(edited_timestamp). The only two required fields on the main form are the employee number and the work area, as these are used to verify/keep track of data.
When I try to reproduce the situation that would make the modal show up, it works, but when I attempt to submit it, I get these messages:
and these are the errors that are being returned.
Now, while I don't understand why the "Enter valid date/time" error is showing, I'm guessing the other two errors are due to the main form requiring the employee_number and the work_area and probably for this request, even though it is updating by the ID, it still wants the other two fields.
I guess my question is, how could I modify this so that these two fields are not required for the modal?
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, help_text="Work Area", related_name="work_area")
station_number = models.ForeignKey(Station, on_delete=models.SET_NULL, null=True, help_text="Station", related_name="stations", blank=True)
edited_timestamp = models.DateTimeField(null=True, blank=True)
time_exceptions = models.CharField(max_length=2, blank=True, default='', choices=EXCEPTION_STATUS)
time_in = models.DateTimeField(help_text="Time in", null=True, blank=True)
time_out = models.DateTimeField(blank=True, help_text="Time out", null=True)
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', 'edited_timestamp')
urls.py
urlpatterns = [
url(r'enter-exit-area/$', views.enter_exit_area, name='enter_exit_area'),
url(r'update-timestamp-modal/(?P<main_pk>\d+)/$', UpdateTimestampModal.as_view(), name='update_timestamp_modal'),
]
(Took out leave_area code for redundancy)
views.py
def enter_exit_area(request):
form = WarehouseForm()
enter_without_exit = None
exit_without_enter = None
if request.method == 'POST':
temp = request.POST.copy()
form = WarehouseForm(temp)
if form.is_valid():
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
if 'enter_area' in request.POST:
new_entry = form.save()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(work_area=area) & Q(time_out__isnull=True) & Q(time_in__isnull=True)) & (Q(station_number=station) | Q(station_number__isnull=True))).update(time_in=datetime.now())
# If employee has an entry without an exit and attempts to enter a new area, mark as an exception 'N', meaning they're getting the modal
enters_without_exits = EmployeeWorkAreaLog.objects.filter(Q(employee_number=emp_num) & Q(time_out__isnull=True) & Q(time_exceptions="")).exclude(pk=new_entry.pk).order_by("-time_in")
if len(enters_without_exits) > 0:
enter_without_exit = enters_without_exits[0]
enters_without_exits.update(time_exceptions='N')
message = 'You have entered %(area)s' % {'area': area}
if station is not None:
message += ': %(station)s' % {'station': station}
messages.success(request, message)
form = WarehouseForm()
return render(request, "operations/enter_exit_area.html", {
'form': form,
'enter_without_exit': enter_without_exit,
})
class UpdateTimestampModal(CreateUpdateModalView):
main_model = EmployeeWorkAreaLog
model_name = "EmployeeWorkAreaLog"
form_class = WarehouseForm
template = 'operations/modals/update_timestamp_modal.html'
modal_title = 'Update Missing Time'
enter_exit_area.html
{% extends "base.html" %}
{% load core_tags staticfiles %}
{% block head %}
<script src="{% static "js/operations/warehouse_enter_exit.js" %}"></script>
{% endblock head %}
{% block main %}
{% if enter_without_exit %}
<div id="auto-open-ajax-modal" data-modal="#create-update-modal" data-modal-url="{% url "operations:update_timestamp_modal" enter_without_exit.id %}" class="hidden"></div>
{% endif %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<div>
<div style="color: red">{{ form.employee_number.errors.as_text }}</div>
<label>Employee</label>
{{ form.employee_number }}
</div>
<div>
<div style="color: red">{{ form.work_area.errors.as_text }}</div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div style="color: red">{{ form.station_number.errors.as_text }}</div>
<div>
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
{% modal id="create-update-modal" title="Update Timestamp" primary_btn="Submit" default_submit=True %}
update_timestamp_modal.html
{% load core_tags %}
<form id="create-update-form" method="post" action="{% url "operations:update_timestamp_modal" main_object.id %}">
{% csrf_token %}
<label>Update</label>
<div class="row">
<div class="form-group col-xs-6">
{% standard_input form.edited_timestamp datetimepicker=True hide_label=True %}
</div>
</div>
</form>
warehouse_enter_exit.js
$(function () {
// Submit the edited timestamp form when they click the "Submit" button in the modal
$(document).on('click', '#update-timestamp-modal-btn-primary', function (e) {
e.preventDefault();
forms.ajaxSubmit($('#create-update-form'), function (data) {
if (data.success && data.redirect) {
window.location.href = data.redirect;
} else {
if (data.warning) {
messages.warning(data.warning);
} else {
messages.error("An error occurred when saving this timestamp, please try again.");
}
}
});
});
});
Could I maybe edit the JS to only update the edited_timestamp field? Or maybe a way I can edit the views so that only that ID is accessed to update that field? The URL to the modal access it based on ID so I thought there could be a way to edit only based on this field.
You'll need to override those fields in your form code.
Like this:
class WarehouseForm(AppsModelForm):
employee_number = forms.ModelChoiceField(queryset=EmployeeWorkAreaLog.objects.all(), required=False)
work_area = forms.CharField(required=False)
class Meta...
I have a problem with my Symfony2 Website. I have a search function which work fine in production but not in wamp.
My twig file :
{% extends "::base.html.twig" %}
{% block title %}Membres en attente de validation{% endblock %}
{% block body %}
{% if confirmation != null %}<div id="confirmationValidation">Nouveau tutoré accepté : {{ confirmation }}</div>{% endif %}
<form id="form_recherche_validation" action="{{ path('paces_user_validation_recherche') }}" method="post">
{{ form_widget(form) }}
<input type="submit" value="Rechercher" />
</form>
<!-- Loading -->
<div class="center-align">
<div class="preloader-wrapper small active loading_validation">
</div>
</div>
<div id="resultats_recherche_validation">
{% include 'PACESUserBundle:Validation:liste.html.twig' with {'tutores' : tutores} %}
</div>
{% endblock %}
Problem is that search is working thanks to a script :
$(".loading_validation").hide();
$("#resultats_recherche_validation").hide();
$("#form_recherche_validation").submit(function() {
$(".loading_validation").show();
$("#resultats_recherche_validation").show();
var numero = $("#paces_user_validationRechercheForm_numero").val();
var nom = $("#paces_user_validationRechercheForm_nom").val();
var prenom = $("#paces_user_validationRechercheForm_prenom").val();
var DATA = {numero: numero, nom: nom, prenom: prenom};
var request = $.ajax({
type: "POST",
url: "{{ path('paces_user_validation_recherche')}}",
data: DATA,
success: function(data) {
$('#resultats_recherche_validation').html(data);
$(".loading_validation").hide();
}
});
return false;
});
This script is normally loaded in the page's twig file. In wamp, all scripts doesn't work except those I put in my base.html.twig. So I created a custom.js file which loads the scripts for the whole website.
According to Chrome developer tools, it should be a route problem :
No route found for "POST /validation/%7B%7B%20path('paces_user_validation_recherche')%7D%7D" (from "http://localhost/trunk/web/validation/")
I searched for a whole day but I don't find an answer.
Do you have an idea ?
I'm trying to add an upvote button using ajax + django. Following this example, I was able to set up the following, but nothing happens when I click "upvote". (runserver is not getting any requests)
views.py
#login_required
def upvote(request):
recommendation = None
if request.method == 'GET':
recommendation = request.GET['recommendation']
if recommendation:
recommendation.votes.up(user)
return HttpResponse(upvotes)
urls.py
url(r'^upvote/$', coreviews.upvote, name='upvote'),
html
<p><strong id="like_count">Vote Count: {{ recommendation.get_total_votes }}</strong> people like this category
{% if user.is_authenticated %}
<button id="upvotes" data-recommendation="{{ recommendation }}" class="btn btn-primary" type="button">
<span class="glyphicon glyphicon-thumbs-up"></span>
Like
</button>
{% endif %}
</p>
ajax.js
$('#upvotes').click(function(){
var recommendation;
recommendation = $(this).attr("data-recommendation");
$.get('/upvote/', {recommendation: recommendation}, function(data){
$('#like_count').html(data);
$('#upvote').hide();
});
});
This is essentially what I'm trying to make the button do (works in python shell):
>>> import core.models as coremodels
>>> var1 = coremodels.Recommendation.objects.get(title="test5")
>>> var1.get_total_votes()
0
>>> var1.votes.up(user)