use django "include" inside a js function - django

I am doing a search page in which parameters are sent by ajax and then upon reception of the queryset I rebuild my cards. The whole thing is classic and working ok, here is a simplified version of the thing. Lots of lines killed or modified since it is not really the subject of the post
let getobject = async (value,url) => {
var res2 = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({
value: value,
})
})
let data2 = await res2.json();
videoitems.innerHTML = ''
modalbin.innerHTML = ''
data2["data"].forEach(async item => {
if (item.ext == '.mp4') {
const dynamicreation = async () => {
let dyncontent3 = await createnewcard(item)
let placing = await videoitems.appendChild(dyncontent3);
}
const nooncares2 = await dynamicreation()
} else if (item.ext == ".pdf") {
const dynamicreation2 = async () => {
let dyncontent4 = await createnewcard(item)
let placing2 = await videoitems.appendChild(dyncontent4);
}
const nooncares4 = dynamicreation2()
}
})
}
the createnewcard function
var createnewcard = item => {
var dyncontent = document.createElement("div");
dyncontent.innerHTML =
`<div class="m-2 extralarge-modal video${item.id}">
<div data-reco="${item.id}"
class="extralarge-modal bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700">
<div class="p-5">
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
${item.title}
</p>
</div>
</div>
</div>`;
return dyncontent
}
What I would like to know is if it would be possible to mix this js with the django "include" function and instead of using js template litterals use an html component of the card that I would include upon looping in the data reveived. I could also maybe include it inside the createnewcard js function but so far it all failed quite miserably.
Thanks a lot

Yes, you've to create card.html or whatever you can name then you've to just include inside your js make sure you use same variables while looping in js eg.(item)
card.html
<div class="m-2 extralarge-modal video${item.id}">
<div data-reco="${item.id}"
class="extralarge-modal bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700">
<div class="p-5">
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
${item.title}
</p>
</div>
</div>
</div>
and inside your Js do like this
var createnewcard = item => {
var dyncontent = document.createElement("div");
dyncontent.innerHTML = `{% include "card.html" %}`
return dyncontent
}

Related

Why images are not submitted when using ajax call in django?

