There's this issue where I created a form using django. The form contains two charfields for name and address, and two datetimefields one for a start_date and the other for a stop_date. Finally there are two radio buttons, such that:
when the first is checked, the start_date field's value is set to date.now() using jquery's selector
when the second radio button is checked, the start_date and stop_date fields are disabled, still using jquery's selector.
The above code works well. However, when the start_date's value is successfully set, I have to post the form data using jquery ajax post method to a django view, the start_date's value is not posted at all, while the other field values are. I don't know how to go about this issue.
Here's my code:
template.html:
{% extends 'base.html' %}
<div class="center">
<form action="" invalidate>
{% crispy form form.helper %}
</form>
</div>
script.js:
$('input:radio').click(function){
if ($(this).val() === '1'){
var start_date = new Date();
$('#id_start_date').val(start_date);
}
else if ($(this).val() === '0'){ document.getElementById('id_stop_date').disabled = true;
document.getElementById('id_start_date').disabled = true;
}
}
$("form").on("submit", function (e){
var name = $("#id_name");
var type = $("id_type");
var start_date = $("id_start_date");
var stop_date = $("id_stop_date");
$.ajax({
type:"POST",
url:"{% url 'postAccount' %}",
data:{
csrfmiddlewaretoken: document.querySelector('input[name="csrfmiddlewaretoken"]').Val(),
name:name, type:type, start_date:start_date, stop_date: stop_date
},
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function (response){
var response = JSON.parse(response ['instance']),
console.log(response);
}
});
e.preventDefault();
});
views.py:
def postAccount(request):
if is_ajax(request) and request.method == 'POST':
form = AccountForm (request.POST)
if form.is_valid():
instance = form.save()
ser_instance = serializers.serialize('json', [ser_instance,])
return JsonResponse({"instance":ser_instance}, status=200)
return JsonResponse ({"error":""}, status=400)
Disabled inputs won't appear in request.POST - you might have better luck using the readOnly attribute instead, it will do basically the same thing but will still include the value in request.POST
Related
I am developing a webpage with filters to filter the results on the page.
A Ajax is called, which sends the filters to my Django back-end. The results are filtered and the data should be passed back to the front-end.
So now I need to pass my results of the models with context to the front-end. This leads to some problems.
My Ajax:
$(document).on('change', '#test-form', function (e) {
e.preventDefault()
var tags = [];
$('input[name="tags[]"]:checked').each(function(i){
return tags[i] = $(this).val();
});
$.ajax({
type: 'POST',
cache: false,
url: "{% url 'core:jobSearch_nosearch' %}",
data: {
tags: tags,
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
},
success: function(data) {
console.log('yey')
console.log(data)
}
});
});
Here my View:
from django.core.serializers.json import DjangoJSONEncoder
from django.utils.functional import Promise
class LazyEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, Promise):
return str(obj)
return super().default(obj)
def jobSearch(request, **search):
companies = Company.objects.all()
if request.method == 'POST':
ads = Ad.objects.all()
search_job = request.GET.get('search')
if search_job:
ads = Ad.objects.filter(title__contains=search_job)
tag_filter = request.POST.getlist('tags[]')
for tag in tag_filter:
print(tag)
ads = ads.filter(tag__name=tag)
print(ads)
context = {'companies': companies, 'ads': ads}
# context = {'companies': list(companies)}
# context = {'msg': 'Success'}
# return JsonResponse(serialize('json', ads, cls=LazyEncoder), safe=False)
return JsonResponse(context)
else:
ads = Ad.objects.all()
context = {'companies': companies, 'ads': ads}
return render(request, 'core/jobSearch.html', context)
As you can see I tried different things in the my view. This return JsonResponse(serialize('json', ads, cls=LazyEncoder), safe=False) passes the result of one model. But I have two models which I have to pass to the front-end.
Additionally, I would like to get the data and being able to use it with the html template language.
In this way: (example)
{% for a in ads %}
{% a %}
{% endfor %}
Is that even possible with Django and Ajax, or is there another way to filter results and passing them without reloading the page?
django template tag work on rendering html content and you can not pass argument after render page so after loading page you can not use
{% for a in ads %}
{% a %}
{% endfor %}
if you have not pass that arguments
you can use api and js for this work
i suggest you read about drf
you can do this work with api and js
I am trying to create an upvote system in my Django site using ajax so it would not refresh when users click the upvote button. In my site's main page with the listview of all the posts, I am able to get the ajax and the upvote request to work, but only for the top post. No matter if I click on an upvote button from another post, it would only register the effect on the top post. Where did I go wrong?
Below is my code.
models.py
User = settings.AUTH_USER_MODEL #this is the user model
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) #many to one relationship where many posts can be tied to one user
content = models.TextField(blank=True, null=True)
date_posted = models.DateTimeField(default=timezone.now)
image = models.ImageField(upload_to='trade_images', blank=True, null=True)
upvotes = models.ManyToManyField(User, blank=True, related_name='upvotes')
total_upvotes = models.IntegerField(default='0')
def get_absolute_url(self):
return reverse('main:post-detail', kwargs={'pk': self.pk}) #returns the url for individual posts
def __str__(self):
return self.content
class Upvote(models.Model):
user = models.ForeignKey(User, related_name='upvoted_user', on_delete=models.CASCADE)
post = models.ForeignKey(Post, related_name='upvoted_post', on_delete=models.CASCADE)
def __str__(self):
return str(self.user) + ':' + str(self.post)
views.py
# list of all posts
class post_list_view(ListView):
model = Post
template_name = 'main/home.html'
context_object_name = 'posts' # this is called from the html as 'for post in posts'
ordering = ['-date_posted'] # minus to reverse the date posted, so newer posts show up on top
paginate_by = 5 #sets pagination per page
return context
def upvote(request):
if request.POST.get('action') == 'post':
result = ''
id = int(request.POST.get('postid'))
post = get_object_or_404(Post, id=id) #grabbing the selected post using postid
new_relation = Upvote(user=request.user, post=post) #storing the upvote relation between user and post using Upvote model arguments - 'user' and 'post'
if post.upvotes.filter(id=request.user.id).exists(): #checking if user already has upvote relations with post by filtering user and post
post.upvotes.remove(request.user) #remove upvote relation from post
post.total_upvotes -= 1 #minus 1 total_upvotes from post
result = post.total_upvotes #storing the new total_upvotes into result
Upvote.objects.filter(user=request.user, post=post).delete() #filtering user and post and deleting the upvote table
post.save()
else:
post.upvotes.add(request.user)
post.total_upvotes += 1
result = post.total_upvotes
print('create upvote')
new_relation.save()
post.save()
return JsonResponse({'result': result }) # return the new total_vote count back to html as json
template.html
<div id="upvote-section" class="card-footer">
{% if user.is_authenticated %}
<form action="{% url 'main:upvote-post' %}" method=POST>
{% csrf_token %}
<button type="submit" class="btn btn-success btn-sm" id="upvote-btn" name="post_id" value="{{post.id}}">Upvote</button>
<button type="submit" class="btn btn-outline-success btn-sm" id="upvote-btn" name="post_id" value="{{post.id}}">Upvote</button>
<p id="total_upvotes">
{{ post.total_upvotes }}
</p>
</i> Comment
{% if post.user == user %}
Edit
Delete
{% endif %}
</form>
{% else %}
Upvotes ({{ post.total_upvotes }}) Please login to upvote or comment.
{% endif %}
</div>
script/ajax
<script>
$(document).on('click', '#upvote-btn', function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url "main:upvote-post" %}',
data: {
postid: $('#upvote-btn').val(),
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
},
success: function (json) {
document.getElementById("total_upvotes").innerHTML = json['result']
},
error: function (xhr, errmsg, err) {
console.log(err)
}
});
})
</script>
The problem lies in your AJAX script. Specifically in your data object,
data: {
postid: $('#upvote-btn').val(),
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
}
Since you're selecting the postid using $('#upvote-btn').val(), you are always sending back the value for the first upvote button.
Selecting anything in your DOM by using it's id, will return the FIRST element that matches the specified id. This is why it's recommended you don't use same ids for more than one element in your DOM.
Now that we know what the problem is, here's ONE possible way to fix it:
You can have the upvote-btn as a class instead of id in your template, so that every upvote button has a class called upvote-btn.
So your script becomes something like:
$(document).on('click', '.upvote-btn', function (e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url "main:upvote-post" %}',
data: {
postid: e.target.value,
csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
},
success: function (json) {
document.getElementById("total_upvotes").innerHTML = json['result']
},
error: function (xhr, errmsg, err) {
console.log(err)
}
});
})
Note, the changes are in the first line of the script, we have changed the selector from #upvote-btn to .upvote-btn because we have previously discussed converting upvote-btn to a class instead of an id.
And we have also changed the postid in your AJAX call from $('#upvote-btn').val() to e.target.value
I have a page that shows every objects in the database, this is handled by an ajax function that gets a JSON file containing every objects in the db and renders out some html code for every object.
There's also a classic Django ModelForm that allows the creations of new db's objects, the new objects is instantly loaded with the others.
I want an html button for every objects that deletes it "on the fly", so without redirecting to a Detail Delete template.
$('.remove').on('click', function() {
$.ajax({
type:'DELETE'
url: 'http://127.0.0.1:8000/MyApp/list-api/' + $(this).attr('data-id')
}
When the button is clicked it should send a DELETE request to the url of the detail api of the object. Nothing happens, no new request in the network tab of the browser.
This is my index.html
<body>
<h2>coffee orders</h2>
<ul id="orders">
</ul>
<h4>Add coffee order</h4>
<form method="POST" action=".">
{% csrf_token %}
{{form.as_p}}
<button id="add-order" type="submit">Add!!!</button>
</form>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="{% static 'MyApp/jquery_js.js' %}"></script>
</body>
This is my jquery.js,
this function get the api and renders out the infos about the object and a delete button which doens't work.
$(function (){
var $orders = $('#orders')
$.ajax({
type: 'GET',
url: 'http://127.0.0.1:8000/MyApp/list-api/?format=json',
success: function(orders) {
$.each(orders, function(i, order){
$orders.append('<li>name: '+order.name+', drink:
'+order.drink+'</li>')
$orders.append("<form method='DELETE'><button data-id=" +
(order.pk)+" class='remove'>X</button>")
});
},
error: function() {
alert('errore caricamento ordini');
}
});
def list_create_view(request):
form = DrinkModelForm(request.POST or None)
if form.is_valid():
print(form.cleaned_data)
obj = form.save(commit=False)
obj.user = request.user
form.save()
form = DrinkModelForm()
context = {"form": form}
return render(request, "MyApp/index.html", context)
class OrderListView(generics.ListCreateAPIView):
pass
serializer_class = OrderSerializer
def get_queryset(self):
return Order.objects.all()
class OrderDetailView(generics.RetrieveDestroyAPIView):
pass
serializer_class = OrderSerializer
def get_object(self):
id_ = self.kwargs.get("pk")
return get_object_or_404(Order, pk=id_)
The X button should delete the objects from the db but nothings happens, I'm new to jquery so any help is really appreciated, thanks.
You should use the OrderDetailView for deletion and remove that pass from the class definition. BTW, you don't need to override the get_object method if you're passing the pk on the URL.
views.py
class OrderDetailView(generics.RetrieveDestroyAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
In a chat-like app, I'm using ajax calls to POST a new message and update the messages displayed on the page without reloading the entire page. My ajax call for posting works - a new message instance is created in the database. However, afterwards when I make an ajax call to GET all messages, the new message is missing from the resulting query set. If I refresh the page fully, I can see all the messages, but that's not what I'm after.
HTML messages template:
{% for message in messages %}
<p>
{{ message.content }}
</p>
{% endfor %}
HTML chat template:
<div id="chat">
{% include "messages.html" %}
</div>
<form id="post-message-form", method="post" enctype="multipart/form-data">
[my form goes here]
</form>
JavaScript:
$('#post-message-form').on('submit', function(event){
event.preventDefault();
$form = $(this);
var data = new FormData($form.get(0));
$.ajax({
url: '/post/a/new/message/',
type: 'POST',
data: data,
success: refresh_chat,
cache: false,
contentType: false,
processData: false
})
return false;
}
function refresh_chat(){
$.ajax({
url: '/get/all/messages/,
type: 'GET',
success: function(json) {
$('#chat').html(json['data']);
}
})
return false;
}
Views:
import json
from django.template import loader
from .forms import MessageForm
# /post/a/new/message/
def post_message(request):
if request.method == 'POST':
form = MessageForm(request.POST)
if form.is_valid():
message = form.save()
return HttpResponse(
json.dumps({'status': 1,
'message': 'Message posted!'}),
content_type='application/json'
)
# /get/all/messages/
def get_messages(request):
if request.method == 'GET':
messages = loader.render_to_string('messages.html', context={'messages': Message.objects.all(), 'form': MessageForm()})
return HttpResponse(
json.dumps({'data': messages}),
content_type='application/json'
)
Any ideas why I do not get the latest database data when I call an ajax GET after POST? Thanks!
Turns out if I do my chat refresh on the ajax "done" call instead of on "success" it works:
$('#post-message-form').on('submit', function(event){
event.preventDefault();
$form = $(this);
var data = new FormData($form.get(0));
$.ajax({
url: '/post/a/new/message/',
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false
}).done(function() {
refresh_chat();
});
return false;
}
Thanks for commenting!
I am wrtitng a simple Airbnb scraper. When the user submits a form with city name filled in, he will get a table with prices and links rendered by AJAX on the same page.
The form is in my "index.html":
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input id="create_table" type="submit" value="View results" />
</form>
<div id="table">Place for table</div>
I am using two views: "index" and "results". "Index" is just for collecting the city name and passing it to the "results" (which is called by AJAX, see below):
def index(request):
if request.method == 'POST':
form = RoomForm(request.POST)
if form.is_valid():
city = form.cleaned_data['city'].title()
request.session['city'] = city
else:
form = RoomForm()
context = {'form': form}
return render(request, 'javascript/index.html', context)
def results(request):
if request.is_ajax():
city = request.session.get('city')
url = 'https://www.airbnb.pl/s/' + str(city) +'
# "results" scraping code continues...
I need to pass the city variable from "index" to "results", hence the request.session part.
However I think the variable never gets assigned because the following .load AJAX call (the other call to API is irrelevant to the example)
function create_table() {
$.ajax({
method: "GET",
url: "/api/data/",
success: function(data){
$('#table').load('http://127.0.0.1:8000/results', function(){
$('#go_back').remove();
});
},
error: function(error_data){
console.log("errorrr")
console.log(error_data)
}
})
}
document.getElementById("create_table").onclick = function() {
create_table();
return false;
, which is assigned to the same submit button, is launched first and "results" view is loaded, skipping the form submit, hence the variable gets passed as None.
The end result looks like this (notice the "None" in link text instead of "Barcelona"):
How do I work around this?
Have you tried assigning the javascript code to an onsubmit attribute
<form onsubmit="return create_table()" action="" method="post>
Just ensure that it returns a default empty state when the data is not required.