Passing url parameter on django modelform attributes - javascript

I need to create a form with 5 select fields, each one dependent of the previous one (Schools, Disciplines, Macro-Content, Micro-Content, Teacher)
I'm able to get only one working, passing an Ajax view to the form div:
<form action="" id="orderForm" method="POST" discipline-queries-url="{% url 'ajax-load-discipline' %}>
and
<script src="//code.jquery.com/jquery-3.6.0.js"></script>
<script>
$("#institute").change(function () {
const url = $("#orderForm").attr("discipline-queries-url");
const instituteId = $(this).val();
$.ajax({
url: url,
data: {
'institute_id': instituteId
},
success: function (data) {
$("#discipline").html(data);
}
});
});
</script>
From what I understand I'd need 4 different ajax views, one for each database query, and pass the url attribute through modelform attributes like this:
'discipline': forms.Select(attrs={'id': 'discipline', 'class': 'form-control',
'discipline-queries-url': '{% url "ajax-load-discipline" %}'}),
But the special characters are escaping and returning something like this:
"GET /order/create/%7B%%20url%20%22ajax-load-discipline%22%20%%7D?institute_id=1 HTTP/1.1" 404 13568
So it is passing {% url "ajax-load-discipline" %} to the url...
Any idea of what I can do to pass the correct parameters?

When you set attributes for a widget, it does not get rendered as if it were part of the template, it gets rendered as if it were a variable being rendered in the template, hence instead of "{% url 'ajax-load-discipline' %}" you should be writing the url itself there using reverse_lazy [Django docs]:
from django.urls import reverse_lazy
'discipline': forms.Select(attrs={'id': 'discipline', 'class': 'form-control', 'discipline-queries-url': reverse_lazy('ajax-load-discipline')}),

Related

Modify twig variable through js without reloading page

I have an array of objects which are rendered through for loop in twig template. I want to apply a filter-like mechanism where after selecting a category from a filter, list of elements in twig would update without refreshing a page and contain elements only from chosen category.
Until now I managed to pass chosen category value through ajax to Symfony controller, parse it, encode to JS and send it as a response and fetch it via ajax.
Here's some code:
//variable passed to twig
return $this->render('#Ad/ad_list.html.twig', [
'adList' => $adList,
]);
//An ajax call
$.ajax({
url : $form.attr('action'),
type: "POST",
data : filters,
complete: function(html) {
console.log(html['responseJSON'])
}
});
//creating a response
$response = JsonResponse::fromJsonString($jsonContent);
$response->prepare($request);
$response->send();
{% for ad in adList|slice(0, 9) %}
...
{% endfor %}
Is it possible to update passed variable ($adList) to twig via JS, so it would render elements from response?
Thanks to brombeer and DarkBee help I achieved my goal. I did it by:
Creating new template which holds list of elements,
Rendering this new template in main template and passing there updated value
main template:
<section>
{% include '#Ad/ad_list_container.html.twig' %}
</section>
ad_list_container template:
<div id="ad-list">
{% for ad in adList %}
...
{% endfor %}
</div>
ajax call:
$.ajax({
url : $form.attr('action'),
type: "POST",
data : filters,
success: function(response) {
$('#ad-list').html(response);
}
});
php controller:
return $this->render('#Ad/ad_list_container.html.twig', [
'adList' => $adList
]);

How to handle AJAX POST request in Django

