How do I inject a template into another template in Flask [duplicate] - flask

I Want to develop a flask navigation bar like Google Contacts.
I Want to Render a particular HTML page inside the red box (as in the picture) when I click each of the navigation buttons (the green box as in picture) without refreshing the page.
I have already tried using
{% extends "layout.html" %}

As #Klaus D. mentioned in the comments section, what you want to achieve can be done using Javascript only. Maybe your question were
How can I send a request to my server-side (to get or fetch some information) and receive back a response on the client-side without having to refresh the page unlike the POST method usually does?
I will try to address the aforementioned question because that's probably your case.
A potential solution
Use Ajax for this. Build a function that sends a payload with certain information to the server and once you receive back the response you use that data to dynamically modify the part of the web-page you desire to modify.
Let's first build the right context for the problem. Let's assume you want to filter some projects by their category and you let the user decide. That's the idea of AJAX, the user can send and retrieve data from a server asynchronously.
HTML (div to be modified)
<div class="row" id="construction-projects"></div>
Javascript (Client-side)
$.post('/search_pill', {
category: category, // <---- This is the info payload you send to the server.
}).done(function(data){ // <!--- This is a callback that is being called after the server finished with the request.
// Here you dynamically change parts of your content, in this case we modify the construction-projects container.
$('#construction-projects').html(data.result.map(item => `
<div class="col-md-4">
<div class="card card-plain card-blog">
<div class="card-body">
<h6 class="card-category text-info">${category}</h6>
<h4 class="card-title">
${item.title_intro.substring(0, 40)}...
</h4>
<p class="card-description">
${item.description_intro.substring(0, 80)}... <br>
Read More
</p>
</div>
</div>
</div>
`))
}).fail(function(){
console.log('error') // <!---- This is the callback being called if there are Internal Server problems.
});
}
Build a function that will fetch the current page via ajax, but not the whole page, just the div in question from the server. The data will then (again via jQuery) be put inside the same div in question and replace old content with new one.
Flask (Server-side)
''' Ajax path for filtering between project Categories. '''
#bp.route('/search_pill', methods=['POST'])
def search_pill():
category = request.form['category']
current_page = int(request.form['current_page'])
## Search in your database and send back the serialized object.
return jsonify(result = [p.serialize() for p in project_list])

Thank you #CaffeinatedCod3r,#Klaus D and #newbie99 for your answers.
I Figured it out. instead of using Flask we can use Angular JS Routing for navigation.
Here is the example that i referred:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<head>
<base href="/">
</head>
<body ng-app="myApp">
<p>Main</p>
Banana
Tomato
<p>Click on the links to change the content.</p>
<p>Use the "otherwise" method to define what to display when none of the links are clicked.</p>
<div ng-view></div>
<script>
var app = angular.module("myApp", ["ngRoute"]);
app.config(function($routeProvider, $locationProvider) {
$routeProvider
.when("/banana", {
template : "<h1>Banana</h1><p>Bananas contain around 75% water.</p>"
})
.when("/tomato", {
template : "<h1>Tomato</h1><p>Tomatoes contain around 95% water.</p>"
})
.otherwise({
template : "<h1>Nothing</h1><p>Nothing has been selected</p>"
});
$locationProvider.html5Mode(true);
});
</script>
</body>
</html>
By Using $locationProvider.html5Mode(true) i was able to remove the # from the URL.

Related

How to use a javascript variable inside a Django {% url %} tag?

