Django authorization when calling other view - python

I have a django system for building formsheets on refrigeration systems.
I have a model called System.
I have plenty of models which represent a form paper and many have :
a Foreign key to System called anlage
a Method called get_pdf_url which returns an url to the pdf generator for the instance
an Attribute CLASSNAME
This is the get method from the zip view :
def get(self, request, *args, **kwargs):
print('# AnlageZip GET NEU ###################################################################################################')
self.pk=self.kwargs.get('pk')
self.template_name = "stammdat/Anlage/info.html"
self.user=self.request.user
#
host=self.request.get_host()
try :
an=Anlage.objects.filter(user=self.request.user).get(pk=self.pk)
except ObjectDoesNotExist :
an=None
#
tfile=BytesIO()
zf = zipfile.ZipFile( tfile, "w" )
# Leere Liste erzeugen
AnlageFiles=[]
#
seq=1
x={}
x['Filename'] = '{:02d}_{}.pdf'.format(seq,an.CLASSNAME)
x['pdf_url'] = 'http://'+host+an.get_pdf_url()
x['data'] = urllib.request.urlopen( x['pdf_url'] )
#x['data1'] =
AnlageFiles.append(x)
print(x['Filename'],x['pdf_url'])
zf.writestr( x['Filename'],x['data'].read())
seq+=1
# Jetzt Dokumentation suchen
# Alle diese modelle benötigen einen foreign Key zu Anlage namens anlage
#
DokuModels = [KurzBetrAnleitA1, KurzBetrAnleitA3, KurzBetrAnleitNH3, UebernahmeUebergabeKurz,UebernahmeUebergabe,
SichDruckBegrenz,DruckFestPruef,AbnahmPruefDruckbeh,WiederPruefDruckbeh,AbnahmPruefRohr,WiederPruefRohr,
PruefListSicht,PruefBeschWK8901,Wartungsvertrag,Einbauerklaerung,
EGKonformErkl,Instandhaltungsvertrag,RisikoBeurteilung]
#
for doku in DokuModels :
docs=doku.objects.filter(anlage=an)
if len(docs) > 0 :
for item in docs :
x={}
x['Filename'] = '{:02d}_{}.pdf'.format(seq,item.CLASSNAME)
x['pdf_url'] = 'http://'+host+item.get_pdf_url()
x['data'] = urllib.request.urlopen( x['pdf_url'] )
AnlageFiles.append(x)
zf.writestr( x['Filename'],x['data'].read())
seq+=1
zf.close()
#
response = HttpResponse(content_type='application/x-zip-compressed')
response['Content-Disposition'] = 'attachment; filename="Anlage_{}.zip'.format(self.pk)
response.write(tfile.getvalue())
tfile.close()
return response
There is a View called SytemOverView
it checks all models for a foreign key to System
if they have one,
it calls the get_pdf_url Method
and creates a context for a list
You can click the urls and get the pdf (up to 54) in the View, works fine
Now I created a ZIP view to download all pdf's in one go
but my pdf files in the zip only contain the login page html code
as the requests query is not authorized.
How can I reuse the django session for the system zip view to launch
the requests for all pdf's?

Like I said in the comments, you are making an external call to your own website, you should call the pdf view directly pass along the request object to retain the session as below, but it seems it bit difficult to modify the request object to construct your different PDFs
def pdf_view(request):
...
# construct pdf
...
return pdf_response
def zipview(request):
pdf1 = pdf_view(request)
pdf2 = pdf_view(request)
# do the zip
I recommend refactor out the pdf function as below:
def generate_pdf(...):
...
return pdf
def pdf_view(request):
...
return generate_pdf(..)
def zip_view(request):
pdf1 = generate_pdf(params)
pdf2 = generate_pdf(params)
# do the zip