I have a sort of twitter like button function in my app such that, when the button is clicked, it triggers an AJAX call and performs the action specified in the views. However, when i click the button, it does not perform action in views. The code reaches the 'like view' but does not execute anything after 'if request.POST:'. Please help.
Menu.html
<form action="{% url 'like'%}" id="plt_{{menu.id}}" data-id="{{menu.id}}" method="post">
{%csrf_token%}
<input name="menu_id" type="hidden" value="{{ menu.id }}">
<div class="like-button" id="btn_{{menu.id}}"></div>
</form>
<script>
$('.like-button').on('click', function () {
var id = $(this).attr('id');
id = id.replace('btn_','');
$(this).toggleClass('animate').promise().done(function () {
var link = $("#plt_"+id).attr('action')
$.ajax({
type: 'POST',
url: link,
headers: {'X-CSRFToken': '{{ csrf_token }}'},
})
});
});
</script>
Views.py
def like(request):
print('reached') //this prints
if request.POST:
menu = Menu.objects.get(pk=request.POST.get('menu_id'))
//execute code to like
return HTTPResponse('')
Maybe you want to check
if request.is_ajax() and request.method== "POST":
request.POST is a dict .Empty here because body is empty in your request.
Empty dicts are treated like False by python like
if {}:
print("Hello World")
Above won't print anything
But below works
if {"hi" : "there"}:
print("Hello World")
And docs suggests this check is wrong if request.POST:
It’s possible that a request can come in via POST with an empty POST
dictionary – if, say, a form is requested via the POST HTTP method but
does not include form data. Therefore, you shouldn’t use if
request.POST to check for use of the POST method; instead, use if
request.method == "POST" (see HttpRequest.method).
It is fairly simple, use serialize() of jquery. Serialize function will take all the values from the form, even csrftokenmiddleware which is hidden input type. So, doing so you will be able to handle post request successfully. Use sthg like this:
<script>
$('.like-button').on('click', function () {
var id = $(this).attr('id');
id = id.replace('btn_','');
$(this).toggleClass('animate').promise().done(function () {
var link = $("#plt_"+id).attr('action');
var data = $("#plt_"+id).serialize(); // It will serialize all form data
$.ajax({
type: 'POST',
url: link,
data: data
});
});
});
</script>
In views.py do as you do for other request. serialize()

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>

AJAX not updating database query as intended

I have pages that use both django context variables and json data. The first page is the "master page" that just displays all available data. The other pages however, are loaded upon submission of a dropdown form where the user can select a specific account to view. When the user selects an account and submits the dropdown form, two things are designed to happen:
The form action goes to a django view function that makes several queries to the database, then passes context variables upon rendering the template. Next however Chart data is supposed to be loaded via Ajax from a certain url that returns a JSONResponse with the data. The chart data seems to load fine for the "master" page, since all data is shown when I visit the url where the data is sent. Upon selecting a specific account however, I want to pass a request to a url to filter the database based on the selection, thereby producing specific chart data. This is the part that doesn't seem to work, as when I select a specific account, I cant seem to get the chart data for only that specific account to show. Here is my code:
urls.py
from django.conf.urls import url, include
from . import views
from .views import get_master_data, get_specific_data
views.py
### SELECTED METER DATABASE DATA PULL ###
def get_specific_data(request, *args, **kwargs): ### This isn't working as intended ###
if request.method == 'POST':
selected_meter = request.POST.get('selected_meter')
usage_data = list(MeterData.objects.filter(name=selected_meter).order_by('date').values_list('usage', flat=True))
#### A bunch of other similar queries ####
data = {
"usage_data": usage_data,
#### corresponding parts for other queries not shown ####
}
return JsonResponse(data)
else:
usage_data = list(MeterData.objects.order_by('date').values_list('usage', flat=True))
##### A bunch of other similar queries #####
data = {
"usage_data": usage_data,
#### corresponding parts for other queries not shown ####
}
return JsonResponse(data)
template.html
// This script is what gets the chart data. This works on the master page //
<script>
var categories = [];
var data = [];
var endpoint = '/properties/api/chart/data/'
$.ajax({
method: "GET",
url: endpoint,
success: function(data){
categories = data.dates
usageData = data.usage_data
OldCostsData = data.oldCosts_data
NewCostsData = data.newCosts_data
OldRatesData = data.oldRates_data
NewRatesData = data.newRates_data
savingsData = data.savings_data
Chart.defaults.global.defaultFontFamily = "Gotham-Black";
var usageCanvas = document.getElementById("usageChart");
var ratesCanvas = document.getElementById("ratesChart");
var costsCanvas = document.getElementById("costsChart");
var savingsCanvas = document.getElementById("savingsChart");
}
});
</script>
// This is supposed to post the dropdown menu selection on submission before loading the new template thus querying specific chart data //
<script>
var meterForm = $('#property-select');
var specificDataUrl = '/properties/api/specific-chart/data/';
meterForm.submit(function(event){
event.preventDefault();
$.ajax({
type: "POST",
url: specificDataUrl,
data: { selected_meter: $('#property-select option:selected').val() }, // data to be sent with the post request
success: function(data) {
console.log(data)
},
});
});;
</script>
<!-- This is the form -->
<form method="POST" action="/properties/property-selected/" id="property-select">
{% csrf_token %}
<select name="meters" class="dropdown-content">
{% for meter in user_meters %}
<option value="{{ meter }}">{{ meter.name }}</option>
{% endfor %}
</select>
<input type="submit" value="Submit" class="submit-button" style="margin-top:30px;"/>
</form>
I'm not really sure why the specific data query for charts data isn't working. Any help would be much appreciated, thanks!
In your Javascript you're posting the request with a parameter called selected_meter:
data: { selected_meter: $('#meter-option').val() }
But your view is asking for a post parameter called selection:
selected_meter = request.POST.get('selection')
Update your view or the Javascript to make the parameter names the same, e.g.
selected_meter = request.POST.get('selected_meter')

