Show loading gif until the django view performs the data processing and renders the template with this data - django

I have a django project where the page has multiple nav links representing different agents. On clicking any nav link, the urls.py redirects to nav specific view and the view needs to perform some processing to get the data needed to render the template. However as this is syncrhonous rendering it takes a long while to load data (in the order of 15-20s).
Below is my urls.py:
from django.urls import path
from . import views
app_name = 'agent'
urlpatterns = [
path('agent1/', views.agent1, name='agent1'),
path('agent2/', views.agent2, name='agent2'),
path('agent3/', views.agent3, name='agent3'),
path('agent4/', views.agent4, name='agent4'),
]
My views method looks as below:
def agent1(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return render(request, 'manager/data.html', {'agent_data': agent_data, 'agent_details': agent_details})
I am using the {{ agent_data.name }}, {{ agent_data.code }}, {{ agent_data.qty }} and {{ agent_data.price }} along with data from agent_details dictionary in my html to populate a table's rows. How should I change my view method, so that it loads the data via AJAX (javascript) in order to show a loading gif in the meantime and also provide me the data so that I can populate the table. Could someone help me with the Ajax code and the steps as I am new to this technology and not finding any help going through the online tutorials.

So for this to work with ajax, you'll need some javascript in manager/data.html which knows the url to fetch data from.
As an example, I've got an ajax setup which checks a given email address isn't already in use;
(function($) {
$(document).ready(function() {
var validateEmailURL = $section_user_signup.data('ajax-email-url');
function validateEmailUnique() {
var valid = true;
clearError($email);
// Fetch unique status of the provided email
$.ajax({
async: false,
url: validateEmailURL,
method: 'POST',
type: 'POST',
dataType: 'json',
data: {
'email': $email.val(),
'csrftoken': $form.find('input[name="csrfmiddlewaretoken"]').val()
},
success: function (response) {
valid = true;
},
error: function (response) {
setError($email, response["responseJSON"]["error"]);
valid = false;
}
});
return valid;
}
});
})(window.jQuery);
This javascript uses the data attribute of a div for the URL to check;
<div data-ajax-email-url="{% url 'account_ajax_validate_email' %}">
The view which the ajax call goes to looks like this;
def ajax_check_email_unique(request, *args, **kwargs):
"""
Return an JsonResponse to identify if an email is unique.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
if request.is_ajax and request.method == "POST":
email = request.POST.get('email')
if email_address_exists(email):
return JsonResponse(
{
"error":
"Email address already exists. Click "
f"here "
"to login"
},
status=400
)
return JsonResponse(
{"email": email},
status=200
)
# some error occurred
return JsonResponse({"error": ""}, status=400)
The important thing for any view which will be used by javascript is that you return a JsonResponse.
So if I was you, I'd setup a new view for ajax, and that makes your existing one really simple;
def agent1_ajax(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return JsonResponse({
"agent_data": agent_data, "agent_details": agent_details
}, status=200)
def agent1(request):
return render(request, 'manager/data.html', {})
And as far as a loading gif goes, you'd need an element that contains the gif and then you can bind to the ajax event to show/hide;
$(document).ajaxStart(function() {
$("#loading").show();
});
$(document).ajaxStop(function() {
$("#loading").hide();
});

Related

How to execute Django view through Stripe signal?

After a customer has unlocked a post, I want to add his profile to the list of profiles that have unlocked the post.
def unlockpostview(request, post_id):
if userthatisunlockingprofile in post.unlockedby.all():
pass
else:
post.unlockedby.add(userthatisunlockingprofile)
When the user has paid I listen to the Stripe's succeeded event and execute the view like so:
if (result.paymentIntent.status === 'succeeded') {
$.get("{% url 'postsapp:unlockpostview' post.post_id %}")
window.alert("Unlocked")
}
My problem was that anybody could go to the post and simply add /unlock to the end of the URL and execute the view.
Then I added
if request.is_ajax():
to the view but this still isn't the optimal solution.
I do not expect a complete solution to this but please point me in the right direction if you can. Thanks :)
in POST request you don't give variables to the URL and also a CSRF token is a gift with it. And dump users can't send POST requests on browser easily
if (result.paymentIntent.status === 'succeeded') {
$.ajax({
type: "POST",
url: "{% url 'postsapp:unlockpostview' %}",
headers: {
'X-CSRFToken': '{{ csrf_token }}'
},
data: {
'operation': 'unlock_action',
'post_id': '{{ post.post_id }}', //quotes are important:)
'user': {{ request.user.id }}
},
dataType: "json",
success: function(response) {
if(response.success == true){
window.alert("Unlocked")
}
},
error: function(rs, e) {
console.log('error')
}
});
}
Just remove the post_id attribute from your url or:
urls.py
urlpatterns = [
path('unlockpostview/', views.unlockpostview, name="unlockpostview"),
]
views.py:
def unlockpostview(request):
if request.user.is_authenticated:
if request.method == 'POST' and request.POST.get("operation") == "unlock_action":
post = Post.objects.get(id=request.POST.get("post_id"))
userthatisunlockingprofile = User.objects.get(id=request.POST.get("user"))
#then your logic
if userthatisunlockingprofile in post.unlockedby.all():
ctx={"succes":True,"status":"exist"}
else:
post.unlockedby.add(userthatisunlockingprofile)
ctx={"succes":True,"status":"created"}
return JsonResponse(ctx)
else:
return redirect("home")
#Here is the security

Navigating to different page instead of Ajax call

I am trying to save a form via ajax as I don't want to reload the page. It is working not completely but it is updating the data except the image or video or any upload file we give.
but after updating it is coming back to the ajax page but the url is diiferent and success message is coming on that page.
I am sharing some of the logic but if more information is required, please let me know .
js code :
$(document).ready(function() {
$('#contentdataform').submit(function(e) {
e.preventDefault();
$.ajax({ // create an AJAX call...
data: $(this).serialize(),
type: 'POST',
url: 'updatecontent',
success: function() {
mess("Success"); //mess is a function to generate a disappearing message box.
},
});
return false;
});
});
function updatecontentform(){
console.log('starting');
document.getElementById('contentdataform').submit();
}
views.py
#csrf_exempt
def updatecontent(request):
print("--------------------")
if request.method == "POST":
id = request.session['content_id']
fm = ContentForm(request.POST, instance= Content.objects.get(pk = id))
print("fm")
print(fm)
if fm.is_valid:
print("valid form")
form = fm.save(commit=False)
form.save()
else:
print("Not Valid")
return JsonResponse("Success", safe= False)
the output should be the message on the same page but it is reflecting on the new page with url '127.0.0.1:8000/updatecontent'

Multiple Django subforms displayed with ajax in a main form but data not registered when subforms are submited (without error)

EDIT #1
well maybe because it is not clear in my head
and your are right saying Django forms are not intented to be used with ajax that why I wonder if there is way way doing what I want using Django forms without losing power of Django forms validation
I will try to clear my demand
considering this UI below
When user reach this "patient page" = main form (image 1), he can select "Inclusion form = subform (INCL-001 for instance) in the menu
I want the corresponding form to be displayed without refresh the page ; that why I have used ajax to display the corresponding form
If the corresponding form does not exist, it is a CREATION and a blank "Inclusion form" is displayed : user can enter data and save
If the corresponding form already exist, it is a UPDATE and a prefilled "Inclusion form" is displayed : user can update data and save or delete the form
what would be an elegant way of doing that?
I have a main form (patient.html) and I want to display different subforms (inclusion.html for instance) with ajax in this form.
I manage to display a subform when user click on a button in the main form
But, data are not stored when submitted and it is quite normal as no view is linked to this subform
I have read about this in different post but I miss the concept how to...
I thinks it is a common task with web so probably Django as an easy way to do it?
thanks for advices
urls.py
app_name='ecrf'
urlpatterns = [
path('', views.index, name='index'),
path('patient/<pk>', views.patient, name='patient'),
path('sub_form', views.sub_form, name='sub_form'),
]
views.py
def patient(request,pk):
patient = {"id":1,"num":'A-001',"dat":'Mon 11, novembre 2020',"arm":1,"doc":'Slater'}
return render(request, 'ecrf/patient.html', {'patient':patient})
def sub_form(request):
if request.is_ajax():
return JsonResponse(
{
'html_inclusion': render_to_string(
'ecrf/inclusion.html',
{
'form': InclusionForm,
'language': request.session.get('language')
},
request=request
),
},
status=200
)
js
$(document).ready(function () {
...
$("#add").on("click",function (event) {
var csrftoken = getCookie('csrftoken');
$.ajax({
type: "POST",
url: '/ecrf/sub_form',
data: {
csrfmiddlewaretoken: csrftoken,
},
dataType: 'html',
success: function (data) {
$("#sub_form").children().remove();
obj = JSON.parse(data);
$("#sub_form").append(obj.html_inclusion);
},
error: function (data) {
console.log('error');
}
});
});
$("body")
.on("click", '#inclusion', function (event) {
console.log('inclusion click')
})
});

calling ajax function in views.py to get the data from database

I want to fetch data from the database. I am using ajax function to get it in the index.html. How should I call this ajax function to the views.py so i can display it in view. How should I attain it?
My codes:
index.html
<script type="text/javascript">
function submitData(){
// Get answer from the input element
var dt = document.getElementById("major1").value;
var dtt = document.getElementById("major2").value;
var dttt = document.getElementById("major3").value;
var dtttt = document.getElementById("major4").value;
var dttttt = document.getElementById("major5").value;
// add the url over here where you want to submit form .
var url = "{% url 'home' %}";
$.ajax({
url: url,
data: {
'major1': dt,
'major2': dtt,
'major3': dttt,
'major4': dtttt,
'major5': dttttt,
},
dataType: 'JSON',
success: function(data){
// show an alert message when form is submitted and it gets a response from the view where result is provided and if url is provided then redirect the user to that url.
alert(data.result);
if (data.url){
window.open(data.url, '_self');
}
}
});
}
</script>
views.py:
def home(request):
majors = Major.objects.filter(percentages__isnull=False).distinct().order_by("pk")
if request.method == 'POST':
form = request.POST.get('be_nextyr_total')
line_chart = pygal.Line(width=1500)
line_chart.title = 'Budget Estimation'
context = {
"chart": line_chart.render_data_uri(),
'majors': majors
}
return render(request, "website/index.html" , context )
If you are doing a post request with Ajax, then you have to write in your ajax code like
type: "POST",
if you want to access your form data in view than you have to write
request.POST.get('your_variable_name_like_major1')

How to update database after clicking on html element?

In my flask application, I have a points column in the database, which is initialized to 0 on user registration. Now, I want to be able to update the points in database after clicking an html element.
class User(UserMixin, db.Model):
points = db.Column(db.Integer)
#bp.route('/activity1')
#login_required
def activity1():
return render_template('activity1.html', title='Activity 1')
Activity1.html has the point-generating html element
#bp.route('/register', methods=['GET', 'POST'])
def register():
...
form = RegistrationForm()
if form.validate_on_submit():
user.points = 0
db.session.add(user)
db.session.commit()
...
<a data-toggle="collapse" class="w3-large" href="#tip4" onclick="getPoints()">...</a>
<script>
function getPoints(){
points += 20; #How do i access the database.points in this case?
}
</script>
I'm not sure if I understand the code correctly, but are you asking how to update the number of points from activity1.html in the last code snippet? That won't work, the HTML is rendered on the server and once presented to the browser, there's no link between the two.
If you need to pass some data to the template while rendering it, you have to pass it in the render_template call, e.g.
render_template('activity1.html', title='Activity 1', user=user)
Then you can access it in the template like this:
<script>
function getPoints(){
points = {{ user.points|int }} + 20;
// post to backend to update data in database (using jQuery)
$.ajax({
type: 'POST',
contentType: 'application/json',
data: {
id: "{{ user.id }}",
points: points
},
dataType: 'json',
url: 'http://xxx/update_points',
success: function (e) {
// successful call
},
error: function(error) {
// an error occured
}
});
}
</script>
If you then want to update the user points in the database, you'll have to make a request to the server, which will take care of this. On the server side in Flask, define a view to handle this request:
#bp.route('/update_points', methods=['POST'])
def update_points():
data = request.get_json()
# don't know exactly what's the model and how to get the 'user' instance
user = User.get_user_by_id(data['id'])
user.points = data['points']
db.session.add(user)
db.session.commit()
Take it as an example code, I didn't test it and ommited quite a lot of details (e.g. how to get user instance in update_points etc.) Also, I might have missed what you are really asking for.