Use the session from the connected client and a simple javascript function to collect the files.
In the template of the django view I create a script that collects all pdf files from the page and builds a zip file from it.
<input id="zipme" type="button" value="Create Zip File (takes some time ...)" onclick="CreateZip();" class="btn btn-primary hidden-print btn-block" />
<script src="/static/js/jszip.min.js"></script>
<script src="/static/js/FileSaver.min.js"></script>
<script type="text/javascript">
function CreateZip(){
var zip = new JSZip();
var zaehler=0
{% for formdata in Doku %}
var {{formdata.blob}} = null;
var {{formdata.request}} = new XMLHttpRequest();
{{formdata.request}}.open("GET", "{{ formdata.pdf_url }}" );
//{{formdata.request}}.onload = function(){
// {{formdata.blob}} = {{formdata.request}}.responseText;
//}
{{formdata.request}}.send();
zaehler=zaehler+1;
//
{{formdata.request}}.onreadystatechange = function () {
if ({{formdata.request}}.readyState == 4) {
zaehler=zaehler-1;
{{formdata.blob}} = {{formdata.request}}.responseText;
zip.file("{{formdata.Filename}}", {{formdata.blob}});
//console.log("{{formdata.Filename}}");
if ( zaehler==0 ) {
zip.generateAsync({type:"blob"})
.then(function(content) {
// see FileSaver.js
saveAs(content, "Anlage.zip");
});
}
}
};
{% endfor %}
};
</script>
In the view I just added a context list variable Doku with dict's of individual and uniq variable names to process. This saves from an asynchronous headache.

Related

Django Form does not load MultipleChoiceField data from ajax POST request

I ve a weird issue with a MultipleChoiceField that does not return the items that are in the POST QueryDict
here is the Form
class TranslationLanguagesForm(forms.Form):
languages = forms.MultipleChoiceField(
widget=forms.SelectMultiple(attrs={"novalidate": "",}),
choices=languages,
required=False,
)
the View is something like (shortened):
class AjaxSpotlerCreateView(View):
def post(self,request):
# ...
# some code before
#
translation_form = TranslationLanguagesForm(
self.request.POST, prefix="translation"
)
if translation_form.is_valid():
translation_languages = translation_form.cleaned_data.get(
"languages"
)
#
# some code after
#
I won't write the whole template but the html created by the form fits what I expected:
<select name="translation-languages" novalidate="" class="form-control " id="id_translation-languages" multiple="">
<option value="fr">french</option>
<option value="en">english</option> <option value="es">spanish</option> </select>
The jquery that sends the data trough ajax request is given below:
function ajaxPOST() {
var dismiss = false;
$.ajax({
method: "POST",
url: ajaxURL,
data: getFormData(),
beforeSend: function () {},
success: function (data) {
$target.find(".modal-content").html(data);
if (data.length == 0) dismiss = true;
},
complete: function () {
if (dismiss) hideUploadModal();
else showUploadModal();
}, //complete
}); //ajax
}
function getFormData() {
const result = {};
const $form = $target.find("form#video-loader-form");
const $inputs = $form.find("input, select, textarea");
$inputs.each((_, element) => {
const $element = $(element);
const type = $element.attr("type");
const name = $element.attr("name");
if (name && type == "checkbox" && $element.prop("checked"))
result[name] = $element.prop("checked");
else if (name && type != "checkbox") result[name] = $element.val();
});
return result;
}
the issue is that the form is never "filled" by the data of request.POST and translation_languages receives always an empty list.
...but self.request.POST.getlist("translation-languages[]") returns the correct values
It only happened on MultipleChoiceField whereas ChoiceField returns the correct value
here are the POST data (you see more data than needed by the form with the issue because there are 4 forms and 1 formset in the view) :
<QueryDict: {'csrfmiddlewaretoken':
['bQQzHTR4JDFZFnmO1ExlPZhqURHswXTmXA9RGC2c05pBM63ns2gnVwUnbnwGzor1'],
'transcription-language': ['en-us'], 'translation-languages[]': ['fr',
'es'], 'spotlers-TOTAL_FORMS': ['1'], 'spotlers-INITIAL_FORMS': ['1'],
'spotlers-MIN_NUM_FORMS': ['0'], 'spotlers-MAX_NUM_FORMS': ['1000'],
'spotlers-0-url':
['https://test-dev-s.storage.googleapis.com/uploads/FR/5e24512/2021/1/9fccac26/9fc37a26-2545-434f-8bd2-0afc3df839aa_full.mp4?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=storage%40funky-tower-264412.iam.gserviceaccount.com%2F20210108%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20210108T125533Z&X-Goog-Expires=3600&X-Goog-SignedHeaders=host&x-goog-signature=8e737cbc384fab5e11002cbc5e6308'], 'spotlers-0-id': ['9fc37a26-1893-434f-8bd2-0afc3df839ef'],
'spotlers-0-name': ['ultraclimb'], 'spotlers-0-duration':
['00:02:43'], 'spotlers-0-is_postedited': ['true'],
'keywords-keywords': [''], 'glossary-glossary': ['']}>
It seems also that the dict returned by ajax POST creates a weird name for multiselect. The name of the field is postfixed by array symbols : []...
So I got 'translation-languages[]': ['fr', 'es'] instead of having 'translation-languages': ['fr', 'es']
well finally I got it !
the problem was in the way I have built the POST data in the jquery.
I created a dict by collecting all inputs fields names and values but it returned a querydict with a wrong field name for array by postfix it with brackets.
I should rather use $form.serialize()
I've made that change and it's now working as expected
A bit late, but you can set the traditional parameter to true to the ajax call. This way, jQuery will use jQuery.param(yourObject, true) instead of simply jQuery.param(yourObject).
You keys referring to list of values will not be altered.
https://api.jquery.com/jQuery.param/
https://api.jquery.com/jquery.ajax/ (see at the data settings explanation)
Cheers,

