This my first time working with an api that returns a result. So I am using a post request to get information from the user in a form:
<form action="{{url_for('international.get_grade_data')}}" method="POST">
........
</form>
the endpoint looks like this:
#international.route("/grade_data", methods=["GET", "POST"])
#tfa_login_required
def get_grade_data():
if request.method == "POST":
grading_system_from= request.form.get('country_from')
grading_system_to = request.form.get('country_to')
users_grade= request.form.get('user_grade')
grade_request= {"free_grade": users_grade, "country_to": grading_system_to, "country_from": grading_system_from}
grade= grade_comparison_tool(grade_request)
return jsonify(
grade_request = grade
)
This data is sent to an api and then returns a result in JSON.
but when the user clicks on the submit button the user gets redirected to the json result.
I would like to get the result in the URL and then display the result on the webpage without redirect to the json result. How do I achieve this? I have gone online to know about POST and GET but not still clear.
Regarding the question I have been able to find a solution by changing the strategy,
firstly, I prevented the default action of the form:
<form action="#" onsubmit="return false" method="POST">
........
</form>
secondly, in the JS file I used an AJAX call to send the data from the form and received the result back in a click event:
function dataLoading(){
let datasetDict = {
"free_grade": free_grade.value,
"country_from": countryFrom.value,
"country_to": countryTo.value
}
$.ajax({
type: "POST",
url: '/international/grade_data',
data: JSON.stringify(datasetDict),
success: function(data_result, status){
console.log( JSON.stringify(data_result) + ': ' + status)
},
contentType: 'application/json;charset=UTF-8',
dataType: 'json'
});
}
document.getElementById('convert-button').addEventListener('click', dataLoading)
modified the flask python code:
#international.route("/grade_data", methods=["POST"])
#tfa_login_required
def get_grade_data():
if request.method == "POST":
user_grade_data =request.get_json()
grade = grade_analy(user_grade_data)
return jsonify(grade)
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')}),
In python, I have
#app.route('/update_account/<account_id>', methods=['POST'])
def update_account(account_id):
In html form, I have:
<form id="update_account_form" name="update_account_form" action="{{ url_for('update_account', account_id=account._id) | safe }}" method="POST">
URL: http://<domain>/edit_account/5b9fbe55fb6fc072da02e2f6
In AJAX, I have:
$(function () {
$('#update_account_form').submit(function (event) {
event.preventDefault();
account_id = 5b9fbe55fb6fc072da02e2f6
$.ajax({
url: '/update_account/'+account_id,
data: $('#update_account_form').serialize(),
type: 'POST',
success: function (response) {
My question is, how do I read the value of 5b9fbe55fb6fc072da02e2f6 from the url to use it in my AJAX call.
Do I use some jQuery regex system to read the url and work out the id, or is there some other clever way that I can pass the id from the html url via ajax to flask on the server side?
Use split after getting url.
$(function () {
$('#update_account_form').submit(function (event) {
event.preventDefault();
var url = window.location.href;
account_id = url.split("/").pop();
...........
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();
i have a html page, which contains a form and i want when the form is successfully submited, show the below div:
<div class="response" style="display: none;">
<p>you can download ithere</p>
</div>
i also have a jquery function:
<script type="text/javascript">
$(function() {
$('#sendButton').click(function(e) {
e.preventDefault();
var temp = $("#backupSubmit").serialize();
validateForm();
$.ajax({
type: "POST",
data: temp,
url: 'backup/',
success: function(data) {
$(".response").show();
}
});
});
});
</script>
and in my views.py (code behind) i create a link and pass it to html page. i have:
def backup(request):
if request.is_ajax():
if request.method=='POST':
//create a link that user can download a file from it. (link)
variables = RequestContext(request,{'link':link})
return render_to_response('backup.html',variables)
else:
return render_to_response('backup.html')
else:
return render_to_response("show.html", {
'str': "bad Request! :(",
}, context_instance=RequestContext(request))
backup = login_required(backup)
my problem: it seems that my view doesn't execute. it doesn't show me the link that i send to this page. it seems that only jQuery function is executed. i'm confused. how can i make both of them to execute(i mean jQuery function and then the url i set in this function which make my view to be executed.)
i don't know how to use serialize function. whenever i searched, they wrote that:
The .serialize() method creates a text string in standard URL-encoded notation and produces query string like "a=1&b=2&c=3&d=4&e=5.
i don't know when i have to use it, while i can access to my form field in request.Post["field name"]. and i don't know what should be the data which is in success: function(data) in my situation.
thank very much for your help.
You have to get and display the data from your ajax post function, where data is the response you render through your DJango server, for example:
t = Template("{{ link }}")
c = Context({"link": link})
t.render(c):
Your JS / jQuery should become something like this:
<script type="text/javascript">
$(function() {
$('#sendButton').click(function(e) {
e.preventDefault();
var temp = $("#backupSubmit").serialize();
validateForm();
$.ajax({
type: "POST",
data: temp,
url: 'backup/',
success: function(data) {
// 'data' is the response from your server
// (=the link you want to generate from the server)
// Append the resulting link 'data' to your DIV '.response'
$(".response").html('<p>you can download ithere</p>');
$(".response").show();
}
});
});
});
</script>
Hope this helps.