how to use ajax function to send form without page getting refreshed, what am I missing?Do I have to use rest-framework for this?

I'm trying to send my comment form using ajax, right now when user inserts a comment then whole page gets refreshed. I want this to be inserted nicely without page getting refreshed.
So I tried bunch of things but no luck. since I'm a beginner, I tried to follow many tutorial links;
https://realpython.com/blog/python/django-and-ajax-form-submissions/
https://impythonist.wordpress.com/2015/06/16/django-with-ajax-a-modern-client-server-communication-practise/comment-page-1/#comment-1631
I realize my problem is that I have a hard time manipulating my code in views.py and forms.py
Thus before doing a client side programming(js and ajax) I need to set my backend(python code) again to be set for the ajax.
Can someone please help me with this?
I don't know how to set my backend....
<div class="leave comment>
<form method="POST" action='{% url "comment_create" %}' id='commentForAjax'>{% csrf_token %}
<input type='hidden' name='post_id' value='{{ post.id }}'/>
<input type='hidden' name='origin_path' value='{{ request.get_full_path }}'/>
{% crispy comment_form comment_form.helper %}
</form>
</div>
<div class='reply_comment'>
<form method="POST" action='{% url "comment_create" %}'>{% csrf_token %}
<input type='hidden' name='post_id' id='post_id' value='{% url "comment_create" %}'/>
<input type='hidden' name='origin_path' id='origin_path' value='{{ comment.get_origin }}'/>
<input type='hidden' name='parent_id' id='parent_id' value='{{ comment.id }}' />
{% crispy comment_form comment_form.helper %}
</form>
</div>
<script>
$(document).on('submit','.commentForAjax', function(e){
e.preventDefault();
$.ajax({
type:'POST',
url:'comment/create/',
data:{
post_id:$('#post_id').val(),
origin_path:$('#origin_path').val(),
parent_id:$('#parent_id').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success:function(json){
I don't know what to do here...I tried it but failing....what is going on here })
this is my forms.py
class CommentForm(forms.Form):
comment = forms.CharField(
widget=forms.Textarea(attrs={"placeholder": "leave your thoughts"})
)
def __init__(self, data=None, files=None, **kwargs):
super(CommentForm, self).__init__(data, files, kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
self.helper.add_input(Submit('submit', 'leave your thoughts', css_class='btn btn-default',))
and my views.py
def comment_create_view(request):
if request.method == "POST" and request.user.is_authenticated() and request.is_ajax():
parent_id = request.POST.get('parent_id')
post_id = request.POST.get("post_id")
origin_path = request.POST.get("origin_path")
try:
post = Post.objects.get(id=post_id)
except:
post = None
parent_comment = None
if parent_id is not None:
try:
parent_comment = Comment.objects.get(id=parent_id)
except:
parent_comment = None
if parent_comment is not None and parent_comment.post is not None:
post = parent_comment.post
form = CommentForm(request.POST)
if form.is_valid():
comment_text = form.cleaned_data['comment']
if parent_comment is not None:
# parent comments exists
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=parent_comment.get_origin,
text=comment_text,
post = post,
parent=parent_comment
)
return HttpResponseRedirect(post.get_absolute_url())
else:
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=origin_path,
text=comment_text,
post = post
)
return HttpResponseRedirect(post.get_absolute_url())
else:
messages.error(request, "There was an error with your comment.")
return HttpResponseRedirect(origin_path)
else:
raise Http404
I'm still very shaky on the idea of using ajax even after reading a tutorial about it...how json comes into play and how I should modify views.py too....can someone please explain how they all group together?and help me getting this done...
else:
raise Http404
see official document of submit() and serialize() and modify your ajax all like this :
<script>
$('#commentForAjax' ).submit(function(e){
e.preventDefault();
$.ajax({
type:'POST',
url:'comment/create/', // make sure , you are calling currect url
data:$(this).serialize(),
success:function(json){
alert(json.message);
if(json.status==200){
var comment = json.comment;
var user = json.user;
/// set `comment` and `user` using jquery to some element
}
},
error:function(response){
alert("some error occured. see console for detail");
}
});
});
At backend side you are returning HttpResponseRedirect() which will redirect your ajax call to some url(status code=302). I will suggest to return any json response.
For Django 1.7+ add line from django.http import JsonResponse to return json response
For pre Django 1.7 use return HttpResponse(json.dumps(response_data), content_type="application/json")
Modify this portion of your views.py to return Json response
def comment_create_view(request):
# you are calling this url using post method
if request.method == "POST" and request.user.is_authenticated():
parent_id = request.POST.get('parent_id')
post_id = request.POST.get("post_id")
origin_path = request.POST.get("origin_path")
try:
post = Post.objects.get(id=post_id)
except:
# you should return from here , if post does not exists
response = {"code":400,"message":"Post does not exists"}
return HttpResponse(json.dumps(response), content_type="application/json")
parent_comment = None
if parent_id is not None:
try:
parent_comment = Comment.objects.get(id=parent_id)
except:
parent_comment = None
if parent_comment is not None and parent_comment.post is not None:
post = parent_comment.post
form = CommentForm(request.POST)
if form.is_valid():
comment_text = form.cleaned_data['comment']
if parent_comment is not None:
# parent comments exists
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=parent_comment.get_origin,
text=comment_text,
post = post,
parent=parent_comment
)
response = {"status":200,"message":"comment_stored",
"user":new_comment.user,
"comment":comment_text,
}
return HttpResponse(json.dumps(response), content_type="application/json")
else:
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=origin_path,
text=comment_text,
post = post
)
response = {"status":200,"message":"new comment_stored",
"user":new_comment.user,
"comment":comment_text,}
return HttpResponse(json.dumps(response), content_type="application/json")
else:
messages.error(request, "There was an error with your comment.")
response = {"status":400,"message":"There was an error with your comment."}
return HttpResponse(json.dumps(response), content_type="application/json")
You don't have to use rest-framework. But if you use rest-framework for this purpose , it will be easy to implement.
This is general structure of your comment app. I am assuming you are using Django REST Framework
- Comment
- models.py
- forms.py
- views.py
Comment Model (models.py)
from django.db import models
class Comment(models.Model):
user = models.ForeignKey(MyProfile)
post = models.ForeignKey(Post)
parent = models.ForeignKey("self")
text = models.TextField()
path = ...
...
Comment Form (forms.py)
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('text', 'post_id', 'parent_id')
post_id = forms.HiddenInput()
parent_id = forms.HiddenInput()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['text'].label = _('Comment')
self.fields['post_id'].value = self.instance.post.id
self.fields['parent_id'].value = self.instance.parent.id
self.helper = FormHelper()
self.helper.form_show_labels = False
self.helper.layout = Layout(
ManageFieldsWrapper(
crispy_fields.Field('text')
),
crispy_fields.SubmitField(submit_label='Leave your thoughts')
)
Comment form view and api view (views.py)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import HttpResponseRedirect
from django.views.generic.edit import CreateView
from .forms import CommentForm
from .models import Comment
class CommentFormView(CreateView):
form_class = CommentForm
class CommentAPISubmitView(APIView):
def post(self, request, format=None):
#... Your checks goes here ...
form = CommentForm(request.POST or None)
if form.is_valid():
#... Your saving logic here ..
return HttpResponseRedirect(redirect_url)
else:
return HttpResponseRedirect(origin_path)
return Response(status=status.HTTP_400_BAD_REQUEST)
Finally client side code AJAX/JQuery
$(document).ready(function() {
$("#commentForAjax").submit(function( event ) {
$.ajax({
type:'POST',
url:'comment/create/',
data:{
post_id:$('#post_id').val(),
origin_path:$('#origin_path').val(),
parent_id:$('#parent_id').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success: function(response){
}
});
event.preventDefault()
});
});
As per your current code It seems like it will always get redirect because after validating the comment form and updating it into database you are returning HttpResponseRedirect which is designed to redirect.
I think what you want is to update the comment into database and get a success response.
So to achieve this you need to change the response type, I would suggest return JsonResponse and based on the json response you can update the html as well, I am sure returning json response won't cause redirect your html.
Let me know if it's clear to you.
Thanks.
After completely reviewing your code and discussing with OP in length. I've managed to resolve the OPs issue.
After removing HttpResponseRedirect I first converted it to JsonResponse and made changes accordingly.
response_data = {
"status":200, "message":"comment_stored",
"parent": True,
"parent_id": parent_comment.id,
"comment_count": parent_comment.comment_count()
}
return JsonResponse(response_data)
Next step was to simply perform DOM manipulation to display the data fetched from the response. But turns out this was more complicated than expected. So, to simplify it I simply separated the template into 2 parts - one will be the main part and the other containing the HTML of only the comment.
Using django.template.loader.render_to_string I generated the required HTML to display the comment and sent with the response as a string in JSON.
html = render_to_string('main/child_comment.html',
{'comment': [new_comment],
'user': request.user,
'comment_form':comment_form
})
response_data = {
"status":200, "message":"comment_stored",
"comment":html,
"parent": True, "parent_id": parent_comment.id,
"comment_count": parent_comment.comment_count()
}
return JsonResponse(response_data)
Finally, after minor changes (not relevant to the current issue) mainly in the DOM manipulation scripts and in one of the form models, the code worked as expected.
$('form').submit(function(e) {
e.preventDefault();
if ($(this).parents("tr") != 0) {
parent_id = $(this).parents("tr").attr("id").split("_")[1];
data_str = $(this).serialize() + "&parent_id=" + parent_id;
} else {
data_str = $(this).serialize();
}
$(this).parents("tr").attr("id").split("_")[1]
$.ajax({
type: 'POST',
url: '{% url 'comment_create' %}',
data: data_str,
success: function(json) {
alert(json.message);
if (json.status == 200) {
var comment = json.comment.trim();
var user = json.user;
if (!json.parent) {
$(comment).insertBefore('.table tr:first');
} else {
$(comment).insertBefore('#comment_' + json.parent_id + ' #child_comment:first');
$(".replies").text("답글" + json.comment_count + "개 모두 보기");
}
}
},
error: function(response) {
alert("some error occured. see console for detail");
}
});
});
BONUS: There was another minor issue which we had faced but I won't discuss it here. I've written a separate answer for it.
The main thing you need to do for preventing page reloading on form submit is calling event.preventDefault() in your form submit event handler.
#Vibhu provided a very good code example in the answer above. That's exactly what you need to do on client side. (I provide his code with a single difference, request is sent to 'comment/create-ajax', not to 'comment/create'
$(document).ready(function() {
$("#commentForAjax").submit(function( event ) {
$.ajax({
type:'POST',
url:'comment/create-ajax',
data:{
post_id:$('#post_id').val(),
origin_path:$('#origin_path').val(),
parent_id:$('#parent_id').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success: function(response){
}
});
event.preventDefault()
});
});
On server side, you may provide one more controller (view in Django terms) which handles POST /comments/create-ajax, it should look somehow like this:
def handle_comment_creation_via_ajax(request):
comment_dict = json.loads(request.body)
# or json.loads(request.body.decode("utf-8")) if you use python 3
comment = CommentModel(**comment_dict) # This will work if comment has no nested models, otherwise, implement static method in your model class which takes dict and returns model instance.
try:
comment.save()
except Exception as e:
return JsonResponse({'success':false, 'message': str(e)}, status_code=400)
return JsonResponse(model_to_dict(instance, fields=[], exclude=[])
JsonResponse and model_to_dict
Good luck!
P.S. Note that incoming model must be validated before save
For the sake of example, I would like to show how to achieve this using Django REST Framework and how much code you DON'T need to change.
TL;DR
Installing DRF doesn't break anything. Just add 8 lines of code (without imports), change 2 existing lines and get rid of your entire comment_create_view.
For those who are interested in more details, please read further.
1. Install django-rest-framework using this guide.
2. Create a serializers.py file with the following contents
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__' # or specify the list of fields you want to be present
Here you define the class that will serialize (transform a Comment model instance to a json object) and deserialize (inverse action of transforming a json object into a Comment instance) your comments.
3. Add the following view to your views.py
from rest_framework.generics import CreateAPIView
class CommentCreateView(CreateAPIView):
queryset = Comments.objects.all()
serializer_class = CommentSerializer
Note: These 4 lines of code actually substitute your whole comment_create_view.
Here you define a generic view designed specifically for creation of objects. CreateAPIView will handle only POST requests and will use the CommentSerializer class to convert objects. A serializer class to Django REST framework is what a form class is to Django - it handles the validation of data and returns a response in form of json, or corresponding error messages (also in json) in case your data is not correct.
4. Add the following to your main urls.py file
url_patterns = [
... # your urls here
url(r'^api/v1/comments/$', CommentCreateView.as_view(), name='comments-list')
]
Here you register your API view as a route to which you can send requests.
5. Edit your ajax request
$.ajax({
type:'POST',
url:'api/v1/comments/', // switch to the API view url
contentType: 'application/json', // tell ajax to send it as `json` content
data:{
post_id:$('#post_id').val(),
origin_path:$('#origin_path').val(),
parent_id:$('#parent_id').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success:function(json){
You POST to the newly created API endpoint data in form of json and your serializer takes it and creates a Comment model instance from it that is saved to the database. In case you need some specific behavior while creating a Comment (or any other model) instance, you can override the .create() method of your CommentSerializer. For more details check the Django REST framework tutorial.
6. Do whatever you need with the newly created comment
This part applies to non Django REST framework scenarios as well. Once you've successfully created the comment, in your success function you will receive it in json form and depending on what you want to do with it, you need to define the desired behavior in this success function.
Basically that's it. Please take into account that the example described here is a minimal required code to make it work for you. I've used the out-of-the-box Django REST framework features, but of course it has many more possibilities to make things work. Maybe you'll need to override some default methods, but in the end, because DRF is designed to deal with ajax calls, your code will be shorter and cleaner.
Good luck!
This is the easiest example of how to implement Ajax forms in conjunction with Django:
You can use Ajax Post to send JSON to Django and then handle the arguments as a dict(). Here is an example:
In browser (JQuery/JavaScript):
function newModule() {
var my_data = $("#my_element").val(); // Whatever value you want to be sent.
$.ajax({
url: "{% url 'modules' %}", // Handler as defined in Django URLs.
type: "POST", // Method.
dataType: "json", // Format as JSON (Default).
data: {
path: my_data, // Dictionary key (JSON).
csrfmiddlewaretoken:
'{{ csrf_token }}' // Unique key.
},
success: function (json) {
// On success do this.
},
error: function (xhr, errmsg, err) {
// On failure do this.
}
});
In server engine (Python):
def handle(request):
# Post request containing the key.
if request.method == 'POST' and 'my_data' in request.POST.keys():
# Retrieving the value.
my_data = request.POST['my_data']
# ...
If the form is submitting it is not your backend code but your javascript code, you need to prevent the form from submitting, you can try changing this:
<script>
$(document).on('submit','.commentForAjax', function(e){
e.preventDefault();
To this:
<script>
$(document).ready(function(){
$("#comentForAjax").submit(function(e){
e.preventDefault();
// Do whatever you need to do, like serializing the form and post with $.ajax
});
});
So make sure you are binding the submit event right after the document loads so the browser knows that the form should not be submitted, and then you just make your ajax call and do whatever you need with the returned serialized data from the server.
Again, if the form is posting, it doesn't have anything to do with the backend code.
Note: For jquery selectors, if you need to select something by id you use the #element hash sign and if you want to target class selectors then you use the dot notation .elements. Also note that you can in theory select just one element by ID (given that you don't duplicate the ID for other elements) but classes are usually used to span several elements that share the same attributes.
In your javascript, you call for the class .commentForAjax.
You should call the ID:
$(document).on('submit','#commentForAjax', function(e){
// ...
});
Hope this helps.
R
In your form tag below
<form method="POST" action='{% url "comment_create" %}' id='commentForAjax'>{% csrf_token %}
You don't need to define action or method. You handle them via ajax. So delete them and you form won't try to execute that action.And hopefully it will solve your refresh page issue.Also change your on submit line '.commentForAjax' into '#commentForAjax' since it is not a class but id.
$(document).on('submit','.commentForAjax', function(e){
e.preventDefault();
// Put
e.stopPropagation();

Categories