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
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
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
I am using wizard forms in django. I want to create a form field only if answer to some other form field is marked "yes" otherwise I don't want this new form field. How can I do this ?
I have tried some other answers related to this but most of them tells about how to mark field required or not but I want to display that field only if answer to other field is "Yes"
Django Form Wizard with Conditional Questions
In below code I want to display field "Pool2" only if answer to field "Pool" is marked "yes" otherwise I don't want that field. Basically I want to get some details of pool in field "Pool2" if there is pool in user's house.
forms.py
class ListingForm2(forms.Form):
Pool = (
("Yes","Yes"),
("No","No"),
)
Pool = forms.ChoiceField(choices = Pool,label = "Does your property have a pool ?")
Pool2 = forms.CharField(required=False)
Views.py
class ListingWizard(SessionWizardView):
template_name = 'listing_form.html'
form_list = [ListingForm1,ListingForm2,ListingForm3,ListingForm4]
def done(self, form_list, **kwargs):
save_data(form.cleaned_data for form in form_list)
return render(self.request,'done.html',{
'form_data' : [form.cleaned_data for form in form_list],
})
What you are trying to do have to be done with JavaScript, you could do it with only Django posts but it is not a right way.
Check this out:
class BookForm(forms.ModelForm):
has_sequel = forms.BooleanField(initial=True)
class Meta:
model = Book
fields = ['author', 'length', 'has_sequel', 'sequel']
class Media:
js = ('book_form.js', )
def clean(self):
if self.cleaned_data['has_sequel'] and self.cleaned_data['sequel'] is None:
raise ValidationError('You should indicate the sequel if the book has one.')
class BookView(FormView):
template_name = 'book_form.html'
form_class = BookForm
success_url = '/done/'
This code is including a Javascript with the form, this way you could reuse the form with its own Javascript, the Javascript code should be something like this (you maybe have to change javascript depending on how you print your form in the template):
$(document).ready(function() {
$('#id_has_sequel')[0].addEventListener('change', (event) => {
let sequelField = $('#id_sequel').parents('p');
if (event.target.checked) {
sequelField.show();
} else {
sequelField.hide();
}
})
});
And the template should be something like this:
{% load static %}
<head>
<title>Book form</title>
<script src="{% static 'jquery-3.4.1.min.js' %}"></script>
{{ form.media }}
</head>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Send message">
</form>
If you have any question feel free to ask but trying to do this without Javascript it is not a good approach. As much you will find some kind of Django widget that will use Javascript too.
I have a Django Post Like/Dislike feature in my App. The feature works amazing on its own. However when I add ajax to the Like/Dislike button the whole thing breaks giving me a Page not found Error
Working Code without Ajax
Models.py
class Post(models.Model): #title, slug, message, etc exist but not included here
likes = models.ManyToManyField(User, blank=True, related_name='post_likes')
def get_absolute_url(self):
return reverse('posts:single', kwargs={'username': self.user.username,
'slug': self.slug})
def get_api_like_url(self):
return reverse('posts:like_post', kwargs={'username': self.user.username,
'slug': self.slug})
Views.py
class PostLikeToggle(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
slug = self.kwargs.get('slug')
print(slug) #Prints the slug
obj = get_object_or_404(Post, slug=slug)
url_ = obj.get_absolute_url()
user = self.request.user
if user.is_authenticated():
if user in obj.likes.all():
obj.likes.remove(user)
else:
obj.likes.add(user)
print(url_)
return url_
Template:
<form action="{{post.get_api_like_url}}" method="post">
{% csrf_token %}
{% if user in post.likes.all %}
<button type="submit" name="post_slug" value="{{ post.slug }}" class="btn btn-link like_button">
<img class="like_heart" src="{% static 'images/HEART.RED.png' %}" height="25px">
</button>
{% else %}
<button type="submit" name="post_slug" value="{{ post.slug }}" class="btn btn-link like_button">
<img class="like_heart" src="{% static 'images/HEART.png' %}" height="25px">
</button>
{% endif %}
</form>
Urls.py
url(r'^(?P<username>[-\w]+)/(?P<slug>[-\w]+)/like_post/$', views.PostLikeToggle.as_view(), name="like_post")
Everything above works perfectly
I added Ajax. Below is my code after adding AJAX where I get the Page not found
Template same as above
Models same as above
Urls same as above
Views.py
class PostLikeToggle(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
slug = self.kwargs.get('slug')
print(slug)
obj = get_object_or_404(Post, slug=slug)
user = self.request.user
if user.is_authenticated():
if user in obj.likes.all():
obj.likes.remove(user)
else:
obj.likes.add(user)
context = {
"post": obj,
}
if self.request.is_ajax():
html = render_to_string("posts/like_section.html", context, request=self.request)
print(html) #This prints the html
print(JsonResponse({"form": html})) #This prints <JsonResponse status_code=200, "application/json">
return JsonResponse({"form": html})
Added Script to base.html
<script>
$(document).ready(function () {
$(".like_button").click(function (event) {
event.preventDefault();
var slug = $(this).attr("value"); #I have tried console.logging slug it works
$.ajax({
url : "{{post.get_api_like_url}}",
type: "POST",
data: {"slug": slug, "csrfmiddlewaretoken": "{{ csrf_token }}"},
dataType: "json",
success: function (data) {
$("#like-section").html(data["form"]);
console.log($("#like-section").html(data["form"]));
}, error: function (rs, e) {
console.log("error");
console.log(rs, e);
}
})
})
})
</script>
Terminal prints everything in the view with print and then gives the below
: [24/Sep/2018 00:23:10] "POST
/posts/charlize/singes-boat-new-york/like_post/ HTTP/1.1" 302 0
Not Found: /posts/charlize/singes-boat-new-york/like_post/
[24/Sep/2018 00:23:10] "GET /posts/charlize/singes-boat-new-york/like_post/%3CJsonResponse%20status_code=200,%20%22application/json%22%3E HTTP/1.1" 404 7845
FYI: If I manually refresh the page the Like/Dislike toggle has worked
You are implementing the get_redirect_url method. Whatever that method returns us treated as a URL to redirect the browser to. The JsonResponse function returns an object with the response data. But since this is get_redirect_url, Django thinks that it is a string with the URL.
Instead, implement the post method and manually return a HttpResponseRedirect if it is a normal request, and the JsonResponse if it is a AJAX request.
Another thing: You are using the LoginRequiredMixin mix-in. No need to check for user.is_authenticated in the View.
class PostLikeToggle(LoginRequiredMixin, RedirectView):
def post(self, *args, **kwargs):
slug = self.kwargs.get('slug')
obj = get_object_or_404(Post, slug=slug)
user = self.request.user
context = {"post": obj}
if user in obj.likes.all():
obj.likes.remove(user)
else:
obj.likes.add(user)
if self.request.is_ajax():
html = render_to_string("posts/like_section.html", context, request=self.request)
return JsonResponse({"form": html})
url = obj.get_absolute_url()
return HttpResponseRedirect(url)
The Goal:
I want to use FullCalendar(https://fullcalendar.io/) to display event objects from my database. FullCalendar accepts an array of event objects as a property.
The Problem I'm having:
I can send context data back to the template with the event objects but as far as I know I can only interact with the data using Django's template tagging system. *EDIT: When I replace the hardcoded array with ptoHistory I receive the following error in the chrome console:
jquery-3.1.1.min.js:2 Uncaught ReferenceError: ptoHistory is not
defined
at HTMLDocument. ((index):141)
at j (jquery-3.1.1.min.js:2)
at k (jquery-3.1.1.min.js:2)
index.html:
{% extends 'base.html' %}
{% block content %}
//ACCESSING THE CONTEXT DATA LIKE THIS WORKS BUT I CAN'T USE ptoHistory IN MY FULLCALLENDAR SCRIPT
{% for history in ptoHistory %}
<li>{{obj.leave_type}}</li>
{% endfor %}
<div class="container">
<div id="calendar">
<!-- Calendar is injected here -->
</div>
<!----------------------- SCRIPTS ----------------------------->
<script>
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView:'month',
editable: true,
// MY ARRAY OF OBJECTS WILL REPLACE THIS HARDCODED ARRAY
events: [
{
title: 'All Day Event',
start: '2017-01-12',
},
{
title: 'Meeting',
start: '2017-01-13T10:30:26',
end: '2014-06-13T12:30:00'
},
],
});
});
</script>
{% endblock content%}
IndexView.py:
class IndexView(FormView):
template_name = 'accounts/index.html'
form_class = PtoRequestForm
success_url = 'login/'
def form_valid(self, form):
form.save()
return super(IndexView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['ptoHistory'] = PtoHistory.objects.all()
print(context['ptoHistory'])
return context
Could someone point me in the right direction?
You are right, it won't work. Django template system is able to process python objects because they are executed already before the template is finished rendering.
You could still assign python list in javascript, though, but not with python objects but json string. The solution is basically compose for only what you need in the views using values() then . I don't know what fields do you need for PtoHistory, but you could do:
# views.py
import json
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
ptoHistory = json.dumps(list(PtoHistory.objects.values()))
# or do this if you only need several fields' value
# ptoHistory = json.dumps(list(PtoHistory.objects.values('field1', 'field2')))
context['ptoHistory'] = ptoHistory
return context
// in javascript
$(document).ready(function() {
$('#calendar').fullCalendar({
defaultView:'month',
editable: true,
var histories = {{ ptoHistory|safe }};