Avoid Multiple submits in Django - django

I am using auto submit in my forms.
Form gets automatically submitted when 10 digits are entered in the number field. This actually comes from USB RFID card reader upon scannig the card (It works as keyboard emulator).
I compare the number with the existing numbers in the database and then identify the user and perform some database transactions.
What happens is that the card reader reads the number multiple times, and due to that before all the database transactions complete for that input, another input is accepted (same number) within 1s and the consistency is lost before the first transaction is fully completed.
Kindly advise.
VIEWS.PY File:
#login_required
def markattdview(request):
# for val in studmst.objects.filter(ccownerid=request.user.id)
# TO DOooo
# select studid from attddetail where studentID= (select student ID from studmst where ccownerid = currentuser.ID)
# lastattd=attddetail.objects.filter()
# Showing last 10 users whose attendance have been marked by the logged in user
#
form_attd = markattdform()
attdsuccess = ""
identified_user = ""
identified_studname=""
loggedin_userid=request.user.id
loggedinuser=request.user.username
print("Logged in User : " + loggedinuser)
if request.method == 'POST':
studmstobj = studentmaster()
attdform = markattdform(request.POST)
unknown_cardno = request.POST.get('cardno')
if attdform.is_valid():
# form_attd.punchtime = datetime.datetime.now()
# attdform.save(commit=True)
obj = attdform.save(commit=False)
obj.punchtime = datetime.datetime.now()
attdsuccess = "Card not assigned. Kindly verify if the card has been registered."
#studlist = studmst.objects.filter(ccownerid = request.user.id)
#print (studlist[0])
# for usr in studlist(cardnumber = obj.cardno):
# Identifying the user who swyped the card
for usr in studentmaster.objects.filter(cardnumber = obj.cardno):
#WORKING FINE AFTER REMOVING WORKER AND REDISTOGO
#saving the details for identified User
identified_studname= usr.studentname
identified_cardno=usr.cardnumber
identified_studid = usr.studentid
punchdatetime=obj.punchtime
obj.studentname=identified_studname
obj.studentid_id=identified_studid
print('User Identified for card number '+ identified_cardno)
print( "Identified user - " + identified_studname)
databasetransactions(identified_studid,identified_studname,punchdatetime)
JAVASCRIPT FOR AUTO FORM SUBMISSION:
$(function () {
console.log(thetext);
$('#thetext').bind('change keyup', function () {
if ($(this).val().length >= 10) {
$('#Form').submit();
}
})
});

I would add two variables: formSubmitted and previousInput. And every time the length of the text is > 10, I would check if the form was submitted and also if previous input is equal to the current input.
When you submit the form - you set the value of formSubmitted to true.
And in this case your form will be submitted if it wasn't submitted already, or if the previous input is different than the current one which means that another text was entered and it should also be submitted.
And finally on ajax success or error you set the formSubmited to false back to allow new form submitions.
Remember that your form will be also submited if the current input is different than the previous input!
$(function () {
var formSubmitted = false;
var previousInput = null;
$('#thetext').bind('change keyup', function () {
if ($(this).val().length >= 10 && (!formSubmitted || previousInput != $(this).val())) {
var formData = new FormData($('#Form')[0]);
previousInput = $(this).val();
formSubmitted = true;
$.ajax(
url: '/endpoint.php',
method: 'POST',
processData: false,
contentType: false,
data: formData,
success: function(data, status, xhr) {
formSubmitted = false;
},
error: function(data, status, xhr) {
formSubmitted = false;
}
});
}
})
});

Related

Getting query length with Ajax / Django