Using an HTML form with Python Flask search a MongoDB collection

I 'ld like for a user to drop a random search in a search form for a book title and find get results if the book is in the db. Below is part of the code block.
I 'm having some issues searching for a single document in my mongodb using a search field and search strings. Below is the code. I'm trying to get the search result via the find_book route.
The code above with the /find_book/<book_id> returns errors.
Below is a part of my code in the app.py file and the search form.
I get the following errors.
werkzeug.routing.BuildError
werkzeug.routing.BuildError: Could not build url for endpoint 'find_book'. Did you forget to specify values ['book_title']?
Traceback (most recent call last)
# create an instance of py_mongo with app as argument
mongo = PyMongo(app)
#app.route('/')
def home():
return render_template('home.html')
# define the various menu options
#app.route('/get_books')
def get_books():
return render_template('books.html', books=mongo.db.books.find())
# Add a book
#app.route('/add_book')
def add_book():
return render_template('add_book.html',
faculties=mongo.db.faculties.find())
# Add submit button for Books
#app.route('/insert_book', methods=['POST'])
def insert_book():
book = mongo.db.books
book.insert_one(request.form.to_dict())
return redirect(url_for('get_books'))
# wire the edit button
#app.route('/edit_book/<book_id>')
# description task, name, due date, is urgent fields will be
# pre-populated based on the information returned in the task.
def edit_book(book_id):
a_book = mongo.db.books.find_one({"_id": ObjectId(book_id)})
# category names will be prepolulated based on the collection
# # of categories returned in the categories cursor
all_faculties = mongo.db.faculties.find()
return render_template('edit_book.html',
book=a_book, faculties=all_faculties)
#app.route('/update_book/<book_id>', methods=['POST'])
def update_book(book_id):
# access the database collection
book = mongo.db.books
# call the update function, specify an id
book.update({'_id': ObjectId(book_id)},
{
'faculty_name': request.form.get('faculty_name'),
'subject_name': request.form.get('subject_name'),
'book_title': request.form.get('book_title'),
'book_author': request.form.get('book_author'),
'book_description': request.form.get('task_description'),
'lender_name': request.form.get('lender_name'),
'due_date': request.form.get('due_date'),
'is_available': request.form.get('is_urgent')
})
return redirect(url_for('get_books'))
# specify the form fields to match the keys on the task collection
# delete a book
#app.route('/delete_book/<book_id>')
def delete_book(book_id):
mongo.db.books.remove({'_id': ObjectId(book_id)})
return redirect(url_for('get_books'))
# find a book by text search
#app.route('/find_book/<book_title>', methods=['GET'])
def find_book(book_title):
book_title = mongo.db.books
book_title.find_one(
{
'book_title': request.form.get('book_title'),
})
return render_template('find.html', book_title=book_title)
# categories function
#app.route('/get_faculties')
def get_faculties():
return render_template('faculties.html',
faculties=mongo.db.faculties.find())
if __name__ == '__main__':
app.run(host=os.environ.get('IP'),
port=int(os.environ.get('PORT')),
debug=True)
<form action="{{ url_for('find_book') }}" method="GET">
<input type="text" placeholder="Book Title" id="book_title" name="book_title" >
<button type="submit"><i class="fa fa-search">Search</i></button>
</form>
Your find_book route is expecting an argument book_title
But you are not passing that in {{ url_for('find_book') }}
You could just change this route to #app.route('/find_book') and get the value from request.form or if you are using this route in another place of your application you could use the approach from this question and use this way:
#app.route('/find_book/', defaults={'book_title': None})
#app.route('/find_book/<book_title>')
def find_book(book_title):
books = mongo.db.books
if book_title is None:
book_title = request.form.get('book_title')
book = books.find_one({
'book_title': book_title
})
return render_template('find.html', book=book)
I could not run this snippet of code now, so let me know if dont work.