Im trying to dynamically change the link of a button based on which div a user has currently selected.
I tried to run a quick test with a JS variable in the HTML script tag, but Django isn't reading the variable like a num.
<script type="text/javascript">
const testing = 10
</script>
<a href="{% url 'battlefield:onevsone_trainer_selection' num_trainers=testing %}" class='description__button btn btn__large'>Next ></a>
URL looks like:
path('one-vs-one/trainers/<int:num_trainers>', views.oneVsOne, name='onevsone_trainer_selection')
Not sure exactly why it's not working. When I passed it a string of '10' it worked
Django templates are handled server side by django, which means by the time the client browser has received the HTML, the url function, i.e. {% url 'battlefield:onevsone_trainer_selection' num_trainers=testing %}, will have been processed. Javascript, of course, runs in the browser, and you can't pass a javascript variable to a django function after it has arrived at the client.
If you want to change this clientside (without communicating with the server via ajax or something) you might need to change the href attribute directly. You could do something like:
<a id='changeableUrl' href="{% url 'battlefield:onevsone_trainer_selection' num_trainers=1 %}" class='description__button btn btn__large'>Next ></a>
<script>
//figure out where the URL should be going
const testing = 10
//get the current URL
url = document.getElementById('changeableUrl').getAttribute("href")
///replace the end of the url href with our value
url.replace('/1/', '/'+testing+'/')
///write back to the link attribute
document.getElementById('changeableUrl').setAttribute("href", url)
</script>
What you should understand is that django template engine works server-side, whereas Javascript only does its job on client side.
What happens is that you are asking django to render the following template :
<script type="text/javascript">
const testing = 10
</script>
<a href="one-vs-one/trainers/testing" class='description__button btn btn__large'>Next ></a>
Here, testing is not a javascript variable, it's just plain html content. Moreover, it's not an integer at rendering time, so the regex used internally by django shall not be matched and a rendering error shall be raised.
If you really want to set the url paramater client-side, then I see two solutions :
The first is to build the url manually without using the django url function. :
<a href="" id="to-change" class='description__button btn btn__large'>Next ></a>
<script type="text/javascript">
document.getElementById("to-change").href = "one-vs-one/trainers/" + num_trainer;
</script>
The second solution is the one proposed by SamSparx.
Whatever the solution you choose, you should carefully keep in mind that neither of the two if fully safe. In the first case, you make the code a little bit less maintainable by giving more work if one day you change the url in your urls.py file. In the second one, you expose you to a url that is predefined. If JS is not enabled or if something wrong happens on the browser of your client, it may mean that your link shall be linked to what it should not.

Using customGPlusSignIn with data-onsuccess method instead of onclick

I am trying to change the file path of my website after having the user sign in.
Here is my body html code
<body>
<div id="map"></div>
<div id="loginmain">
<div id="center">
<div id="login-center">
<img src="Assets/google.svg">
<a id="customBtn" class="customGPlusSignIn" onclick="document.location='index.html'">Sign In</a>
<script>startApp();</script>
</div>
<p id="help-text">Sign in with your school Google account</p>
</div>
</body>
The intention of the onclick method is to call a separate html page, after the user has successfully signed in to Google. However, using onclick would change the file path regardless of the Google Sign in.
I have tried using data-onsuccess method, however it seems to be a method only for class = "g-signin2".
Instead, I am using class="customGPlusSignIn", and I'm not particularly sure how to change the file path after ensuring that the user has successfully signed in.
For context, the function startApp() is copied over from Google's sign in docs, from the bottommost code cell.
Could anyone help me with this? Thanks

add buttons that populate other fields in built-in templates of Flask-Admin