According to the selection in the form, I want to get the number of records that match the id of the selected data as an integer.
Here is my view :
def loadRelationalForm(request):
main_task_id = request.GET.get('main_task_id')
relational_tasks = TaskTypeRelations.objects.filter(main_task_type_id = main_task_id)
data_len = len(relational_tasks)
return JsonResponse({'data': data_len})
Here is my ajax :
<script>
$("#id_user_task-0-task_types_id").change(function () {
const url = $("#usertask-form").attr("data-relationalform-url");
const mainTaskId = $(this).val();
$.ajax({
url: url,
data: {
'main_task_id': mainTaskId,
},
success: function (resp) {
console.log(resp.data);
}
});
});
I want to write the number of relational tasks associated with the main_task_id of the selected data in the form. But I couldn't do it. Thanks for your help. Kind regards
Make sure the data you're pulling is an integer.
This line :
main_task_id = request.GET.get('main_task_id')
Might be a string, and the resulting query would find no match.
Aside, if you want a number of related objects, you have more efficients way to do it :
def loadRelationalForm(request):
main_task_id = request.GET.get('main_task_id')
if main_task_id:
main_task_id = int(main_task_id)
main_task = TheMainTaskModel.objects.get(id=main_task_id)
related_task_count = main_task.tasktyperelations_set.count()
return JsonResponse({'data': related_task_count})
See doc about reverse relationship and .count()

Why is flask jsonify returning unidentified?

I am using fetch on the frontend to send data to my flask backend in order to make a movie seat booking. The whole process works fine until the client awaits the response, which is "undefined" . So , basically the database saves the data , the only problem is the response which is sent to the client. I used jsonify which usually works fine. Can anybody tell me what I am missing? Thanks in advance.
Here is the JS code :
function sendReservationToServer() {
const selectedSeats = sessionStorage.getItem('selectedSeats')
const reservation = { userId, selectedSeats, showTimeId, movieHallId }
fetch('/bookSeats', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(reservation)
}).then(response => {
response.json()
}).then(data => {
theatreHall.innerHTML = `${data} <br> <a href='/home'>Back to main menu</a>`
console.log(`${data}`)
}).catch(err => infoMsg.textContent = err)
sessionStorage.clear()
}
And this is the flask controller which handles the request:
#app.route("/bookSeats", methods=["POST"])
def book_seats():
selected_seats = request.json
user_id = selected_seats.get('userId')
seats = json.loads(selected_seats.get('selectedSeats'))
movie_hall_id = selected_seats.get('movieHallId')
seat_ids = []
showtime_id = selected_seats.get('showTimeId')
for seat in seats:
seat_ids.append(db.session.query(Seat).filter(
Seat.seat_number == seat).filter(Seat.movie_hall_id == movie_hall_id).all()[0].stid)
for seat in seat_ids:
reserved_seat = ReservedSeat(
seat_id=seat, show_time=showtime_id, user=user_id)
db.session.add(reserved_seat)
db.session.commit()
reservation = Reservation(
user=user_id, show_time=showtime_id, number_of_tickets=len(seat_ids))
db.session.add(reservation)
db.session.commit()
message = f'{seats} booked successfully'
return jsonify(message)
data is undefined because the first then does not return anything. Either make it return response.json() or move everything in the second then to the first and replace data with response.json().

Django2: Submit and store blobs as image files

