Reputation: 33
I'm writing a prototype front end for a home heating control system and when the user selects the room and day of the week from the drop down box, I want to be able to query the Redis database and retrieve the currently set temperatures for each hour and use this to set the selected value in the temperature drop down boxes. The user can then change a value or values and submit them. I have the submit and write working, just trying to get my head around setting the selected.
Here's the HTML:
{% extends "bootstrap/base.html" %}
{% block title %}Setting Room Temperatures{% endblock %}
{% block navbar %}
<div class="navbar navbar-fixed-top">
<!-- ... -->
</div>
{% endblock %}
{% block content %}
</head>
<body>
<h1>Room temperature configuration</h1>
<form action="{{ url_for('heating_config_form')}}" method="post" class="form-inline">
<div class="form-group">
<label for="roomselect">Room:</label>
<select class="form-control" name="roomselect">
<option value="dining">Dining</option>
<option value="kitchen">Kitchen</option>
<option value="lounge">Lounge</option>
<option value="study">Study</option>
</select>
</div>
<br>
<br>
<div class="form-group">
<label for="dayselect">Day :</label>
<select class="form-control" name="dayselect">
<option value="monday">Monday</option>
<option value="tuesday">Tuesday</option>
<option value="wednesday">Wednesday</option>
<option value="thursday">Thursday</option>
<option value="friday">Friday</option>
<option value="saturday">Saturday</option>
<option value="sunday">Sunday</option>
</select>
</div>
<br>
<br>
<div class="form-group">
<label for="1AM">1AM :</label>
<select class="form-control" name="1AM">
<option value="5">5</option>
<option value="6">6</option>
</select>
<label for="2AM">2AM :</label>
<select class="form-control" name="2AM">
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
<br>
<br>
<button type="submit" value="submitted" name="update">Update temperature </button><br>
<br>
</form>
<br>
</body></html>
{% endblock %}<html><head>
And here's the Python Flask code
from flask import Flask
from flask import request
from flask import render_template
from flask_bootstrap import Bootstrap
import redis
app = Flask(__name__)
app.debug = True
bootstrap = Bootstrap(app)
db = redis.Redis('localhost')
@app.route('/', methods=['GET','POST'])
def heating_config_form():
error=""
if request.method == 'POST' and request.form['update'] == 'submitted':
Room = (request.form.get('roomselect'))
Day = (request.form.get('dayselect'))
AM1 = (request.form.get('1AM'))
AM2 = (request.form.get('2AM'))
key = (Room + "_" + Day)
db.hset(key, "1:00", AM1)
db.hset(key, "2:00", AM2)
return render_template('set-room-tempv5.html')
else :
return render_template('set-room-tempv5.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
I'm aware there are probably better ways of approaching this (WTForms?) but wanted to get a simple setting interface that I understand so I can prototype the system while developing a better front end, as I'm new to Flask and don't want the whole heating system to be dependent on my ability to pick this up :)
Adding the temperature to the database works fine, this is a typical monitor output from the redis-cli monitor
127.0.0.1:6379> monitor
OK
1482336847.287342 [0 127.0.0.1:34180] "HSET" "kitchen_tuesday" "1:00" "5"
1482336847.288042 [0 127.0.0.1:34180] "HSET" "kitchen_tuesday" "2:00" "5"
I was thinking that maybe something like the following context processor could help?
@app.context_processor
def utility_processor():
def retrieve_temp(sroom, sday, stime):
skey= (sroom + "_" + sday)
stemp = db.hget(skey, stime)
return stemp
return dict(retrieve_temp=retrieve_temp)
which would make the function retrieve_temp available to all templates - I think!
Then somehow, once the template is rendered, the default room and day is used to set the "selected" option on the time drop downs, and then every time the room and day drop downs are moved, the same happens again.
So if the default is dining and Monday given they are the first in the select options, the temperatures for 1AM and 2AM are retrieved and set as selected for these drop downs (there are more times and more temps, these have been deleted for brevity). If the room and/or the day is changed, it happens again.
Upvotes: 0
Views: 9921
Reputation: 5362
OK - So just wanted to be sure on your question before I gave you information to solve the problem. The approach you've outline as an alternative in the comments is the easiest way to get this to work (e.g. intermediary page to make the first selection).
It's important to remember that any python or jinja2 code is only going to executed 1 time. Jinja2 uses some logic to render the HTML output and then Flask responds with a static HTML page to the browser, so you can use a context_processor
to pass specific logic to jinja2, but once Flask renders the page any interactivity would need to be managed by javascript.
It looks like you are using Bootstrap, which includes jQuery
javascript library. jQuery
is great (in my opinion) because it handles some browser quirks for you in terms of how it interacts with and manipulates the DOM (e.g. the Document Object Model, javascript's representation of your HTML elements).
So, if you want to dynamically populate the second select you can design some code to asynchronously (e.g. without making another HTTP request or "reloading" the page) send data from the browser to a Flask endpoint to fetch the required options then update the front end. I'll give you an example:
HTML looks like this (I'm just adding ID's. The for
tag is suppose to reference the ID field, not the name, and it also makes the javascript selection easier):
<div class="form-group">
<label for="roomselect">Room:</label>
<select class="form-control" name="roomselect" id="roomselect">
<option value="dining">Dining</option>
<option value="kitchen">Kitchen</option>
<option value="lounge">Lounge</option>
<option value="study">Study</option>
</select>
</div>
<div class="form-group">
<label for="dayselect">Day:</label>
<select class="form-control" name="dayselect" id="dayselect">
<option value="monday">Monday</option>
<option value="tuesday">Tuesday</option>
<option value="wednesday">Wednesday</option>
<option value="thursday">Thursday</option>
<option value="friday">Friday</option>
<option value="saturday">Saturday</option>
<option value="sunday">Sunday</option>
</select>
</div>
<div class="form-group">
<label for="1AM">1AM:</label>
<select class="form-control" name="1AM" id="1AM">
<option value="5">5</option>
<option value="6">6</option>
</select>
<label for="2AM">2AM:</label>
<select class="form-control" name="2AM" id="2AM" disabled>
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
Then, you need to add a javascript event listener and AJAX method, usually you do this in the bottom of the body of your HTML or in a linked file:
<script charset="utf-8" type="text/javascript">
// this is called a javascript closure. it also wont run the code until the page is finished loading
// and it protects the scope of your variables from other code you may later add to the page
$(function() {
var select_room = $('#roomselect'),
select_day = $('#dayselect'),
select_1am = $('#1am'),
select_2am = $('#2am');
select_room.on('change', function() {
// fires when room selection changes
getUpdatedSettings();
});
select_day.on('change', function() {
// fires when day selection changes
getUpdatedSettings();
});
function getUpdatedSettings() {
// data to send back to the server
var send = {
room: select_room.val(),
day: select_day.val()
};
// make the selections disabled while fetching new data
select_1am.attr('disabled', true);
select_2am.attr('disabled', true);
$.getJSON("/_get_updated_settings", send, function(response) {
// this send the room and the day select vals to the URL specified
// we will need to add a handler for this in Flask
// for the purpose of the example I am assuming the response will be
// a JSON object that has a dictionary of elements ("am_1" and "am_2")
// each of which is a list of values for the selects....
console.log(response); // good for QA!
// populate 1am
select_1am.empty();
$.each(response.am_1, function (index, value) {
select_1am.append(
$('<option>', {
value: value,
text: value
}, '</option>'))
});
// populate 2am
select_2am.empty();
$.each(response.am_2, function (index, value) {
select_2am.append(
$('<option>', {
value: value,
text: value
}, '</option>'))
});
// remove disabled now
select_1am.removeAttr('disabled');
select_2am.removeAttr('disabled');
});
}
});
</script>
Now, you need to add some Flask logic to handle this AJAX request:
from flask import jsonify
@app.route('/_get_updated_settings')
def get_updated_settings():
# good for debug, make sure args were sent
print request.args
day = request.args.get('day', 'default_if_none')
room = request.args.get('room', 'default_if_none')
key = (room + "_" + day)
output = {}
# I have no idea what this returns...just doing a list generator here assuming we get a list of values
output['am_1'] = [x for x in db.hget(skey, '1am')]
output['am_2'] = [x for x in db.hget(skey, '1am')]
return jsonify(output)
Ha...turned out a bit longer than I expected, but this should at the very least provide you with a straw man to get functionality like this working. Javascript definitely provides for a better user experience, but you can generally accomplish the same thing by having a series of intermediary forms that populate static pages.
Upvotes: 4