I would like to add a button to my Flask-Admin create view, and following this question I managed to do so.
Now, assuming that the model passed to that view, say User has:
A one to many relationship with ClassA and ClassB
ClassA has also a one to many relationship with ClassB
And assuming that in my create view I've added some instance of ClassA when creating an instance of User, say my_class_a_instance, I would like that this button:
performs a query on my_class_a_instance, returning any instances of ClassB (or my_class_b_instances) related at that moment with it
populates the form.class_b field in the create template with those results
if possible, pop up some modal window promting the user for confirmation. Possible with #action
My approach so far goes like this:
# templates/admin/cascade_button_create.html
{% extends 'admin/model/create.html' %}
{% block body %}
{% call lib.form_tag(form) %}
<div class="row">
<div class="col-xg-10">
{{ lib.render_form_fields([form.name])}}
</div>
</div>
<div class="row">
<div class="col-xg-10">
<!-- form.cities follows the attributes of sqla model -->
{{ lib.render_form_fields([form.instances_of_a])}}
</div>
<div class="col-xg-2">
<!-- so this button should query any model related to form.cities
and populate the create form with whatever comes out.
Say Street has a one to many relationship, I want this
button to run some method of the form get_instances_of_b_from_instance_of_a(form.instances_of_a) (query_method() for short) that fills
the field form.instances_of_b
If possible I would like to pop up a modal window prompting the user
to confirm this before filling this field.-->
Add with cascade
</div>
</div>
<div class="form-buttons">
{{ lib.render_form_buttons(return_url) }}
</div>
{% endcall %}
{% endblock %}-
I would be registering this view like the documentation says
# admin/views.py
class CascadesView(ModelView):
create_template = 'admin/cascade_button_create.html'
I haven't found information on this, and the templates don't have many comments that help.
Thanks!
EDIT:
I've copied the examples from the flask-admin repo and set mine in https://github.com/diegoquintanav/flask-admin-autopopulate to play with
I looked a bit further into it, and this seems very specific behavior, which is not implemented in flask-admin. I went ahead and had a look at it anyways, and the only way I could think of to easily do this is by using Ajax and a special api route.
So I've added this to your js:
Add with cascade
<script>
function retrieve_location_b_ids(){
// make sure that the user wants to preload the b locations
if (confirm('load location b connected to location a?')){
// look for the selected options
var selected_a_locations = $('#s2id_sub_locations_a').select2("val");
// request the b_ids using the a_ids provided by the user using ajax
var oData = new FormData();
oData.append('selected_a_locations', JSON.stringify(selected_a_locations));
var oReq = new XMLHttpRequest();
oReq.open("POST", "{{url_for('look_up_b_locations_connected_to_a_locations')}}", true);
oReq.onload = function(oEvent) {
if (oReq.status == 200) {
// get the correct b ids back from the ajax request, and use them to load the select2 field
var selected_b_ids_list = JSON.parse(oReq.responseText)
$('#s2id_sub_locations_b').select2('val', selected_b_ids_list);
} else {
alert("Error " + oReq.status + " occurred when retrieving the ids")
}
};
oReq.send(oData);
}
}
</script>
and the flask route which deals with this request:
#app.route('/api/look_up_b_locations_connected_to_a_locations', methods=['POST'])
def look_up_b_locations_connected_to_a_locations():
# use a set in case the same b location is in multiple a locations to prevent duplicates
b_location_set = set()
a_location_list = json.loads(request.form['selected_a_locations'])
for a_location_id in a_location_list:
a_location = SubLocationA.query.get_or_404(a_location_id)
for b_location in a_location.sub_sub_locations_b:
b_location_set.add(str(b_location.id))
return jsonify(list(b_location_set))
It seems to work pretty well, and deals with most edge cases (I hope).

Mako inhereting from multiple files