I have made a few Django projects after having read the tutorial but I am by no means an expert in Django.
I am trying to take a screenshot of the current page and store it (if one does not exist).
To achieve this, we require a few things:
function to get screen shot of current page
function to async post this image to a view which should store it
view that stores the posted image
However, the screen shot function results in a Blob and I am having trouble getting a Django view to properly handle this.
A demo project is available here: https://gitlab.com/SumNeuron/so_save_blob
Function for screenshot
const screenshot = (function() {
function urlsToAbsolute(nodeList) {
if (!nodeList.length) {
return [];
}
var attrName = 'href';
if (nodeList[0].__proto__ === HTMLImageElement.prototype
|| nodeList[0].__proto__ === HTMLScriptElement.prototype) {
attrName = 'src';
}
nodeList = [].map.call(nodeList, function (el, i) {
var attr = el.getAttribute(attrName);
if (!attr) {
return;
}
var absURL = /^(https?|data):/i.test(attr);
if (absURL) {
return el;
} else {
return el;
}
});
return nodeList;
}
function addOnPageLoad_() {
window.addEventListener('DOMContentLoaded', function (e) {
var scrollX = document.documentElement.dataset.scrollX || 0;
var scrollY = document.documentElement.dataset.scrollY || 0;
window.scrollTo(scrollX, scrollY);
});
}
function capturePage(){
urlsToAbsolute(document.images);
urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
var screenshot = document.documentElement.cloneNode(true);
var b = document.createElement('base');
b.href = document.location.protocol + '//' + location.host;
var head = screenshot.querySelector('head');
head.insertBefore(b, head.firstChild);
screenshot.style.pointerEvents = 'none';
screenshot.style.overflow = 'hidden';
screenshot.style.webkitUserSelect = 'none';
screenshot.style.mozUserSelect = 'none';
screenshot.style.msUserSelect = 'none';
screenshot.style.oUserSelect = 'none';
screenshot.style.userSelect = 'none';
screenshot.dataset.scrollX = window.scrollX;
screenshot.dataset.scrollY = window.scrollY;
var script = document.createElement('script');
script.textContent = '(' + addOnPageLoad_.toString() + ')();';
screenshot.querySelector('body').appendChild(script);
var blob = new Blob([screenshot.outerHTML], {
type: 'text/html'
});
return blob;
}
return capturePage
})()
Function to async post Blob
function setupAjaxWithCSRFToken() {
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
}
function asyncSubmitBlob( url, blob ) {
var fd = new FormData();
fd.append('image', blob);
$.ajax({
url: url,
type: "POST",
data: fd,
contentType: false,
processData: false,
success: function(response){ console.log(response) },
error: function(data){ console.log(data) }
})
}
So to submit a screenshot of the current page:
setupAjaxWithCSRFToken()
const page = window.location.pathname;
const blob_url = "{% url 'my-app:post_blob' 'REPLACE' %}".replace(/REPLACE/,page == '/' ? '' : page)
asyncSubmitBlob( blob_url, screenshot() )
View to store the posted blob image
urls.py
...
from django.urls import include, path
...
app_name='my-app'
url_patterns=[
...
path('post_blob/', views.post_blob, {'page':'/'},name='post_blob'),
path('post_blob/<page>', views.post_blob,name='post_blob'),
...
]
views.py
from .models import PageBlob
...
def post_blob(request, page):
if request.FILES: # save screenshot of specified page
try:
pb = PageBlob.objects.all().filter(page=page))
if not pb.count():
pb = PageBlob()
pb.page = page
pb.blob = request.FILES['image']
pb.save()
return HttpResponse('Blob Submitted')
except:
return HttpResponse('[App::my-app]\tError when requesting page_image({page})'.format(page=page))
else: # return screenshot of requested page
try:
# get objects storing screenshot for requested page
pb = PageBlob.objects.all().filter(page=page)
# if one exists
if pb.count():
pb = pb[0]
## this just returns the string literal "blob"
return HttpResponse(str(pb.blob))
return HttpResponse('[App::my-app]\tNo blob for {page}'.format(page=page))
except:
return HttpResponse('[App::my-app]\tError when trying to retrieve blob for {page}'.format(page=page))
return HttpResponse('Another response')
models.py
class PageBlob(models.Model):
page = models.CharField(max_length=500)
blob = models.TextField(db_column='data', blank=True)
But I can not seem to faithfully capture and retrieve the blob.
Many S.O. questions of storing blobs use the model approach with import base64 to encode and decode the blob. One even recommends using the BinaryField. However, Django's documentation states firmly that BinaryField is not a replacement for the handling of static files.
So how could I achieve this?
S.O. posts I have found helpful to get this far
Upload an image blob from Ajax to Django
How to screenshot website in JavaScript client-side / how Google did it? (no need to access HDD)
How to download data in a blob field from database in django?
passing blob parameter to django
Returning binary data with django HttpResponse
https://djangosnippets.org/snippets/1597/
Django Binary or BLOB model field
Django CSRF check failing with an Ajax POST request
How can javascript upload a blob?

