Getting query length with Ajax / Django - 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()

Related

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?

Avoid Multiple submits in 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;
}
});
}
})
});

How to make django querysets with dynamic filters?

So basically I have a website in Django that is a storefront and the end user has three filters to use. A product type filter (pants, shoes, shirts, etc), a delivery filter (yes/no), and a location/popularity filter.
Currently in my views.py I have this method.
if request.is_ajax():
if request.GET.get('filter') == 'shirts':
latest_entries = Entry.objects.filter(entrytype="shirts")
context = {'latest_entries': latest_entries}
return render(request, 'storefrontload.html', context)
if request.GET.get('filter') == 'pants':
latest_entries = Entry.objects.filter(entrytype="pants")
context = {'latest_entries': latest_entries}
return render(request, 'storefrontload.html', context)
if request.GET.get('filter') == 'shoes':
latest_entries = Entry.objects.filter(entrytype="shoes")
context = {'latest_entries': latest_entries}
return render(request, 'storefrontload.html', context)
As you can see, this handles the first filter. The problem I'm having is if I select, let's say 'pants', it filters by pants but disregards what was selected in the other two filters. Another example, let's say I select pants, the page populates with the results of that filter. But if I then go to the delivery filter and select "Yes", the page populates with items that are only deliverable, but the "pants" filter is forgotten.
What I'm trying to figure how is how to create a queryset in my views that remembers the other two querysets (if that makes sense).
The only way I can think of to do this is to create True/False flags for each value in each filter, and then add probably 100 lines of if/then statements checking each flag. There has got to be a better way.
UPDATE:
This is how I am passing the filter value from my template.
function filter(type) {
$.get("/storefront/?filter="+type, function(data) {
var $data = data;
$('.grid').children().remove();
$('.grid').append( $data ).masonry( 'appended', $data, true ).masonry( 'layout' );
});
}
//Product Filter
$("#shirts").unbind().click(function () {
filter("shirts");
return false;
});
$("#pants").unbind().click(function () {
filter("pants");
return false;
});
$("#shoes").unbind().click(function () {
filter("shoes");
return false;
});
//Delivery Filter
$("#deliveryyes").unbind().click(function () {
filter("deliveryyes");
return false;
});
$("#deliveryno").unbind().click(function () {
filter("deliveryno");
return false;
});
In my views.py, this will not work:
entry_types = request.GET.getlist('filter')
latest_entries = Entry.objects.filter(entrytype__in=entry_types)
Because I will need to filter by entrytype('pants', 'shirts', shoes') AND deliveryoption ('deliveryyes', 'deliveryno'). Each filter has it's own column in my model.
models.py
class Entry(models.Model):
headline= models.CharField(max_length=200,)
body_text = models.TextField()
author=models.ForeignKey(settings.AUTH_USER_MODEL, related_name='entryauthors')
pub_date=models.DateTimeField(auto_now_add=True)
zipcode =models.IntegerField(null=True, max_length=10)
!!! entrytype = models.CharField(null=True, max_length=10)
!!! deliveryoption=models.CharField(null=True, max_length=5)
You can pass the comma-separated list of the filter values:
/mylist/filter=shirts,pants
And then get entries using the __in lookup:
entry_types = request.GET.get('filter', '').split(',')
latest_entries = Entry.objects.filter(entrytype__in=entry_types)
Or use the the getlist() method of the QueryDict:
/mylist/filter=shirts&filter=pants
With the same ORM call:
entry_types = request.GET.getlist('filter')
latest_entries = Entry.objects.filter(entrytype__in=entry_types)
UPDATE: To pass the multiple types to the view save them in the array and use join() method to get the comma-separated string of them:
var types = []; // currently shown entry types
function filter(type) {
// add or remove the items from the grid
var index = types.indexOf(type);
if (index > -1) {
types.splice(index, 1); // remove the type from the list
} else {
types.push(type); // add the type to the filter
}
$.get("/storefront/?filter="+types.join(","), function(data) {
...
}
}
UPDATE 2: If you use two fields to filter the queryset then you have to create two separate lists in your view:
filters = request.GET.getlist('filter')
entry_types = [f for f in filters if not f.startswith('delivery')]
delivery_types = [f for f in filters if f.startswith('delivery')]
latest_entries = Entry.objects.all()
if entry_types:
latest_entries = latest_entries.filter(entrytype__in=entry_types)
if delivery_types:
latest_entries = latest_entries.filter(deliverytype__in=delivery_types)
JavaScript code will work without any touches.
It is perfectly valid for a parameter to show up multiple times in a request. Use QueryDict.getlist() to get a list containing all values.
http://example.com/?foo=12&foo=34
...
print >>sys.stderr, request.GET.getlist('foo')

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);
}},

Refresh a webpage just once after 5 seconds

I'm looking for a JavaScript solution (or whatever else) that will refresh a webpage ONLY once, after 5 seconds it has been opened. Is this possible without being stuck in a refresh loop?
try this:
setTimeout(function ()
{
if (self.name != '_refreshed_'){
self.name = '_refreshed_';
self.location.reload(true);
} else {
self.name = '';
}
}, 5000);
You could do this in many different ways, but I think the easiest would be to add a query string to the url after the refresh, allowing us to tell if the refresh has already occurred:
//Function to get query string value. Source: http://www.bloggingdeveloper.com/post/JavaScript-QueryString-ParseGet-QueryString-with-Client-Side-JavaScript.aspx
function getQuerystring(key, default_){
if (default_==null) default_="";
key = key.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
var qs = regex.exec(window.location.href);
if(qs == null)
return default_;
else
return qs[1];
}
//check if our query string is already set:
if(getQuerystring(r) !== 1){
setTimeout(function(){window.location.href = window.location.href + '?r=1'},5000)
}
If there is the possibility that a query string is already present, you will have to account for that and change the '?' to an '&'.
Sure, if you don't mind using jquery you can do it via an ajax call after waiting 5 seconds. Just throwing you some sample code:
How to wait 5 seconds with jQuery?
$(document).ready(function() {
// Get data
$.ajax({
url : '/tommyStockExchange/Data',
dataType : 'html',
data : {
'format' : 'H',
'type' : 'E'
},
success : function(data) {
$("#executions").html(data);
},
statusCode : {
404 : function() {
alert('executions url 404 :(');
}
}
});
});
Make it redirect to the same page with a different #hash and in JS only register the redirect if the hash isn't set.
You just need to pass some sort of data between page loads. This can be done in a multitude of ways — use a cookie, a URL query parameter, or something on the server side. Query parameter example:
if (!location.search.match(/(\?|&|^)stopRefreshing(=|&|$)/))
{
setTimeout(function ()
{
var search = location.search;
location.search = search ? search + '&stopRefreshing' : 'stopRefreshing';
}, 5000);
}
Demo: http://jsbin.com/ofawuz/edit