I have a pyramid application with multiple views each depending on a single mako template. The views are quite complicated and bug free, so I don't want to split or merge views, and by extension, the corresponding templates.
However, I would like a single view to represent all the others. Merging all the pyramid views and templates is practically not an option.
For example, I have a login view & template and a signup view & template. Now I want my root page to contain both of them. Both login and signup inherit from base.mak, which contains common scripts and style sheet imports. The following is a pictorial representation of the mako import structure I want.
base.mak
/ \
login.mak signup.mak
\ /
root.mak
Alternatively, I tried chaining them as such:
base -> login -> signup -> root
However, I think that the views no longer talk to their respective templates.
My problem comes in when I do the 3rd chain (login.mak -> signup). I'll post analogous and extract code below, since my full code is a bit long (If more code is needed, feel free to shout).
base.mak:
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>
${next.title()}
</title>
#Imports
${next.head()}
</head>
<body>
<div id = "content">
${next.body()}
</div>
</body>
</html>
login.mak:
<%inherit file="base.mak"/>
<%def name="title()">
${next.title()}
</%def>
<%def name="head()">
${next.head()}
</%def>
<div id="login">
<div id="message">
${sMessage}
</div>
<div id="form">
<form action="${url}" method="post"> <--- url returned in views.py
...
</div>
${next.body()}
signup.mak:
<%inherit file="login.mak"/>
<%def name="title()">
</%def>
<%def name="head()">
</%def>
<div id="box">
...
</div>
Now my problem here is that my url returned from my views is undefined when I try to inherit as in above.
Then of course if I get this working, adding base.mak to to inherit from signup should be trivial.
I assume that there is a simple fix for this, but I can't find an example/explanation on how to do this in pyramid, where the templates actually do stuff.
Alternatively, Is there another way to bring together multiple pyramid views and templates into a single view?
Ok, I figured it out. One has to use mako's <%include/>, and then there is no complicated inheritance structure. So, now my files look like this:
root.mak
<%inherit file="base.mak"/>
<%def name="title()">
Welcome
</%def>
<%def name="head()">
</%def>
<%include file="login.mak"/>
<%include file="signup.mak"/>
login.mak:
<%inherit file="base.mak"/>
<%def name="title()">
</%def>
<%def name="head()">
<link rel="stylesheet" type="text/css" href="${request.static_url(...
</%def>
<div id="login">
<div id=".....
</div>
and the same structure with signup.mak. base.mak still looks the same as in the question above.
Now, if you're using pyramid (I assume another framework will work the same), and you have views that receive and pass information from forms for example, then turn them into normal functions (without #view_config(renderer='path/file.mak') and place their functionality into the parent view function, in my case root. In other words:
#view_config(renderer='pyramidapp:templates/root.mak',
context=Root,
name="")
#forbidden_view_config(renderer='pyramidapp:templates/root.mak')
def root(self):
xLoginRet = login(self)
xSignupRet = signup(self)
#logic and functionality for both, return stuff to go to base.mak

How to Connect JQuery-UI Autocomplete Widget to Django-Dajax?