I am using a modelForm to create post objects via ajax. The images field are part of the form but not passed to the fields of the Meta class because that will allow to save the post first and add the images uploaded after that. My issue is if I do use a regular view(without ajax) the request.FILES are being submitted correctly but when I use via ajax those files are not part of the request.files which renders an empty <MultiValueDict: {}> I don't really know why.
Here is my code.
def post(self, request, *args, **kwargs):
form = PostForm(request.POST or None, request.FILES or None)
result = {}
files = request.FILES
print(files)
if is_ajax(request=request) and form.is_valid():
print("the request is ajax and the form is valid")
title = form.cleaned_data.get("content", "")
print("Title ", title)
post_instance = form.save(commit=False)
post_instance.author = request.user
result['success'] = True
return JsonResponse(result)
$.ajax({
url: $("#CreatePostModal").attr("data-url"),
data:$("#CreatePostModal #createPostForm").serialize(),
method: "post",
dataType: "json",
success: (data) => {
if (data.success) {
setTimeout(() => {
$(e.target).next().fadeOut();
ResetForm('createPostForm', 'PreviewImagesContainer')
$("#CreatePostModal").modal('hide')
$(e.target.nextElementSibling).fadeOut()
alertUser("Post", "has been created successfully!")// alerting the user
}, 1000)
console.log(data.title)
} else {
$("#createPostForm").replaceWith(data.formErrors);
$("#PreviewImagesContainer").html("");
$("#CreatePostModal").find("form").attr("id", "createPostForm");
$(e.target.nextElementSibling).fadeOut()
};
$(e.target).prop("disabled", false);
},
error: (error) => {
console.log(error)
}
})
});
Here is the form file
class PostForm(forms.ModelForm):
images = forms.ImageField(
required=False,
widget=forms.ClearableFileInput(attrs={
'multiple': True,
})
)
class Meta:
model = Post
fields = ("content",)
widgets = {
"content": forms.Textarea(attrs={"placeholder": "Tell us something today....", "rows": 5, "label": ""})
}
again the imagefield has a manytomany relationship with the post model.
What I am doing wrong?
Here is the modal where I am rendering the form itself with crispy form
<!-- create post modal -->
<div class="modal fade" id="CreatePostModal" data-url="{% url 'post-list-view' %}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Creating Post</h5>
<button type="button" class="close" data-bs-dismiss="modal" aria-label="Close">
×
</button>
</div>
<div class="modal-body">
<div class="container-fluid">
{% crispy form %}
<div id="PreviewImagesContainer">
</div>
</div>
</div>
<div class="modal-footer float-right">
<button type="button" class="btn btn-sm btn-dark" data-bs-dismiss="modal">Cancel</button>
<button type="submit" form="createPostForm" id="createPostBtn" class="btn btn-sm btn-primary">Post</button>
<span class="loading-icon"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
</div>
</div>
</div>
<!-- end of create post modal -->
I finally solved it using the FormData of javascript by looping through of the files that I am getting from the input then append it to the data of the FormData.
let imageFiles = []
$("#CreatePostModal").on('change', (e) => {
$(postImagesPreviewContainer).html("")
if ($(e.target).attr("id") !== "id_images") return;
var filenames = "";
for (let i = 0; i < e.target.files.length; i++) {
filenames += (i > 0 ? ", " : "") + e.target.files[i].name;
}
e.target.parentNode.querySelector('.custom-file-label').textContent = filenames;
//why is the this element returning the document and not the target itself
// check the length of the files to know what template to make
const files = e.target.files
const numberOfImages = files.length
let gridColumnSize;
if (numberOfImages > 5 | numberOfImages === 0) return;
var row = document.createElement("div")
row.setAttribute("class", "post-images")
for (file of files) {
const postImageChild = document.createElement("div");
postImageChild.setAttribute("class", "post-images__child_down")
const reader = new FileReader();
reader.onload = () => {
img = document.createElement("img")
img.setAttribute("src", reader.result)
img.onload = (e) => {
// here i will process on resizing the image
const canvas = document.createElement("canvas")
const max_width = 680
const scaleSize = max_width / e.target.width
canvas.width = max_width
canvas.height = e.target.height * scaleSize
var ctx = canvas.getContext("2d") // setting the context of the canvas
ctx.drawImage(e.target, 0, 0, canvas.width, canvas.height)
const encodedSource = ctx.canvas.toDataURL(e.target, 'image/png', 1)
const processedImg = document.createElement("img") // create a processed image and return it.
processedImg.src = encodedSource
$(postImageChild).append(processedImg)
imageFiles.push(processedImg)
}
}
$(row).prepend(postImageChild)
$(postImagesPreviewContainer).append(row);
reader.readAsDataURL(file)
}
After getting and resizing all the images I made the ajax call like that:
$("#CreatePostModal").on("click", (e) => {
if ($(e.target).attr("id") !== "createPostBtn") return;
e.preventDefault();
e.target.setAttribute("disabled", true);
$(e.target.nextElementSibling).fadeIn()
var form = $("#createPostForm")[0]
var data = new FormData(form); // getting the form data
console.log("this is the data", data)
for (var i = 0; i < imageFiles.length; i++) { // appending images to data
data.append('images', imageFiles[i]);
};
$.ajax({
url: $("#CreatePostModal").attr("data-url"),
data: data, //$("#CreatePostModal #createPostForm").serialize(),
method: "post",
processData: false,
cache: false,
contentType: false,
dataType: "json",
success: (data) => {
if (data.success) {
setTimeout(() => {
$(e.target).next().fadeOut();
ResetForm('createPostForm', 'PreviewImagesContainer')
$("#CreatePostModal").modal('hide')
$(e.target.nextElementSibling).fadeOut()
alertUser("Post", "has been created successfully!")// alerting the user
}, 1000)
} else {
$("#createPostForm").replaceWith(data.formErrors);
$("#PreviewImagesContainer").html("");
$("#CreatePostModal").find("form").attr("id", "createPostForm");
$(e.target.nextElementSibling).fadeOut()
};
$(e.target).prop("disabled", false);
},
error: (error) => {
console.log(error)
}
})
});
and finally getting the images from request.FILES in the views.py file.
def post(self, request, *args, **kwargs):
form = PostForm(request.POST or None, request.FILES or None)
result = {}
files = request.FILES.getlist("images")
if is_ajax(request=request) and form.is_valid():
post_obj = form.save(commit=False)
post_obj.author = request.user
print(post_obj)
post_obj.save()
for file in files:
new_file = Files(image=file)
new_file.save()
post_obj.images.add(new_file)
post_obj.save()
result['success'] = True
return JsonResponse(result)

Why does Django/console give me an error of missing value for stripe.confirmCardPayment intent secret, saying it should be a client secret string?

I'm trying to learn this tutorial, the custom payment flow last bit to integrate stripe with Django
https://justdjango.com/blog/django-stripe-payments-tutorial
in my views.py, I have these views
class StripeIntentView(View):
def post(self, request, *args, **kwargs):
try:
req_json = json.loads(request.body)
customer = stripe.Customer.create(email=req_json['email'])
price = Price.objects.get(id=self.kwargs["pk"])
intent = stripe.PaymentIntent.create(
amount=price.price,
currency='usd',
customer=customer['id'],
metadata={
"price_id": price.id
}
)
return JsonResponse({
'clientSecret': intent['client_secret']
})
except Exception as e:
return JsonResponse({'error': str(e)})
class CustomPaymentView(TemplateView):
template_name = "custom_payment.html"
def get_context_data(self, **kwargs):
product = Product.objects.get(name="Test Product")
prices = Price.objects.filter(product=product)
context = super(CustomPaymentView, self).get_context_data(**kwargs)
context.update({
"product": product,
"prices": prices,
"STRIPE_PUBLIC_KEY": settings.STRIPE_PUBLIC_KEY
})
return context
and in my urls I have
from django.contrib import admin
from django.urls import path
from products.views import stripe_webhook
from products.views import StripeIntentView, CustomPaymentView
urlpatterns = [
path('admin/', admin.site.urls),
path('create-payment-intent/<pk>/', StripeIntentView.as_view(), name='create-payment-intent'),
path('custom-payment/', CustomPaymentView.as_view(), name='custom-payment')
and in my custom_payment.html I have
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>Custom payment</title>
<script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
<script src="https://js.stripe.com/v3/"></script>
<link rel="stylesheet" href="{% static 'products/global.css' %}">
</head>
<body>
<section>
<div class="product">
<div class="description">
<h3>{{ product.name }}</h3>
<hr />
<select id='prices'>
{% for price in prices %}
<option value="{{ price.id }}">${{ price.get_display_price }}</option>
{% endfor %}
</select>
</div>
<form id="payment-form">{% csrf_token %}
<input type="text" id="email" placeholder="Email address" />
<div id="card-element">
<!--Stripe.js injects the Card Element-->
</div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay</span>
</button>
<p id="card-error" role="alert"></p>
<p class="result-message hidden">
Payment succeeded, see the result in your
Stripe dashboard. Refresh the page to
pay again.
</p>
</form>
</div>
</section>
<script>
var csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
var stripe = Stripe("{{ STRIPE_PUBLIC_KEY }}");
document.querySelector("button").disabled = true;
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: 'Arial, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d"
}
},
invalid: {
fontFamily: 'Arial, sans-serif',
color: "#fa755a",
iconColor: "#fa755a"
}
};
var card = elements.create("card", { style: style });
// Stripe injects an iframe into the DOM
card.mount("#card-element");
card.on("change", function (event) {
// Disable the Pay button if there are no card details in the Element
document.querySelector("button").disabled = event.empty;
document.querySelector("#card-error").textContent = event.error ? event.error.message : "";
});
var form = document.getElementById("payment-form");
form.addEventListener("submit", function(event) {
event.preventDefault();
var selectedPrice = document.getElementById("prices").value
// Complete payment when the submit button is clicked
fetch(`/create-payment-intent/${selectedPrice}/`, {
method: "POST",
headers: {
"Content-Type": "application/json",
'X-CSRFToken': csrftoken
},
body: JSON.stringify({
email: document.getElementById('email').value
})
})
.then(function(result) {
return result.json();
})
.then(function(data) {
payWithCard(stripe, card, data.clientSecret);
});
});
// Calls stripe.confirmCardPayment
// If the card requires authentication Stripe shows a pop-up modal to
// prompt the user to enter authentication details without leaving your page.
var payWithCard = function(stripe, card, clientSecret) {
loading(true);
stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: card
}
})
.then(function(result) {
if (result.error) {
// Show error to your customer
showError(result.error.message);
} else {
// The payment succeeded!
orderComplete(result.paymentIntent.id);
}
});
};
/* ------- UI helpers ------- */
// Shows a success message when the payment is complete
var orderComplete = function(paymentIntentId) {
loading(false);
document
.querySelector(".result-message a")
.setAttribute(
"href",
"https://dashboard.stripe.com/test/payments/" + paymentIntentId
);
document.querySelector(".result-message").classList.remove("hidden");
document.querySelector("button").disabled = true;
};
// Show the customer the error from Stripe if their card fails to charge
var showError = function(errorMsgText) {
loading(false);
var errorMsg = document.querySelector("#card-error");
errorMsg.textContent = errorMsgText;
setTimeout(function() {
errorMsg.textContent = "";
}, 4000);
};
// Show a spinner on payment submission
var loading = function(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("button").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("button").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}
};
</script>
</body>
</html>
The tutorial was missing a csrf token so I added that and the card element loaded up, and also I had to add an id of prices to the select
Then I got this error
(index):1 Uncaught (in promise) IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client_secret string.
at X ((index):1)
at Q ((index):1)
at uo ((index):1)
at (index):1
at (index):1
at e.<anonymous> ((index):1)
at e.confirmCardPayment ((index):1)
at payWithCard ((index):104)
at (index):94
Can anyone help me with this ? Thanks
i would suggest adding an additional line to check what is the value of data (and data.clientSecret). It looks like clientSecret may not have a value, or may not be a string.
.then(function(data) {
console.log(data);
payWithCard(stripe, card, data.clientSecret);
});
You would then need to trace why clientSecret does not have the expected value.

