When the user clicks a specific button, I want to call an synchronous function inside the already used view function, but passing a parameter from JavaScript. How can I do it?
Template:
<input class="form-check-input" type="checkbox" value="{{ subject.id }}" id="flexCheckDefault{{ subject.name }}" onclick="checkRequisite(this.defaultValue)">
Javascript:
function checkRequisite(id){
}
View:
if request.user.is_authenticated and request.user.groups.filter(name='student'):
subjects = subject.objects.all()
async def checkResquisite(id):
requisite = Requisite.objects.filter(subject_requisite_id=id)
context = {'subjects': subjects, 'requisite': requisite}
template = loader.get_template('student/subject/select.html')
return HttpResponse(template.render(context, request))
elif request.user.is_authenticated and request.user.groups.filter(name='teacher'):
return render(request, 'admin/home/index.html', {})
else:
return redirect('login')
I think there are a few misconcepts here. Async functions are called from the frontend to the backend (with ajax, fetch...), not the other way around:
async function checkRequisite(id){
response = await fetch(...);
}
Also, normally you would have two different views, I believe just as a good practice to have your code more organized and descriptive of what your views do exactly.
def load_template(request):
...
return render(...)
def ajax_view(request):
...
return JsonResponse(...)
But, to answer your question, the code below does the following:
On the template, with every click on checkboxes search which of them are selected take their value (subject.id), push into a list and send that list of IDs to backend using a post request with the fetch API.
There (on the backend), check the type the request method and filter requisite based on that list of IDs.
student/subject/select.html
{% extends 'base.html' %}
{% block content %}
{% for subject in subjects %}
<label>{{ subject.name }}</label>
<input class="form-check-input" type="checkbox" value="{{ subject.id }}" id="checkbox" onclick="checkRequisite()">
<br>
{% endfor %}
<hr>
<div id="demo"></div>
{% endblock %}
{% block script %}
<script>
async function checkRequisite() {
var id_list = [];
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].type == "checkbox") {
if (inputs[i].checked) {
id_list.push(inputs[i].getAttribute('value'))
}
}
}
var payload = {
subject_ids: id_list,
};
var data = new FormData();
data.append( 'data' , JSON.stringify( payload ) );
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
await fetch("{% url 'core:load-template' %}", {
method: 'post',
body: data
}).then((response) => {
return response.json();
}).then((data) => {
let element = document.getElementById("demo").innerHTML = '';
for (let key in data['requisites']){
let element = document.getElementById("demo").innerHTML += `<p>Requisite: ${data['requisites'][key]['name']} | Subject: ${data['requisites'][key]['subject']}<p><br>`;
}
});
}
</script>
{% endblock %}
views.py
def load_template(request):
if request.user.is_authenticated and request.user.groups.filter(name='student'):
queryset = Subject.objects.all()
requisite = None
if request.method == 'POST':
data = json.loads(request.POST.get('data'))
requisites = Requisite.objects.filter(subject__id__in=data['subject_ids'])
response = {}
for requisite in requisites:
response[requisite.id] = { 'name': requisite.name, 'subject': requisite.subject.name }
return JsonResponse({ 'requisites': response })
return render(request, 'student/subject/select.html', {'subjects': queryset })
elif request.user.is_authenticated and request.user.groups.filter(name='teacher'):
return render(request, 'admin/home/index.html', {})
else:
return redirect('login')
urls.py
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
path('load/template/', views.load_template, name='load-template'),
]
Related
I am trying to create a button that allows users to save edits to a post, which they write in a textarea, through JSON. I am trying to save the data through a PUT request, but I get the following error:
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
javascript function:
function save_edit(id){
console.log("save button is clicked");
const edit_area = document.querySelector(`#edit_area_${id}`);
//save the post
fetch(`/edit/${id}`,{
method: 'PUT',
post: JSON.stringify({
post: edit_area.value
})
})
fetch(`/edit/${id}`)
.then(response => response.json())
.then(post => {
const post_itself =
document.querySelector(`#post_itself_${id}`);
post_itself.value = `${post.post}`;
post_itself.style.display = 'block';
})
}
django.views:
def edit(request, post_id):
try:
post = Post.objects.get(pk=post_id)
except Post.DoesNotExist:
return JsonResponse({"error": "Post not found."}, status=404)
if request.method == "POST":
edited_post = request.POST.get('post')
try:
post.post = edited_post
post.save()
except:
return JsonResponse({"error": "Editing the post did not work."}, status=404)
elif request.method == "GET":
return JsonResponse(post.serialize())
elif request.method == "PUT":
data = json.loads(request.body)
edited_post = data["edit_area"]
post.post = data["edited_post"]
post.save()
else:
return JsonResponse({"error": "Need a GET request."}, status=404)
html
{% for post in page_obj.object_list %}
<div class = "individual_posts">
<h5 id="p_user" class = "post_user">{{ post.user }}</h5>
<h6 id = "post_itself_{{ post.id }}" class="post_itself">{{ post.post }}</h6>
{% if post.user == request.user %}
<button id="{{ post.id }}" class="edit_button" value="{{ post.id }}">Edit</button>
{% endif %}
<textarea class="textarea" id="edit_area_{{ post.id }}" cols="220" rows="5"></textarea>
<button class="edit_save" id="save_{{ post.id }}">Save</button>
</div>
{% endfor %}
models.py serialization
def serialize(self):
return{
"id": self.pk,
"post": str(self.post),
"user": self.user.pk,
}
The GET request works correctly, but I am receiving the previously stated error from the PUT request. I think that it is because of the way I am getting the value through edited_post = data["edit_area"]. How do I correctly get access to the text within the textarea to pass to JSON?
In your save_edit PUT function you are using
post: JSON.stringify({
post: edit_area.value
})
But in your view you are looking for
data = json.loads(request.body)
edited_post = data["edit_area"]
post.post = data["edited_post"]
The JSON you are sending looks like
{"post": "Here's my edits"}
So you probably want something like:
data = json.loads(request.body)
post.post = data["post"]
Also - fetch uses "body" not "post" so you might want to amend your put function to
body: JSON.stringify({
post: edit_area.value
})
I am trying to create an API in Django but am receiving the following errors message in the JavaScript console.
GET http://127.0.0.1:8000/edit/undefined 404 (Not Found)
POST http://127.0.0.1:8000/edit/undefined 404 (Not Found)
Does anyone know how to fix this problem?
API url: path("edit/<int:post_id>", views.edit, name="edit")
views.py
def edit(request, post_id):
try:
post = Post.objects.get(user=request.user, pk=post_id)
except Post.DoesNotExist:
return JsonResponse({"error": "Post does not exist."}, status=404)
if request.method == "GET":
return JsonResponse(post.serialize())
else:
return JsonResponse({"error": "Need a GET request."}, status=404)
JavaScript
document.addEventListener('DOMContentLoaded', function(){
const editButtons = document.querySelectorAll('.edit_button');
for (const button of editButtons) {
button.addEventListener('click', () => edit_email());
}
});
function edit_email(id){
console.log("edit button is clicked")
document.querySelector('#post_itself').style.display = 'none';
document.querySelector('#date_and_time').style.display = 'none';
document.querySelector('#likes').style.display = 'none';
const textarea = document.createElement('textarea');
//get post
fetch(`/edit/${id}`)
.then(response => response.json())
.then(post => {
textarea.innerHTML = `${post.post}`
document.querySelector('#p_user').append(textarea);
})
//save the post
fetch(`/edit/${id}`,{
method: 'POST',
post: JSON.stringify({
post: textarea.value
})
})
}
HTML
{% for post in page_obj.object_list %}
<div class = "individual_posts">
<h5 id="p_user" class = "post_user">{{ post.user }}</h5>
<h6 id = "post_itself">{{ post.post }}</h6>
<h6 id="date_and_time" class = "post_elements">{{ post.date_and_time }}</h6>
<h6 id="likes" class = "post_elements">{{ post.likes }}👍</h6>
{% if post.user == request.user %}
<button id="editButton" class="edit_button">Edit</button>
{% endif %}
</div>
{% endfor %}
I think something might be wrong in the way I am passing in the id to the API, but I am not sure. Could the for loop in the HTML be causing the problem?
models.py
class User(AbstractUser):
followers = models.ManyToManyField("self", related_name="users_followers", symmetrical=False)
following = models.ManyToManyField("self", related_name ="who_user_is_following", symmetrical=False)
def serialize(self):
return{
"followers": self.followers,
"following": self.following
}
class Post(models.Model):
post = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE, default="")
likes = models.IntegerField(default=0)
date_and_time = models.DateTimeField(auto_now_add=True)
def serialize(self):
return{
"id": self.id,
"post": self.post,
"user": self.user,
"likes": self.likes,
"date_and_time": self.date_and_time
}
you call edit_email without id here:
button.addEventListener('click', () => edit_email());
of cause, after call you get /edit/undefined on this line:
fetch(`/edit/${id}`)
you don't send anything like id, I can imagine it should be something like this:
button.addEventListener('click', (event) => edit_email(event.target.value));
You will also need to pass the value property to the button as post.id assuming that the post object will have an id key in your for loop.
If you are getting a reference error you need to check if page_obj.object_list has an id key for all the posts.
I am getting Post.match is not a function error when i do this :( Please help, i am a newbie in JavaScript part. (I am getting back an object so have to turn it into an array but still get this error after using the Objects.values Method)
My Views.py File:
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from .models import Post
def Data(request):
items = Post.objects.all()
data = []
for qs in items:
I = {"title":qs.title,
"content": qs.content,
"image": qs.image.url,
}
data.append(I)
return JsonResponse({"data":data})
My HTML File:
{% extends 'blog/base.html' %}
{% load static %}
{% block content %}
<div class = 'w-100 text-center'>
<h1>Search Results</h1>
<form id = "search-form" autocomplete="off">
{% csrf_token %}
<input name = 'game' type="text" id = "search-input" placeholder= "Post Search..">
</form>
<div id = "results-box" class = "results-card">
</div>
</div>
{% endblock content %}
{% block js %}
<script defer src="{% static 'blog/S1.js' %}"> </script>
{% endblock js %}
My Java Script File:
console.log('Heelowwww')
const url = window.location.href
const searchForm = document.getElementById("search-form")
const searchInput = document.getElementById("search-input")
const resultsBox = document.getElementById("results-box")
const csrf = document.getElementsByName("csrfmiddlewaretoken")[0].value
options = {method: "GET",
headers: {
Accept: "application/json"
},
data:{
'csrfmiddlewaretoken': csrf,
}
}
const SearchPosts = async SearchIt => {
const res = await fetch("http://localhost:8000/data/",options)
const Posts = await res.json()
S = Object.values(Posts["data"])
let matches = S.filter(post =>{
const regex = new RegExp(`^${SearchIt}`, 'gi')
return post.match(regex)
})
console.log(matches)
}
searchInput.addEventListener('input', () => SearchPosts(searchInput.value))
My data Json Page:
Json Data Page
Here post.match(regex) your trying to call the match method on a js object. It seems you should be calling it on on of the string properties of it. Something like: post.title.match(regex).
I'm trying to implement chained dependent dropdown combobox selection, so you start with one combobox for main category and once you select main category, another <select> appears to select a subcategory, and so on until the innermost (most specific) category is selected. The code I have currently only works for one subcategory (direct children), how can I make it work for other levels too? So, I need to attach an onChange callback to a newly created <select> somehow.
This is jQuery code in my Django template:
{% extends 'pages/base.html' %}
{% block content %}
<h1>Create a product</h1>
<form method='POST' id='productForm' data-products-url="{% url 'products:ajax_load_categories' %}">
{{ form.as_p }}
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("select").change(function () {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
$("#productForm").append(data);
}
});
});
</script>
{% endblock %}
Here is my view:
def load_categories(request):
category_id = request.GET.get('category')
subcategories = Category.objects.get(id=category_id).get_children()
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
products/category_dropdown_list_options.html
<select id="select_{{ subcategories.first.get_level }}">
<option value="">---------</option>
{% for subcategory in subcategories %}
<option value="{{ subcategory.pk }}">{{ subcategory.name }}</option>
{% endfor %}
</select>
Here is my urls.py:
app_name = 'products'
urlpatterns = [
path('create/', product_create_view, name='product-create'),
path('ajax/load-categories/', load_categories, name='ajax_load_categories')
]
Here is my Category model as per request:
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
unique_together = (('parent', 'slug',))
verbose_name_plural = 'categories'
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_slug_list(self):
ancestors = self.get_ancestors(include_self=True)
slugs = [ancestor.slug for ancestor in ancestors]
new_slugs = []
for idx, ancestor in enumerate(slugs, 1):
new_slugs.append('/'.join(slugs[:idx]))
return new_slugs
def get_recursive_product_count(self):
return Product.objects.filter(category__in=self.get_descendants(include_self=True)).count()
You'll need to turn your jQuery ajax script into a function, then call it recursively, like this:
<script>
var $r_ = function() {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
if (data != 'leaf_node') {
$("#productForm").append(data);
}
$('select').change($r_);
}
});
} //end of $r_
$('select').change($r_);
</script>
Update
If you take a look at the get_children method of the MPTT model, you'll see that it checks whether or not the instance has any children, and returns None if it doesn't.
Add a None check in your view, then add a different response when you've reached a leaf node:
from django.http import HttpResponse
def load_categories(request):
category_id = request.GET.get('category')
subcategories = Category.objects.get(id=category_id).get_children()
if subcategories:
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
return HttpResponse('leaf_node')
Then add a check for leaf nodes in your ajax call (see above).
Hi I am new on django i was creating custom plugin in django cms it is the form other fields get saved easily, but the image field is not getting saved.
Here is my code let me know if I am missing something I am really stuck over here your help will be really appreciated.
models.py
class Blogs(models.Model):
name = models.CharField(u'Name',
blank=True,
default='',
help_text=u'Your name',
max_length=64,
)
description = models.TextField(u'Description',
blank=True,
default='',
help_text=u'Enter your Blog.',
)
comments = models.TextField(u'Short Description',
blank=True,
default='',
help_text=u'Short title of the Blog.',
)
images = models.ImageField(
upload_to='blog_image',
default='blog_image/black.png',
blank=True
)
# Meta, non-form data
contact_date = models.DateTimeField(
u'contact date',
blank=True,
default=timezone.now,
help_text=u'When this person completed the contact form.',
)
was_contacted = models.BooleanField(u'has been contacted?',
default=False,
help_text=u'Check this if someone has already reached out to this person.',
)
notes = models.TextField(u'contact notes',
blank=True,
default='',
help_text=u'Internal notes relating to contacting this person.',
)
referer = models.CharField(u'referring page',
blank=True,
default='',
help_text=u'This is the page the visitor was on before coming to the contact page.',
max_length=2048,
)
def send_notification_email(self):
subject.txt', {
'contact': self,
})
email_body = render_to_string('contacts/notification-body.txt', {
'contact': self,
})
try:
send_mail(
email_subject,
email_body,
settings.SERVER_EMAIL,
settings.MANAGERS,
fail_silently=(not settings.DEBUG)
)
except Exception:
if (settings.DEBUG):
raise
def save(self, *args, **kwargs):
if not self.pk:
try:
self.send_notification_email()
except:
pass
super(Blogs, self).save(*args, **kwargs)
def __unicode__(self):
return '%s (%s)' % (self.name, str(self.contact_date),)
class BlogPluginModel(CMSPlugin):
title = models.CharField(u'title',
blank=True,
help_text=u'Optional. Title of the widget.',
max_length=64,
)
def __unicode__(self):
return self.title
cms_plugins.py
class BlogPlugin(CMSPluginBase):
model = BlogPluginModel
name = _("Blog Form")
render_template = "blogs/_blogs_widget.html"
def render(self, context, instance, placeholder):
try:
path = context['request'].path
except:
path = ''
form = BlogAjaxForm(initial={'referer': path})
context.update({
"title": instance.title,
"form": form,
"form_action": reverse("blogging_form"),
})
return context
plugin_pool.register_plugin(BlogPlugin)
forms.py
class BlogBaseForm(ModelForm):
class Meta:
abstract = True
required_css_class = 'required'
verify_email = forms.EmailField(
label=u'Verify email',
help_text=u'Please retype your email address here.',
max_length=255,
required=True,
)
required_fields = []
class blogForm(BlogBaseForm):
images=forms.ImageField(help_text="Upload image: ", required=False)
class Meta:
model = Blogs
fields = [
'name','images', 'description', 'comments', 'referer',
]
widgets = {
'referer': forms.HiddenInput(),
}
required_fields = ['name', 'email', 'verify_email', ]
class BlogAjaxForm(BlogBaseForm):
images=forms.ImageField(help_text="Upload image: ", required=False)
class Meta:
model = Blogs
fields = ['name','images', 'description','comments','referer', ]
widgets = { 'referer': forms.HiddenInput(),}
view.py
class BlogFormView(FormView):
form_class = BlogBaseForm
template_name = 'contacts/contact_form.html'
def get_initial(self):
"""
still preserve this original HTTP_REFERER.
"""
initial = super(BlogFormView, self).get_initial()
initial['referer'] = self.request.META.get('HTTP_REFERER', ''),
return initial
def get_success_url(self):
page = get_object_or_404(
Page,
reverse_id='blog_form_submission',
publisher_is_draft=False
)
return page.get_absolute_url()
def form_valid(self, form):
self.object = form.save()
return super(BlogFormView, self).form_valid(form)
class AjaxableResponseMixin(object):
def __init__(self):
self.request = None
self.object = Blogs
def render_to_json_response(self, context, **response_kwargs):
data = json.dumps(context)
response_kwargs['content_type'] = 'application/json'
return HttpResponse(data, **response_kwargs)
def form_invalid(self, form):
response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return self.render_to_json_response(form.errors) # , status=400)
else:
return response
def form_valid(self, form):
response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
'pk': self.object.pk,
}
return self.render_to_json_response(data)
else:
return response
class BlogFormAjaxView(AjaxableResponseMixin, FormView):
form_class = BlogAjaxForm
http_method_names = [u'post']
template_name = 'contacts/_contact_widget.html'
form = BlogAjaxForm(http_method_names)
def get_success_url(self):
page = get_object_or_404(
Page,
reverse_id='blog_form_submission',
publisher_is_draft=False
)
return page.get_absolute_url()
def form_valid(self, form):
self.object = form.save(commit=True)
return super(BlogFormAjaxView, self).form_valid(form)
urls.py
urlpatterns = [
url(
r'^blogging_form/$',
BlogFormAjaxView.as_view(),
name='blogging_form'
),
url(r'^$', BlogFormView.as_view(), name='Blog_Form'),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
_blog_widget.html
{% load i18n static sekizai_tags %}
{% addtoblock "js" %}<script src="{% static 'blogs/scripts/Blogs.js' %}"></script>
{% endaddtoblock %}
{% addtoblock "css" %}
<link rel="stylesheet" href="{% static 'contacts/css/styles.css' %}">
{% endaddtoblock %}
{% addtoblock "js" %}
<script src="{% static 'http://malsup.github.com/jquery.form.js' %}"></script>
{% endaddtoblock %}
<div class="Blogs-plugin plugin">
<div class="inner">
<div class="Blog-form">
{% if title %}<h3>{{ title }}</h3>{% endif %}
<form method="post" action="{{ form_action }}" enctype="multipart/form-data">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{% if hidden.name == 'referer' %}
<input type="hidden" name="{{ hidden.name }}" value="{{ hidden.value }}">
{% endif %}
{% endfor %}
{% for visible in form.visible_fields %}
<div class="field-wrapper field-row half {{ visible.css_classes }}">
{{ visible.label_tag }}
<div class="help-text">{{ visible.help_text }}</div>
<div class="error empty"></div>
{{ visible }}
</div>
{% endfor %}
<input type="submit" value="Submit">
</form>
<div class="legend"><span class="required">*</span> Required field
</div>
<div class="success" style="display:none">Thank you, a member of our
team will contact you shortly, if appropriate.
</div>
<div class="errors" style="display:none"><span class="form-errors">
</span>
</div>
</div>
</div>
</div>
Blogs.js
(function($){
"use strict";
$(function(){
$('.Blogs-plugin input[type=submit]').on('click',
function(evt){
var $form = $(this).parents('form').eq(0);
function handleResponse(data){
if (data.pk) { // Success!
$form.siblings('.success').html(data.success).show(100);
$form.add('.legend').hide(100);
}
else { // Validation Issues...
//
// data will a dictionary like so:
// { 'field_name': ['error1', 'error2'], ... }
//
$form.find('.error').empty();
$.each(data, function(key, value){
var $field = $form.find('input[name='+key+']').first();
$field.parents('.fieldwrapper').find('.error').html(value.join(' '));
});
if (data.__all__) {
$form.siblings('.errors').find('.form-
errors').html(data.__all__.join(' '));
}
else {
$form.siblings('.errors').find('.form-errors').empty();
}
$form.siblings('.errors').show(100);
}
}
evt.preventDefault();
$form.siblings('.errors, .success').hide(100);
$.ajax({
type: 'POST',
url: $form.attr('action'),
data: $form.serialize(),
}).always(handleResponse);
});
});
}(window.jQuery));