I have a Django app for which I am trying to build an autocomplete UI for making selections. As the user types into a text box and the the app should make search suggestions based on values retrieved from the database. I want to use Dajax/Dajaxice to handle the ajax and the jquery-ui autocomplete widget for the UI template. Can someone please explain how to get the jquery-ui autocomplete widget to call my dajax function via the autocomplete source attribute (or any other better way)?
My code is a combination of this dajax example and this jquery-ui autocomplete example.
my_app/ajax.py:
from my_app.models import MyModel
from dajax.core import Dajax
from dajaxice.decorators import dajaxice_register
from django.utils import simplejson
#dajaxice_register
def auto_filter_selections(request, symbol):
dajax = Dajax()
result = MyModel.objects.filter(symbol__startswith = symbol)
dajax.assign('#id_symbol', 'innerHTML', str(result))
return dajax.json()
template: my_app_ui.html
<head>
....
<script type="text/javascript" src="{{STATIC_URL}}jquery/jquery-1.7.2.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}dajax/jquery.dajax.core.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}/jquery/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
....
<script>
$(function() {
$("#id_symbol").autocomplete({
source: "Dajaxice.ui.auto_filter_symbols(Dajax.process,'symbol':$('#id_symbol').serialize(true)});",
minLength: 1 //We want to search for even one character
});
});
</script>
....
</head>
<body>
....
<div class="ui-widget">
<label for="id_symbol">Symbol: </label>
<input id="id_symbol">
</div>
....
</body>
If you notice above, I am using jquery-1.7.2 and jquery-ui-1.10.4. This is because the dajax documentation says it is compatible with jquery-1.7.2. Not sure if I can use a newer jquery version.
I am not sure how to get the template's javascript to call my dajax function. the jquery-ui documentation for .autocomplete says to use its source attribute but does not give a very good example. Can anyone tell me if the above is correct?
Once the dajax view function retrieves suggestions from the DB, how do I populate the .autocomplete text box with it?
I've done quite a bit of research over the past few days but there are few examples of Django-Dajax-JQueryUI applications out there.
After considerably more research and piecing various clues together I finally found the key to getting the JQuery-UI autocomplete widget to work with Dajax/Dajaxice and Django.
Synopsis:
Initialize JQuery-UI .autocomplete's source option with a separate javascript (JQuery) setter function
Connect .autocomplete widget's source option to server-side function with a Dajaxice command via a third javascript function.
Explanation:
Firstly, it is important to remember that Dajaxice provides the ajax communication channel by which your client-side javascript and the server side code transact, whereas Dajax provides a convenient way of generating the javascript (JSON) code that will perform the desired function in the client browser without having to write any javascript.
Since JQuery-UI provides us with the javascript widget (in the form of jquery) to perform our desired function (.autocomplete), we do not need Dajax and will not, according to my findings, have any success with it in this application. We ONLY need Dajaxice in order to connect the client side javascript (.autocomplete) to our server side function that does the searching and returns results to suggest to the user.
Below is my revised code that I have gotten working. Note that I have only tested it on a development platform thus far.
my_autocomplete_search_template.html with extraneous code removed:
<head>
....
<script type="text/javascript" src="{{STATIC_URL}}jquery/jquery-1.7.2.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}dajax/jquery.dajax.core.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}/jquery/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
....
<script>
//Initialize the source option of .autocomplete widget during page load
//See: http://api.jqueryui.com/autocomplete/#method-option setter example code
var local_source = ["Abc",] //Dummy string for initialization only; temporary value
$("#search_box").autocomplete("option", "source", local_source)
</script>
<script>
//.autocomplete widget is Dajaxice callback function
// *data* comes from server-side function
function search_result(data) {
$("#search_box").autocomplete({
source: data,
});
}
</script>
<script>
//This function called by user <input> tag and calls the server-side function via Dajaxice command
function dajax_filter_search_term() {
Dajaxice.my_app.auto_filter_search_term(search_result,'search_term':$('#search_box').val()});
// | | | ----------------------
// | | | |
// server-side .autcomplete serverside user-typed
// function callback func. func. argument value to search
}
</script>
....
</head>
<body>
....
<div class="ui-widget">
<label for="search_box">Search Term: </label>
<input type="text" name="search" id="search_box" onkeyup="dajax_filter_search_term()" />
</div>
....
</body>
my_app/ajax.py
from dajaxice.decorators import dajaxice_register
from django.utils import simplejson
from my_app.models import MyModel
#dajaxice_register
def auto_filter_search_term(request, search_term):
# Query DB for suggestions to present to user
q = MyModel.objects.filter(myfield__istartswith=search_term)
data = []
for item in q:
# Format returned queryset data, if needed
data_item = item.field1 + ', ' + item.field2 + ', ' + item.field3
data.append(data_item)
# Return data to callback function *search_result*
return simplejson.dumps(data)
That is it! The key is to initialize the .autocomplete source option first and to only use Dajaxice to communicate with the server-side function.
It is possible to optimize the client-side code further by getting rid of the javascript function dajax_filter_search_term() and including the Dajaxice call in the #search_box input tag like so:
<input type="text" name="search" id="search_box" onkeyup="Dajaxice.ui.auto_filter_search_term(search_result, {'search_term':$('#search_box').val()})" />
References
JQuery-UI API docs: http://api.jqueryui.com/autocomplete/#method-option:
Additional clues here:
Django, JQuery, and autocomplete