Rails4 strange behavior in test

In my test I want multiple users to be created and the last one logged in.
So here is my code:
test "add_answer to ad" do
user = newuser
text = "mytext mytext mytext mytext mytext mytext mytext"
post :savenew, cityId: City.last.id, text: text , maxPrice: "1", subscribe: false
$stdout.puts "#{assigns(:current_user)} CURUSER"
#ad = assigns(:ad)
user = newuser(0, 'new2#new.com')
post :add_answer, id: #ad.id, text: text
$stdout.puts "#{assigns(:current_user)} CURFIRM"
assert_equal "Ответ успешно добавлен.", flash[:success]
user.destroy
end
How I create new users:
def newuser(rank = 2, email = 'new#new.com')
user = User.new
user.password = '123456'
user.newpassword = true
user.email = email
user.contact = 'new_user'
case rank
when 0
user.urank = URank.firm.id
when 1
user.urank = URank.admin.id
when 2
user.urank = URank.user.id
end
user.save
$stdout.puts "#{user} NEW"
this_controller = #controller
#controller = MainController.new
get :logout
post :login, email: email, pass: '123456'
#controller = this_controller
return user
end
And my logout:
if cookies[:id]
Rails.cache.delete(cookies.signed[:id][0])
cookies.delete(:id)
end
#current_user = nil
(There is no cache it Test)
So as a result when I create new users I can see this:
As you can see the first user is just another user and second == first. What is happening there and how can I fix this?
Thank you!
I don't know why, but the problem was
#current_user ||= User.auth_with_salt(*cookiesId)
In test just do
#current_user = User.auth_with_salt(*cookiesId)

Return multiple items from a Django View (ajax)

I have a function on my page that uses ajax. The ajax goes to a Django view, which can then return some sort of data for me. Unfortunately, I can only get it to return one piece of data at the moment. The second gets returned as the string "success". Here's what I have going:
Ajax:
success: function(data, message) {
if(data === 'False'){
$('#mailsquare').css('background-color', '#A80000');
$('.dropdown-menu').prepend('<li class="message" id="{{item.id}}">'+message.substring(0,30)+'</li>');
} else {
$('#mailsquare').css('background-color', '#1b1b1b');
}
},
View:
#login_required
def checkMail(request):
user = request.user.get_profile()
i = inbox.objects.get(user = user)
read = i.read
new = i.message.order_by('created')[:1]
return HttpResponse(read, new)
The prepend statement doesn't use the "message" value that it is receiving, but rather just inserts the string "success". The "data" parameter is handled correctly.
The success method is defined like this:
success: function(data, textStatus, jqXHR) {
So, message is actually the textStatus Hence the result.
You need to send the data from the view correctly for this to work correctly.
One way to do it is:
#login_required
def checkMail(request):
user = request.user.get_profile()
read = inbox.objects.get(user = user).read
newest = i.message.order_by('created')[:1]
return HttpResponse(simplejson.dumps({'read': read, 'newest': newest}))
and in the js
success: function(data, textStatus, jqXHR) {
var read = data.read;
var newest = data.newest;
//rest of the stuff
}
You wrong return the data for ajax
def answer(request):
# same you code
payload = {'success':True,'param1': 'you_value', 'param2':'you_value2'}
return HttpResponse(json.dumps(payload), content_type='application/json')
In Javascript
success: function(data) {
if(data.success === false){
alert(data.param1);
alert(data.param2);
} else {
$('#mailsquare').css('background-color', data.param1);
$('#mailsquare').html(data.param2);
}},