Image is not passing to view (cropperjs, django, ajax)

I have an user profile where he can crop his image, and so after cropping, and printing data in view nothing is passed to view
Form
<form method="post" action="change_photo/" id="avatar_changing_form">
{% csrf_token %}
<label for="upload_image">
{% if item.image%}
<img src="{{item.image.url}}" alt="" style="max-height:300px">
{%else%}
<img src="{%static 'avatar_sample.png' %}" id="uploaded_image"
class="img-responsive img-circle" />
{%endif%}
<div class="overlay">
<div class="text">Click to Change Profile Image</div>
</div>
<!-- <input type="file" name="image" class="image" id="upload_image" style="display:none" /> -->
{{imageForm.image}}
</label>
</form>
JS & Ajax
$(document).ready(function(){
const imageForm = document.getElementById('avatar_changing_form')
const confirmBtn = document.getElementById('crop')
const input = document.getElementById('upload_image')
const csrf = document.getElementsByName('csrfmiddlewaretoken')
var $modal = $('#modal');
var image = document.getElementById('sample_image');
var cropper;
$('#upload_image').change(function (event) {
var files = event.target.files;
var done = function (url) {
image.src = url;
$modal.modal('show');
};
if (files && files.length > 0) {
reader = new FileReader();
reader.onload = function (event) {
done(reader.result);
};
reader.readAsDataURL(files[0]);
}
});
$modal.on('shown.bs.modal', function () {
cropper = new Cropper(image, {
aspectRatio: 1,
viewMode: 3,
preview: '.preview'
});
}).on('hidden.bs.modal', function () {
cropper.destroy();
cropper = null;
});
$('#crop').click(function () {
cropper.getCroppedCanvas().toBlob((blob) => {
console.log('confirmed')
const fd = new FormData();
fd.append('csrfmiddlewaretoken', csrf[0].value)
fd.append('file', blob, 'my-image.png');
$.ajax({
type: 'POST',
url: imageForm.action,
enctype: 'multipart/form-data',
data: fd,
success: function (response) {
console.log('success', response)
$modal.modal('hide');
$('#uploaded_image').attr('src', fd);
},
error: function (error) {
console.log('error', error)
},
cache: false,
contentType: false,
processData: false,
})
});
});
})
View
def change_photo(request):
if request.user.is_authenticated and Guide.objects.filter(user = request.user).exists():
item = Guide.objects.get(user=request.user)
if request.method == "POST":
Photoform = ChangeImageForm(request.POST or None, request.FILES or None, instance = item)
if Photoform.is_valid():
print(Photoform.cleaned_data['image'])
Photoform.save()
return HttpResponseRedirect('/profile/')
form
class ChangeImageForm(ModelForm):
class Meta:
model = Guide
fields = ['image']
def __init__(self, *args, **kwargs):
super(ChangeImageForm, self).__init__(*args, **kwargs)
self.fields['image'].widget = FileInput(attrs={
'name':'image',
'class':'image',
'id':'upload_image',
'style':'display:none'
})
When i print image field from cleaned data in terminal displays "none", and when i load image through admin everything working good, can pls someone tell me where the problem is?