django custom template filter with variable as argument

I'm using a Django custom template filter as described here to access entries in a 3D array in my template. I pass the 3D array swipeData from views.py to index.html.
index.html:
function getNearestHalfHourTimeString() {
var now = new Date();
var hour = now.getHours();
var minutes = now.getMinutes();
var ampm = "AM";
if (minutes < 15) {
minutes = "00";
} else if (minutes < 45){
minutes = "30";
} else {
minutes = "00";
++hour;
}
return("T" + hour + minutes);
}
var currentDay = (new Date()).getDay();
var currentTimeRounded = getNearestHalfHourTimeString();
{% block content %}
{% load hello_extras %}
var atrium = {{swipeData|bldIndex:'ATRIUMCAFE'|dayIndex:currentDay|timeIndex:currentTimeRounded }};
{% endblock %}
hello_extras.py:
from django import template
register = template.Library()
#register.filter
def bldIndex(List, strIndex):
dctBuild = {'ATRIUMCAFE':0, 'BUTLERCOLLEGE':1, 'CAFEVIVIAN':2, 'CENTERFORJEWISHLIFE':3, 'CHANCELLORGREEN':4, 'CHEMISTRYCAFE':5, 'CONCESSIONS_12':6, 'FORBESCOLLEGE':7, 'FRISTCSTORE':8, 'FRISTGALLERY1':9, 'FRISTGALLERY2':10, 'FRISTGALLERY3':11, 'FRISTGALLERY4':12, 'FRISTWITHERSPOONS':13, 'GRADUATECOLLEGE':14, 'LIBRARY_CART':15, 'MATHEYCOLLEGE':16, 'ROCKEFELLERCOLLEGE':17, 'STUDIO34BUTLEREMPORIUM':18, 'WHITMANCOLLEGE':19, 'WILSONCOLLEGE':20, 'WOODROWWILSONCAFE':21, 'FIRESTONELIBRARY':22}
i = int(dctBuild.get(strIndex))
return List[i]
#register.filter
def dayIndex(List, strIndex):
return List[int(strIndex)]
#register.filter
def timeIndex(List, strIndex):
dctTime = { "T0000":0, "T0030":1, "T0100":2, "T0130":3, "T0200":4, "T0230":5, "T0300":6, "T0330":7, "T0400":8, "T0430":9, "T0500":10, "T0530":11, "T0600":12, "T0630":13, "T0700":14, "T0730":15, "T0800":16, "T0830":17, "T0900":18, "T0930":19, "T1000":20, "T1030":21, "T1100":22, "T1130":23, "T1200":24, "T1230":25, "T1300":26, "T1330":27, "T1400":28, "T1430":29, "T1500":30, "T1530":31, "T1600":32, "T1630":33, "T1700":34, "T1730":35, "T1800":36, "T1830":37, "T1900":38, "T1930":39, "T2000":40, "T2030":41, "T2100":42, "T2130":43, "T2200":44, "T2230":45, "T2300":46, "T2330":47}
i = int(dctTime.get(strIndex))
return List[i]
I get the error
VariableDoesNotExist at /
Failed lookup for key [currentDay] in u"[{
How can I use a variable as an argument to my custom template filter? I've tried using a with block already.
Your problem is that you are trying to pass a JS var to a django filter, that it's wrong
currentDay and currentTimeRounded are JS Vars not Python vars, for this reason filter fails
Remember that you can get all necessary info in your view, and then render in your template
I suggest you that format your data in your view and in the template assign it to the respective JS var
some as follow
def some_view(request):
# here you can calculate, convert, create all values to your chart as you need
# if you need time or date can use datetime module ie
values_to_chart = ""
render(request, template_name, locals())
And in your template
var atrium = {{values_to_chart}}