Display custom success or error progress bar

I am making a django app that displays a progress bar. So far I have got it working to display a progress bar using this library and the following code which they suggested.
<div class='progress-wrapper'>
<div id='progress-bar' class='progress-bar' style="background-color: #68a9ef; width:
0%;"> </div>
</div>
<div id="progress-bar-message">Waiting for progress to start...</div>
<script src="{% static 'celery_progress/celery_progress.js' %}"></script>
<script>
// vanilla JS version
document.addEventListener("DOMContentLoaded", function () {
var progressUrl =
"{%
try:
url 'celery_progress:task_status' task_id
catch:
pprint("PHEW")
%}";
CeleryProgressBar.initProgressBar(progressUrl);
});
</script>
However, how might i integrate the above code with the code below to get it to display success or error:
function customSuccess(progressBarElement, progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/aww-yeah.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'success!'
}
function customError(progressBarElement, progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/okay-guy.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'shucks.'
}
CeleryProgressBar.initProgressBar(taskUrl, {
onSuccess: customSuccess,
onError: customError,
});
just change the script in your html with this one:
<script>
function customSuccess(progressBarElement,
progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/aww-yeah.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'success!'
}
function customError(progressBarElement, progressBarMessageElement) {
progressBarElement.innerHTML = (
'<figure class="image"><img src="/static/projects/images/okay-guy.jpg"></figure>'
)
progressBarElement.style.backgroundColor = '#fff';
progressBarMessageElement.innerHTML = 'shucks.'
}
document.addEventListener("DOMContentLoaded", function () {
var progressUrl = "{% url 'celery_progress:task_status' task_id %}";
CeleryProgressBar.initProgressBar(progressUrl, {
onSuccess: customSuccess,
onError: customError,
});
});
</script>
i tried it with the same app. it works! :)
don't forget to change this one to a valid url:
<img src="/static/projects/images/aww-yeah.jpg">)

Comparing value from html to context

I need to compare a value (variable) extracted from a page to context.
For example:
Color is a dropdown selection.
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
{% if context_value == selected_color %}
.. do something
{% endif %}
})};
Is this possible ? If not, is there some solution for such case ?
I recommend you use Ajax to communicate asynchronously between JavaScript and python (without refreshing the page).
your JS:
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
$.ajax({
method: "POST",
url: 'color_check',
data: selected_color,
success: handleFormSuccess,
error: handleFormError,
})
})
function handleFormSuccess(data, textStatus, jqXHR){
.. do something
}
function handleFormError(jqXHR, textStatus, errorThrown){}
};
Your python view:
def color_check(request):
if request.is_ajax():
selected_color = request.POST
context_value = 'Red'
if selected_color == context_value:
return JsonResponse(True)
EDIT: Arun Singh's solution is simpler and works too. I would only make the paragraph hidden from the user:
<p style="display:none" id="my-data" data-name="{{context_value}}"></p>
<p id='data'>{{ context_value }}</p>
or
<p id="my-data" data-name="{{context_value}}"></p>
$(document).ready(function(){
$("#color").change(function() {
var selected_color = $(this).val() ;
var djangoData = $('#data').val();
if (djangoData === selected_color){
console.log('do something)
}else{
console.log('do something else')
}
})};