How to create Django click buttons in ajax template for Python callback functions

I'm trying to create a template/ajax that three buttons that send three different parametervalues back to Python function according to the following;
- one variabel current_count starts with the value 0 and updates +-1 for each button click on one of the following clickbuttons;
Button1 - Click button 'Forward' returns the char 'F' from the template when clicked to the view function one_move() wherein it (the char 'F') is passed as a parameter to a Python -function that is executed when it receives a parameter.When clickbutton Forward is returned to the view it also increases the value of current_count with +1 current value(through an if statement)
Button2 - Click button 'Backward' returns the char 'D' from the template when clicked to the view function one_move() wherein it (the char 'D') is passed as a parameter to a Python -function that is executed when it receives a parameter.When clickbutton Backward is returned to the view it also decreases the value of current_count with -1 from it's current value(through an if statement)
Button3 - Click button 'OK' returns chars 'S' from the template when clicked to the view function one_move() wherein it (the char 'S') is passed as a parameter to a Python -function that is executed when it receives a
parameter.When clickbutton OK the value of current_count remains unchanged, that is, with its current value.
The purpose of the above is to implement a pygame wherein the modelfields listview and sentence, which are passed through the Python callback function in one_move() in views.py, are displayed in the html as ["Dave", 8, "to", "work"] wherein 8 is a marker for current position and "went" is placed on the left of the list (which is done by the Python callback function). Pressing the clickbutton "Forward" results in the marker moving one step to the right per click, from "went" to "to", pressing "Backward" results in the marker moving one step to the left per click, e.g from "went" to "Dave", within the range of the list. The third button, OK, selects the word at the marker position, for instance "went". The listview is updated according to the current positionnumber in the variabel current_count.
in models.py
class MoveInList(models.Model):
click_char = models.CharField(max_length=70)
current_count = models.IntegerField()
listview = models.TextField()
sentence = models.TextField()
# function to update increase/decrease in (self)current_count value
def count_changes(self):
count = self.change_set.filter(is_public=True).count()
self.current_count = count
self.save()
def __unicode__(self):
return self.current_count
class OneMove(models.Model):
click_char = models.CharField(max_length=70)
current_count = models.IntegerField()
listview = models.TextField()
sentence = models.TextField()
belongsTo = models.ForeignKey(MoveInList)
def __unicode__(self):
return self.click_count
in forms.py
class MoveInListForm(forms.Form):
click_char = forms.CharField(max_length=70)
current_count = forms.IntegerField()
listview = forms.CharField(max_length=70)
sentence = forms.CharField(max_length=70)
in views.py
def MoveInListIndex(request):
moves = MoveInList.objects.all()
return render(request,"button.html", {"moves":moves})
def one_move(request,postID):
one_moves = MoveInList.objects.get(id=postID)
if request.method == 'POST':
form = MoveInListForm(request.POST)
if form.is_valid:
post_one_move(request, one_move)
else:
MoveInListForm()
# PYTHON CALLBACK function is used with params from template
c = {"one_move":one_move,"form":form}
c.update(csrf(request))
return render(request,"button.html", c)
def post_one_move(request, one_move):
click_count = request.POST['click_count']
current_count = request.POST['current_count']
listview = request.POST['listview ']
sentence = request.POST['sentence']
clickMove = OneMove(belongsTo=one_move,click_count=click_count,current_count=current_count,listview=listview,sentence=sentence)
clickMove.save()
in urls.py
url(r'^button$', MoveInListIndex),
url(r'^button/(?P<postID>\d+)$', one_move),
in template.py with ajax.py
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form method="post" action="">{% csrf_token %}
Forward: <input type="submit" value="button""></input></br>
Backward: <input type="submit" value="button""></input></br>
OK: <input type="submit" value="button""></input></br>
{{ form.as_p}}
</form>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
$('#button').on('click', function (e) {
var current_count = $('#button').val();
var data = { current_count :current_count };
var args = { dataType: "json", type:"POST", url:"/button/",
data:data, complete:ajax_call_complete };
$.ajax(args);
});
});
var ajax_call_complete = function(res, status) {
data = jQuery.parseJSON(res.responseText);
// use data as a json response object here and
// do something with the response data
}
</script>
</body>
</html>
At the moment, the template button.html isn't returning or displaying anything and any advice on how to get the ajax going is highly appreciated.
I think you might need to use HttpResponse() rather than render() to pass back the data to client side:
from django.http.response import HttpResponse
import json
def one_move(request,postID):
...
d = {"responseText": "your data here"}
return HttpResponse(json.dumps(d))
Then you should be able to get the return in your ajax_call_complete() javascript function

Return JSON object to View with Flask and Ajax

I am trying to return a python dictionary to the view with AJAX and reading from a JSON file, but so far I am only returning [object Object],[object Object]...
and if I inspect the network traffic, I can indeed see the correct data.
So here is how my code looks like. I have a class and a method which based on the selected ID (request argument method), will print specific data. Its getting the data from a python discretionary. the problem is not here, have already just tested it. But just in case I will link it.
# method to create the directionary - just in case #
def getCourselist_byClass(self, classid):
"""
Getting the courselist by the class id, joining the two tables.
Will only get data if both of them exist in their main tables.
Returning as a list.
"""
connection = db.session.connection()
querylist = []
raw_sql = text("""
SELECT
course.course_id,
course.course_name
FROM
course
WHERE
EXISTS(
SELECT 1
FROM
class_course_identifier
WHERE
course.course_id = class_course_identifier.course_id
AND EXISTS(
SELECT 1
FROM
class
WHERE
class_course_identifier.class_id = class.class_id
AND class.class_id = :classid
)
)""")
query = connection.engine.execute(raw_sql, {'classid': classid})
for column in query:
dict = {
'course_id' : column['course_id'],
'course_name' : column['course_name']
}
querylist.append(dict)
return querylist
my jsonify route method
#main.route('/task/create_test')
def get_courselist():
#objects
course = CourseClass()
class_id = request.args.get('a', type=int)
#methods
results = course.getCourselist_byClass(class_id)
return jsonify(result=results)
HTML
and here is how the input field and where it should link the data looks like.
<input type="text" size="5" name="a">
<span id="result">?</span>
<p>click me
and then I am calling it like this
<script type=text/javascript>
$(function() {
$('a#link').bind('click', function() {
$.getJSON("{{ url_for('main.get_courselist') }}", {
a: $('input[name="a"]').val()
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
but every time I enter a id number in the field, i am getting the correct data. but it is not formatted correctly. It is instead printing it like [object Object]
source, followed this guide as inspiration: flask ajax example
The data return by your server is like: {result: [{course_id: 'xxx', course_name: 'xxx'}]}, in which data.result is a JS Array.
when you set it to $("#result").text(), JS convert a array to string, so the result is [object Object].
You should iterate over the array to construct a string, then set the string in DOM, like:
courseStr = data.result.map(function(course) {return course.course_id + '-' + course.course_name; }).join(',');
$("#result").text(courseStr);
The API description for flask.json.jsonify indicates it's expecting keyword parameters. What you actually want to do seems to be serialize a list object containing dictionaries, have you tried flask.json.dumps instead? Assuming you've got the dumps symbol imported, instead of your jsonify call you can try:
return dumps(results)

Categories