By Nick Antonaccio
Create production web applications using a browser-based version of Python
called 'Brython', as well as the Python Flask and SQLAlchemy libraries.
This tutorial can be used by absolute beginners who have never written a line
of code.
The 'front end' of your application will run in a web browser. It contains the 'User Interface' (UI) parts of the application, which users see and touch: buttons, dropdown selectors, text entry fields, links, menus, datagrids that display tables of information, etc.
To start out, use any text editor (Notepad, Google Docs, VSCode, etc.), to create a new plain text document. Paste the following code into the document, and save the file as 'brython1.html'. You can save the file on your desktop, or to any folder where you can find it later in your file explorer:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Section 1</h2> </div> <script type="text/python"> from browser import document, html </script> </body> </html>
You can start every Brython front-end project with that generic boilerplate code. All this code does is load the Brython environment and some 'Bootstrap' styling files to make your app look nice, and it sets up a basic screen layout. Save the file to your desktop then click the icon for 'brython1.html', to open it in your web browser. Right now, the application code doesn't do anything except display 'My Application' in the browser tab, and 'Section 1' on the screen.
Next, let's put a few widgets on the screen. The following code adds a text box and a dropdown selector to the boilerplate code above. Save this file as 'brython2.html' and open it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Section 1</h2> </div> <div class="container mt-4"> <form id="exampleForm"> <input type="text" class="form-control" id="textInput" placeholder="Enter text"> <select class="form-control" id="selectInput"> <option>option 1</option> <option>option 2</option> <option>option 3</option> </select> <button type="button" class="btn btn-primary" id="submitButton">Submit</button> </form> </div> <script type="text/python"> from browser import document, html </script> </body> </html>
Take a moment to look through the code above: try to identify the parts which were already in the boilerplate code from the first example. Notice particularly what has been added to the body section. In code above, there's just a newly added, indented form 'tag', containing indented input, select, and button tags (tags are pointy angle brackets which enclose some code). Each of the tags contains some various additional pieces of info such as 'id', 'type', 'class', etc. Look at the overall shape of the code outline, and compare it to what you see on screen when the app opens in your browser - it all consists of just some boilerplate from the first example, with a few widgets placed inside a form. For the moment, that's enough to understand. Details will make much more sense as you see more of the same sorts of things in every additional application example.
Next, save the code below as 'brython3.html'. Open this file in your browser and try entering some text into the text field, select one of the dropdown options, and click the button. You'll see the application print out your selected values every time you click the button.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Section 1</h2> </div> <div class="container mt-4"> <form id="exampleForm"> <input type="text" class="form-control" id="textInput" placeholder="Enter text"> <select class="form-control" id="selectInput"> <option>option 1</option> <option>option 2</option> <option>option 3</option> </select> <button type="button" class="btn btn-primary" id="submitButton">Submit</button> </form> </div> <div id="output" class="mt-4"></div> <script type="text/python"> from browser import document, html def handle_submit(event): text_input = document["textInput"].value select_input = document["selectInput"].value output = document["output"] output.clear() output <= html.P(f"Text Input: {text_input}") output <= html.P(f"Select Input: {select_input}") document["submitButton"].bind("click", handle_submit) </script> </body> </html>
Notice that this code adds a 'script' section to the previous example. Indented in the script section there's a 'def' section, which is what's called a 'function definition'. The function is named 'handle_submit', and it simply groups together a few lines of code. There's also a function 'binding', which binds the 'click event' of the submit button, to run the 'handle_submit' function. When the submit button is clicked by the user, the actions in the handle_submit function definition are executed.
The lines in the handle_submit function definition above perform the following actions:
- First, assign 'variable' names to the 'textInput', 'selectInput', and 'output' objects in the 'document' (the application page). Variables are simply *labels for values which the user has typed into the textInput and selectInput widgets. Notice that the widgets are referred to by their 'id' values. For example, the text input widget tag includes: id="textInput". The dropdown selector widget includes: id="selectInput". Those ids are used to give every widget on the screen a name which can be used in functions to refer to the values which users enter into them.
- Next, clear the 'output' object (erase any content currently displayed in that named area of the screen).
- Finally, add the values in the text_input and select_input widgets to the object named 'output' (i.e., display the user-entered values there).
If you can understand how tags (things inside pointy brackets) display widgets on screen, how ids are used to refer to those widgets, how variable names are used to represent the values which users enter into those widgets, and how functions are bound to events that occur when the user interacts with widgets, to run the actions in function definitions - then you've got the core understanding you need to start building front-end parts of web applications.
Learn to look for the script sections of code where actions are performed in function definitions, triggered by events (users interacting with widgets), using variable labels to refer to values entered by users. Focus on that, and things will continue to make more and more sense.
Next, let's make the layout of the app above look just a slicker. Currently, widgets are all scrunched closely next to each other on screen, without any space in between. In the following example, we'll add a few more widgets, and indent each of them in 'div' tags (divs are just section 'div'iders on the page), along with some labels that appear over the widgets to describe what they do. We'll also add a few more lines into the function definition to retrieve and print out the all the values which the user enters. Save this example as 'brython4.html', open it in your browser, and explore using the widgets:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Section 1</h2> </div> <div class="container mt-4"> <form id="exampleForm"> <div class="form-group"> <label for="textInput">Text Input</label> <input type="text" class="form-control" id="textInput" placeholder="Enter text"> </div> <div class="form-group"> <label for="selectInput">Select Menu</label> <select class="form-control" id="selectInput"> <option>option 1</option> <option>option 2</option> <option>option 3</option> </select> </div> <div class="form-group form-check"> <input type="checkbox" class="form-check-input" id="checkInput"> <label class="form-check-label" for="checkInput">Check me out</label> </div> <div class="form-group"> <label>Radio Buttons</label> <div class="form-check"> <input class="form-check-input" type="radio" name="radioInput" id="radio1" value="option1"> <label class="form-check-label" for="radio1">Option 1</label> </div> <div class="form-check"> <input class="form-check-input" type="radio" name="radioInput" id="radio2" value="option2"> <label class="form-check-label" for="radio2">Option 2</label> </div> </div> <button type="button" class="btn btn-primary" id="submitButton">Submit</button> </form> </div> <div id="output" class="mt-4"></div> <script type="text/python"> from browser import document, html def handle_submit(event): text_input = document["textInput"].value select_input = document["selectInput"].value check_input = document["checkInput"].checked radio_input = None for radio in document.select("input[name='radioInput']"): if radio.checked: radio_input = radio.value break output = document["output"] output.clear() output <= html.P(f"Text Input: {text_input}") output <= html.P(f"Select Input: {select_input}") output <= html.P(f"Checkbox: {'Checked' if check_input else 'Unchecked'}") output <= html.P(f"Radio Button: {radio_input}") document["submitButton"].bind("click", handle_submit) </script> </body> </html>
Now that is starting to look a little more like a screen you'd see in a typical, useful data management app. Get used to the code in this example - it's a good starting point for a generalized outline containing the pieces of code you'd expect to see in almost any app.
The values entered into widgets in a layout like the simple one above could potentially represent almost any useful data, such as items in an inventory tracking program, scheduled events in a calendar app, prices of products that are tallied up in a point of sale application, text messages entered into a forum or social media app, etc. Even the most complex user interfaces, which enable deeply useful computing objectives, are typically made up of many simple little widgets.
Even the graphics in games and the control inputs entered by players, can be thought of and dealt with in the same way - everything on screen in an application is just some object represented by a variable label, and lists of actions can be performed upon those values, using functions.
We'll take values, which are entered by users into the front-end UI widgets, and send them to a 'back-end' server machine, where other people can interact with that data in useful ways. That's how simple forms like you've seen so far, become part of very powerful software computing solutions.
Let's explore a few more front-end techniques you'll use regularly. 'Loops' are code structures that you'll find in almost every computer program, no matter the programming language. A loop 'iterates' (repeats) an action a number of times, with some values changed each time. Save this example as brython_loops.html and run it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Loops</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div id="output" class="mt-4"></div> <script type="text/python"> from browser import document, html output = document["output"] for counter in range(99, 0, -1): output <= html.P( f"""{counter} bottles of beer on the wall. {counter} bottles of beer. Take one down, pass it around, {counter - 1} bottles of beer on the wall.""" ) </script> </body> </html>
You should recognize the default boilerplate code, and notice some lines of Python code in the script section. The loop starts on the line with the word 'for'. In Python, 'for' loops are used to perform some specified action to/with every item in a list. In the example above, a list of numbers is created in the range of 99-0, counting backwards by -1 (0 is not included - the count stops at that last number). 'For' each of those numbers, a string of text (in between the """ characters) is added to the output block on screen (the div with the id 'output'). Notice that in the output text, the current number is represented by the variable 'counter', and that counter number is included in the body of a text string, inside curly brackets: {}. The whole block of code which runs for each number in the list of 99-1, is indented inside the 'for' loop.
'Conditional evaluations' are another code structure you'll put to use in virtually every application. A conditional evaluation works as a logic gate: IF something is true or false, then do something, ELSE do something different. Sometimes this is referred to as 'branching' logic in an app. If you think about about it, making an app do something based on what a user enters - well, that's clearly one of the most important parts of building any app. Save this code as 'brython_conditionals.html', and open it your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Conditional Evaluations</title> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div id="output"></div> <script type="text/python"> from browser import document, html from datetime import datetime, time output = document["output"] current_time = datetime.now().time() if current_time < time(12, 0): output <= html.P('Good morning.') else: output <= html.P('Good afternoon.') </script> </body> </html>
When this app opens, it displays 'Good morning.' if the time on your computer is before noon. Otherwise, it displays 'Good afternoon.'. Ignore the boilerplate as much as possible, and look directly at the script section. There's a line to import the Python 'datetime' library, which enables Python to handle dates and times, there's a line which sets the variable 'current_time' to the current time, and then there's a conditional evaluation: IF the current_time is less than 12 noon, show the good morning message, otherwise ('else'), show the good afternoon message.
Instead of running a conditional evaluation directly when the page loads, this example includes some conditional script lines inside a function definition, adds a form to the UI layout, includes a button widget on the form, and binds the function to run whenever the button is clicked by the user (all pieces of code you've seen previously). The conditional 'if' evaluation prints one message when the user selects option 1, and a different message for option 2. Save this code as 'brython_conditionals2.html':
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Using loops and conditional evaluations</h2> <form id="choicesForm"> <div class="form-group"> <label for="selectInput">Select an option:</label> <select class="form-control" id="selectInput"> <option>bad option</option> <option>good option</option> </select> </div> <button type="button" class="btn btn-primary" id="choiceButton">Respond to Choice</button> </form> </div> <div id="output" class="mt-4"></div> <script type="text/python"> from browser import document, html def handle_choice(event): select_input = document["selectInput"].value output = document["output"] output.clear() if select_input == 'good option': output <= html.P('You\'ve made a great choice.') else: output <= html.P('You\'ve made a horrible choice.') document["choiceButton"].bind("click", handle_choice) </script> </body> </html>
Let's combine both the previous loop and conditional examples into a single program. There are now 2 function definitions, 2 forms in the UI layout containing 2 button widgets, and 2 event bindings to make each button run the appropriate function, whenever a specified button is clicked. Save this code as 'brython6.html', and run it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Using loops and conditional evaluations</h2> <form id="singForm"> <div class="form-group"> <p>Sing a song by clicking the button:</p> </div> <button type="button" class="btn btn-primary" id="singButton">Sing</button> </form> <p></p> <form id="choicesForm"> <div class="form-group"> <label for="selectInput">Select an option:</label> <select class="form-control" id="selectInput"> <option>bad option</option> <option>good option</option> </select> </div> <button type="button" class="btn btn-primary" id="choiceButton">Respond to Choice</button> </form> </div> <div id="output" class="mt-4"></div> <script type="text/python"> from browser import document, html def handle_sing(event): output = document["output"] output.clear() output <= html.P("SING ALONG!") for counter in range(99, 0, -1): output <= html.P( f"""{counter} bottles of beer on the wall. {counter} bottles of beer. Take one down, pass it around, {counter - 1} bottles of beer on the wall.""" ) def handle_choice(event): select_input = document["selectInput"].value output = document["output"] output.clear() if select_input == 'good option': output <= html.P('You\'ve made a great choice.') else: output <= html.P('You\'ve made a horrible choice.') document["singButton"].bind("click", handle_sing) document["choiceButton"].bind("click", handle_choice) </script> </body> </html>
That's great, but have you noticed that the output text is always printed tightly along the left edge of the browser screen, without any padding or layout styling? The updated version below adds a 'style' section which defines some display properties for the output div, including aligning text to the center of the screen, adding margin/padding/border sizes, and adding some colors. On web pages, these styling options are defined by 'CSS' rules, which you can learn about separately from this tutorial (there are endless tutorials online about CSS). For now, just know that when you see a style tag, it's just defining some rules for how elements on the page should look (CSS style is *separate from program functionality). Save this code as 'brython6_styled.html', and run it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>My Application</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> <style> #output { text-align: center; margin-top: 20px; padding: 10px; border: 2px solid #007bff; border-radius: 10px; background-color: #f8f9fa; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } </style> </head> <body onload="brython()"> <div class="container mt-4"> <h2>Using loops and conditional evaluations</h2> <form id="singForm"> <div class="form-group"> <p>Sing a song by clicking the button:</p> </div> <button type="button" class="btn btn-primary" id="singButton">Sing</button> </form> <p></p> <form id="choicesForm"> <div class="form-group"> <label for="selectInput">Select an option:</label> <select class="form-control" id="selectInput"> <option>bad option</option> <option>good option</option> </select> </div> <button type="button" class="btn btn-primary" id="choiceButton">Respond to Choice</button> </form> </div> <div id="output" class="mt-4"></div> <script type="text/python"> from browser import document, html def handle_sing(event): output = document["output"] output.clear() output <= html.P("SING ALONG!", Class='font-weight-bold') for counter in range(99, 0, -1): output <= html.P( f"""{counter} bottles of beer on the wall. {counter} bottles of beer. Take one down, pass it around, {counter - 1} bottles of beer on the wall.""" ) def handle_choice(event): select_input = document["selectInput"].value output = document["output"] output.clear() if select_input == 'good option': output <= html.P('You\'ve made a great choice.', Class='text-success') else: output <= html.P('You\'ve made a horrible choice.', Class='text-danger') document["singButton"].bind("click", handle_sing) document["choiceButton"].bind("click", handle_choice) </script> </body> </html>
You may have noticed the Class='text-success' and Class='text-danger' properties added to the output in the previous example. Those little nuggets are part of the 'Bootstrap' framework we've been including in the boilerplate code since the very first example. Bootstrap is perhaps the most common front-end framework used on the Internet, to help layout and style web pages. It makes web applications look good on all size screens, and it 'responsively' adjusts the positioning and sizing of elements in an invisible onscreen 'flex' grid, so they all automatically fit nicely on whatever screen real estate your mobile or desktop device has available.
Bootstrap is basically a huge collection of CSS styling that includes a bunch of the most common bits and pieces used to layout user interfaces, such as form elements, menus, and other components. Almost everything you see in a tag can be styled by Bootstrap 'classes' and properties. For the time being, you don't need to know anything more than that, but learning more about how to layout your web apps with Bootstrap is a great direction to go, once you've finished this tutorial.
Most web applications connect to back-end servers using 'REST APIs'. A REST API is just a URL, typically in the form http://somesite/somepath/?somevalues . The server at the specified URL can accept input values, called 'request' variables, and send back information called 'response' values.
One free public REST API server you can use is found at https://picsum.photos . Try going to https://picsum.photos/200 in your browser. Every time you go to that URL in your browser, you'll see a different image displayed.
Now try changing the URL to https://picsum.photos/400 , and then https://picsum.photos/800 . You'll see that by changing the number at the end of the URL path, the size of the returned image changes. We can incorporate images from that API into a Brython app. Save this code as 'brython_image_api.html', and run in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lorem Picsum API with Brython</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> </head> <body onload="brython()"> <div class="container mt-4 text-center"> <h2>Random Image from Lorem Picsum</h2> <div id="output" class="mt-4"></div> </div> <script type="text/python"> from browser import document, html def display_image(): url = "https://picsum.photos/200" output = document["output"] output.clear() output <= html.IMG(src=url, Class='img-fluid') display_image() </script> </body> </html>
Refresh this app a number of times in your browser, and you'll see a different image appear each time. Note that the display_image function fills the output area with 'html.IMG()' (image) content. All the examples you've seen so far have previously used html.P() (paragraph text) content.
This next example app builds on the code above, to include a dropdown widget which enables the user to select the size of the image to be downloaded. Save this code as 'brython_image_api2.html' and run it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lorem Picsum API with Brython</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> </head> <body onload="brython()"> <div class="container mt-4 text-center"> <h2>Random Image from Lorem Picsum</h2> <div class="form-group"> <label for="imageSize">Select Image Size:</label> <select class="form-control" id="imageSize"> <option value="200">200</option> <option value="400">400</option> <option value="800">800</option> </select> </div> <button class="btn btn-primary" id="fetchImage">Fetch Image</button> <div id="output" class="mt-4"></div> </div> <script type="text/python"> from browser import document, html def fetch_image(event): size = document["imageSize"].value url = f"https://picsum.photos/{size}" output = document["output"] output.clear() output <= html.IMG(src=url, Class='img-fluid') document["fetchImage"].bind("click", fetch_image) </script> </body> </html>
Note that the display now includes a dropdown selector widget, and the fetch_image function sets the URL to include a {size} variable inside curly brackets. In Python, putting variable values inside strings of text, using curly brackets, performs what's called 'concatenation' of the string - which simply means that the variable value is joined with the surrounding static text string. Concatenation is used constantly in every programming language. You'll notice that in Python, the letter 'f' is placed before strings that are to be concatenated. This is called an 'f-string' in Python. You'll see more of this, in all sorts of Python code!
Most data on the Internet is transferred in a format called 'JSON' (JavaScript Object Notation). Json typically includes either 'lists' of information, enclosed in square brackets:
[1, 2, 3] ['apple', 'orange', 'banana']and/or 'dictionaries' of information, made up of key-value pairs, enclosed in curly brackets:
{'color' : 'brown', 'size': 12}Note that values in json lists are separated by commas, and that strings of text are enclosed in single or double quotes:
'this is a string' "this is also a string"Single and double quotes can be mixed:
"I'm a string containing a single quote character" 'This string contains the quote "To be or not to be"'Multi-line strings are enclosed in triple quotes:
"""I'm a single string that spans a few lines"""You can assign a variable label to a list, and then pick items from it using index numbers (list indexes start at 0 in Python), and by using other Python 'slice' notations in the form [number:number]:
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Get the value at index 1 print(my_list[1]) # Output: 1 # Slice from index 2 to 5 (not including 5) print(my_list[2:5]) # Output: [2, 3, 4] # Slice from the start to index 4 (not including 4) print(my_list[:4]) # Output: [0, 1, 2, 3] # Slice from index 6 to the end print(my_list[6:]) # Output: [6, 7, 8, 9] # Slice the whole list print(my_list[:]) # Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Slice from the third last element to the end print(my_list[-3:]) # Output: [7, 8, 9] # Slice from the start to the third last element (not including) print(my_list[:-3]) # Output: [0, 1, 2, 3, 4, 5, 6] # Slice using negative start and end indices print(my_list[-5:-2]) # Output: [5, 6, 7] # Slice every second element from index 1 to 7 print(my_list[1:8:2]) # Output: [1, 3, 5, 7] # Slice every third element from the start to the end print(my_list[::3]) # Output: [0, 3, 6, 9] # Slice every second element from the list in reverse order print(my_list[::-2]) # Output: [9, 7, 5, 3, 1] # Replace a slice with new values my_list[2:5] = [20, 30, 40] print(my_list) # Output: [0, 1, 20, 30, 40, 5, 6, 7, 8, 9] # Delete a slice del my_list[5:7] print(my_list) # Output: [0, 1, 20, 30, 40, 7, 8, 9] # Insert elements using slice assignment my_list[5:5] = [50, 60] print(my_list) # Output: [0, 1, 20, 30, 40, 50, 60, 7, 8, 9]Don't be too put off by any complexity in the list above. For now, just get the basic idea that values can be picked from a list using the list_name[index:index] notation.
Using Dictionaries:
Dictionary keys are typically strings which are used to provide labels for each value in a dictionary. You use keys to pick values out of a dictionary:
# Creating a dictionary my_dict = {'name': 'Alice', 'age': 25, 'city': 'New York'} # Accessing values by keys print(my_dict['name']) # Output: Alice print(my_dict['age']) # Output: 25 # Adding a new key-value pair my_dict['email'] = 'alice@example.com' print(my_dict) # Output: {'name': 'Alice', 'age': 25, 'city': 'New York', 'email': 'alice@example.com'} # Modifying an existing value my_dict['age'] = 26 print(my_dict) # Output: {'name': 'Alice', 'age': 26, 'city': 'New York', 'email': 'alice@example.com'} # Deleting a key-value pair del my_dict['city'] print(my_dict) # Output: {'name': 'Alice', 'age': 26, 'email': 'alice@example.com'} # Using pop() to remove and return a value email = my_dict.pop('email') print(email) # Output: alice@example.com print(my_dict) # Output: {'name': 'Alice', 'age': 26} # Using get() to access values print(my_dict.get('name')) # Output: Alice print(my_dict.get('email', 'Not Available')) # Output: Not Available # Getting all keys keys = my_dict.keys() print(keys) # Output: dict_keys(['name', 'age', 'city']) # Getting all values values = my_dict.values() print(values) # Output: dict_values(['Alice', 25, 'New York']) # Getting all key-value pairs items = my_dict.items() print(items) # Output: dict_items([('name', 'Alice'), ('age', 25), ('city', 'New York')]) # Updating the dictionary with another dictionary my_dict.update({'city': 'New York', 'email': 'alice@example.com'}) print(my_dict) # Output: {'name': 'Alice', 'age': 25, 'city': 'New York', 'email': 'alice@example.com'} # Clearing the dictionary my_dict.clear() print(my_dict) # Output: {} # Iterating through keys for key in my_dict: print(key, my_dict[key]) # Output: # name Alice # age 25 # city New York # Iterating through key-value pairs for key, value in my_dict.items(): print(key, value) # Output: # name Alice # age 25 # city New York # Creating a nested dictionary nested_dict = { 'person1': {'name': 'Alice', 'age': 25}, 'person2': {'name': 'Bob', 'age': 30} } # Accessing values in a nested dictionary print(nested_dict['person1']['name']) # Output: Alice print(nested_dict['person2']['age']) # Output: 30 # Adding a new entry in a nested dictionary nested_dict['person3'] = {'name': 'Charlie', 'age': 35} print(nested_dict) # Output: # {'person1': {'name': 'Alice', 'age': 25}, # 'person2': {'name': 'Bob', 'age': 30}, # 'person3': {'name': 'Charlie', 'age': 35}}Now look at the JSON data returned by this REST API at https://jsonplaceholder.typicode.com/users/1
{ "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "Sincere@april.biz", "address": { "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": { "lat": "-37.3159", "lng": "81.1496" } }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": { "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets" } }There are a number of nested values in the info above. For example, user's 'lat' and 'lng' values are nested inside the 'geo' value, which is nested inside the address value. This is how most data transferred over the Internet looks!
You can pick a different user's record from the API above, just by changing the index number at the end of the URL. For example, at the URL https://jsonplaceholder.typicode.com/users/2 you'll find a similarly structured json document, but with different values:
{ "id": 2, "name": "Ervin Howell", "username": "Antonette", "email": "Shanna@melissa.tv", "address": { "street": "Victor Plains", "suite": "Suite 879", "city": "Wisokyburgh", "zipcode": "90566-7771", "geo": { "lat": "-43.9509", "lng": "-34.4618" } }, "phone": "010-692-6593 x09125", "website": "anastasia.net", "company": { "name": "Deckow-Crist", "catchPhrase": "Proactive didactic contingency", "bs": "synergize scalable supply-chains" } }
One of the most common techniques used in web applications is to retrieve json data values from REST APIs, using 'AJAX' calls. Brython enables AJAX requests with: ajax.get(URL). Save the following code as 'brython_json_api.html', and run it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Simple Brython AJAX Example</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">User Name</h2> <div id="output" class="alert alert-primary text-center"> <!-- User name will be displayed here --> </div> </div> <script type="text/python"> from browser import document, ajax def on_complete(req): user = req.json document['output'].text = user['name'] def fetch_user(): ajax.get('https://jsonplaceholder.typicode.com/users/1', oncomplete=on_complete) fetch_user() </script> </body> </html>
Notice the fetch_user function, which runs the brython ajax.get() method, which loads data from a REST API URL, and when complete, runs the on_complete function. The on_complete function sets the output of the object to display a value contained in the json data returned by the ajax call (a value labeled by the 'name' key, in the json labeled as 'user' ).
Since web applications are always dealing with network connections that can go down, it's a good idea to always check for any errors returned during the request process. The example below extends the one above with a conditional evaluation that checks the request status (a status of 200 means that the request was successfully returned by the API server):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Simple Brython AJAX Example</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">User Name</h2> <div id="output" class="alert alert-primary text-center" role="alert"> <!-- User name will be displayed here --> </div> </div> <script type="text/python"> from browser import document, ajax def on_complete(req): if req.status == 200: user = req.json document['output'].text = user['name'] else: document['output'].text = "Error fetching data" def fetch_user(): ajax.get('https://jsonplaceholder.typicode.com/users/1', oncomplete=on_complete) fetch_user() </script> </body> </html>
This next example extends what we've done above, to populate a table widget with the data from the ten records at the API above. The table header (the 'thead' tag), contain 6 columns, which are populated using a 'for' loop in the populate_table table function. Save this code to a file named 'brython_json_api_table.html' and run it in your browser:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Records with Brython</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">User Records</h2> <table class="table table-striped" id="dataTable"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Username</th> <th>Email</th> <th>Phone</th> <th>Website</th> </tr> </thead> <tbody id="table-body"> <!-- Data will be populated here by Brython --> </tbody> </table> </div> <script type="text/python"> from browser import document, ajax # Global variable to hold the fetched users users = [] # Function to fetch data from the API def fetch_data(): def on_complete(req): global users if req.status == 200: users = req.json populate_table(users) else: print("Error fetching data:", req.status) ajax.get('https://jsonplaceholder.typicode.com/users', oncomplete=on_complete) # Function to populate the table with data def populate_table(data): table_body = document['table-body'] table_body.innerHTML = '' for user in data: row = f""" <tr> <td>{user['id']}</td> <td>{user['name']}</td> <td>{user['username']}</td> <td>{user['email']}</td> <td>{user['phone']}</td> <td><a href="http://{user['website']}" target="_blank">{user['website']}</a></td> </tr> """ table_body.innerHTML += row # Fetch data when the page loads fetch_data() </script> </body> </html>
Note that the fetch_data function above is executed when the page loads. The 'tr' and 'td' tags, which are used to populate the table widget, stand for 'table row' and 'table data'. Each row contains 6 columns of data inside opening and closing 'td' tags, which you can see consist of each user's id, name, username, email, phone, and website values - all enclosed in curly brackets within concatenated strings, where the variable values are pulled from the dictionary labeled 'user'.
Now THAT's some fantastically useful stuff. In fact, doing things like that form the basis of most really useful web applications that you'll ever see. And, using datagrid tables is one of the most important things you'll learn to do in building web apps which handle useful data.
Since data grids are so important, let's finish up this section with an even more useful extension of the previous application, which includes more columns, and enables the user to sort each column by clicking on column headers:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Records with Brython</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">User Records</h2> <table class="table table-striped" id="dataTable"> <thead> <tr> <th id="header-id">ID</th> <th id="header-name">Name</th> <th id="header-username">Username</th> <th id="header-email">Email</th> <th id="header-address">Address</th> <th id="header-phone">Phone</th> <th id="header-website">Website</th> <th id="header-company">Company</th> </tr> </thead> <tbody id="table-body"> <!-- Data will be populated here by Brython --> </tbody> </table> </div> <script type="text/python"> from browser import document, ajax # Global variable to hold the fetched users users = [] # Function to fetch data from the API def fetch_data(): def on_complete(req): global users if req.status == 200: users = req.json populate_table(users) ajax.get('https://jsonplaceholder.typicode.com/users', oncomplete=on_complete) # Function to populate the table with data def populate_table(data): table_body = document['table-body'] table_body.innerHTML = '' for user in data: address = f"""{user['address']['street']}, {user['address']['suite']}, {user['address']['city']}, {user['address']['zipcode']}""" company = user['company']['name'] row = f""" <tr> <td>{user['id']}</td> <td>{user['name']}</td> <td>{user['username']}</td> <td>{user['email']}</td> <td>{address}</td> <td>{user['phone']}</td> <td><a href="http://{user['website']}" target="_blank">{user['website']}</a></td> <td>{company}</td> </tr> """ table_body.innerHTML += row # Function to sort the table data def sort_table(column, reverse=False): sorted_data = sorted(users, key=lambda x: x[column], reverse=reverse) populate_table(sorted_data) # Function to initialize sorting functionality def initialize_sorting(): sort_state = {} def toggle_sort(event, col): current_order = sort_state.get(col, False) sort_table(col, not current_order) sort_state[col] = not current_order for column in ['id', 'name', 'username', 'email', 'address', 'phone', 'website', 'company']: document[f'header-{column}'].bind('click', lambda event, col=column: toggle_sort(event, col)) # Fetch data when the page loads fetch_data() initialize_sorting() </script> </body> </html>
At this point, you may not yet understand every minute detail about how the code above works, but you should be able to follow that there are some tags which display a table in the document body, and some functions which fetch json data from a REST API server, using AJAX, and that there are some other functions which populate the data table, and which enable sorting. You can see that a 'for' loop is used to populate the table, and an 'if' conditional evaluation is used to check whether the response from the API server is valid. That's enough for the moment.
Everything you've seen so far, even though just barely cracking the surface of how to begin development applications, is certainly a lot to grok. Software development is hard and there are many details to learn! So, how are you supposed to ingrain all this learning, and start to build applications on your own, now that you've seen some of how it works?
Visual drag and drop tools have been around for many years, and they can really help ease the fatigue of laying out front-end user interfaces. It makes a lot of sense to create visual layouts, visually. There are many commercial products which can help you lay out extremely complex user interfaces, just by dragging widgets onto a canvas. bootstrapstudio.io ($59 for a lifetime license) and Pinegrow are two visual editors that specifically support Bootstrap. They can help speed up the development the most complex layouts. Using such visual editors is a great way to save brain cells for the more complex logical algorithmic engineering processes which can't be performed by dragging and dropping.
If you want to give visual layouts a shot, you can try a simple builder for free at:
And a tool which enables copying of selected code for pieces of visually previewed objects:
https://angrytools.com/bootstrap/editor
The angrytools code previewer is a nice way to get to know the basics of Bootstrap. Just click on the visual layout design items you want to use, and then copy the displayed code to paste into your apps. It's simple, and forces you to see/manipulate the code which produces desired layout results.
In the past, tutorials like this would turn into 1000 page books and web sites. Texts of that size were required to provide enough useful examples, not just to learn from, but to function as repositories of necessary code that can be re-used and altered to handle common situations. With the advent of generative AI, things in the programming industry have changed dramatically.
It's now possible to open chatGPT 4o, Claude Sonnet 3.5, Llama 3.1, Perplexity.ai, Deepseek2 Coder, and other chatbots - and they will write *exactly the code you need to perform nearly any software development goal (or at least manageable pieces of bigger goals).
Go to chatGPT and type in this prompt:
'Please provide the brython code to display 256 common colors used in web development, along with the names of those colors'Here's the output this author received to that prompt:
Sure! Here's a Brython code example that displays 64 common colors used in web development along with their names. The colors are displayed in a Bootstrap-styled grid. HTML and Brython Code: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Common Web Colors with Brython</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">Common Web Colors</h2> <div id="color-grid" class="row"> <!-- Colors will be populated here by Brython --> </div> </div> <script type="text/python"> from browser import document, html colors = [ ("Black", "#000000"), ("Navy", "#000080"), ("DarkBlue", "#00008B"), ("MediumBlue", "#0000CD"), ("Blue", "#0000FF"), ("DarkGreen", "#006400"), ("Green", "#008000"), ("Teal", "#008080"), ("DarkCyan", "#008B8B"), ("DeepSkyBlue", "#00BFFF"), ("DarkTurquoise", "#00CED1"), ("MediumSpringGreen", "#00FA9A"), ("Lime", "#00FF00"), ("SpringGreen", "#00FF7F"), ("Aqua", "#00FFFF"), ("Cyan", "#00FFFF"), ("MidnightBlue", "#191970"), ("DodgerBlue", "#1E90FF"), ("LightSeaGreen", "#20B2AA"), ("ForestGreen", "#228B22"), ("SeaGreen", "#2E8B57"), ("DarkSlateGray", "#2F4F4F"), ("DarkSlateGrey", "#2F4F4F"), ("LimeGreen", "#32CD32"), ("MediumSeaGreen", "#3CB371"), ("Turquoise", "#40E0D0"), ("RoyalBlue", "#4169E1"), ("SteelBlue", "#4682B4"), ("DarkSlateBlue", "#483D8B"), ("MediumTurquoise", "#48D1CC"), ("Indigo", "#4B0082"), ("DarkOliveGreen", "#556B2F"), ("CadetBlue", "#5F9EA0"), ("CornflowerBlue", "#6495ED"), ("MediumAquaMarine", "#66CDAA"), ("DimGray", "#696969"), ("DimGrey", "#696969"), ("SlateBlue", "#6A5ACD"), ("OliveDrab", "#6B8E23"), ("SlateGray", "#708090"), ("SlateGrey", "#708090"), ("LightSlateGray", "#778899"), ("LightSlateGrey", "#778899"), ("MediumSlateBlue", "#7B68EE"), ("LawnGreen", "#7CFC00"), ("Chartreuse", "#7FFF00"), ("Aquamarine", "#7FFFD4"), ("Maroon", "#800000"), ("Purple", "#800080"), ("Olive", "#808000"), ("Gray", "#808080"), ("Grey", "#808080"), ("SkyBlue", "#87CEEB"), ("LightSkyBlue", "#87CEFA"), ("BlueViolet", "#8A2BE2"), ("DarkRed", "#8B0000"), ("DarkMagenta", "#8B008B"), ("SaddleBrown", "#8B4513"), ("DarkSeaGreen", "#8FBC8F"), ("LightGreen", "#90EE90"), ("MediumPurple", "#9370DB"), ("DarkViolet", "#9400D3"), ("PaleGreen", "#98FB98"), ("DarkOrchid", "#9932CC"), ("YellowGreen", "#9ACD32"), ("Sienna", "#A0522D"), ("Brown", "#A52A2A"), ("DarkGray", "#A9A9A9") ] def create_color_block(name, color): block = html.DIV(Class="col-3 mb-4") block <= html.DIV( style={"background-color": color, "height": "50px", "width": "100%"} ) block <= html.P(name, Class="text-center") return block color_grid = document["color-grid"] for name, color in colors: color_grid <= create_color_block(name, color) </script> </body> </html> Explanation: HTML Structure: Included Bootstrap CSS for styling. A container with Bootstrap classes for layout and styling. A div with an ID of color-grid to display the color blocks in a grid layout. Brython Script: Import the necessary modules (document and html). Define a list of tuples named colors, where each tuple contains the name and hex value of a color. Define the create_color_block function to create a Bootstrap-styled color block with the color name and color display. Iterate over the colors list and populate the color-grid with color blocks by calling the create_color_block function. This code displays 64 common colors used in web development along with their names in a Bootstrap-styled grid layout using Brython.
The sections of that generated code look pretty familiar, don't they? In the past, that code would have taken some time to write, test, and debug. Now it takes a few seconds, and if there's anything you don't understand about how any of the code works, you can *ask GPT what it means. You can ask for an on the spot tutorial about anything you want to learn. You can also ask GPT to alter and extend any code that's been created so that it works exactly how you want it to work.
If any code created by any LLM ('Large Language Model' like chatGPT) doesn't work properly, just press the F12 key on your keyboard, copy the error messages in your browser's console, and paste them directly in for your AI to read. You typically don't even need to explain much about what went wrong while running the code - the AI will understand the error messages and re-generate code that attempts to fix the error. A few cycles of running a generated app, and pasting errors into your AI chatbot, are typically all that's required to fix the toughest problems. If your AI gets stuck and can't find a solution, paste the code generated by one AI into another AI (try chatGPT, Claude, and Perplexity, or if you want to use open-source AIs, try Llama3.1 and Deepseek2). Just like when working with a group of people, one AI may come up with answers that another AI can't. You can use your AI assistants to work like a team of employees who help each other move forward on tough software projects.
If you're going do any work in software development, you will be 10x-1000x more productive if you learn to use AI to generate code, right from the beginning. Most AIs are now better at generating working code than many experienced human developers, as long as you're using well known languages and software development tools. AIs tend to be absolute virtuosos particularly at writing Python, Bootstrap, and code for the other frameworks/libraries used in this tutorial.
Previously in this tutorial, we used a couple freely available public REST APIs to produce random image and JSON outputs that we then displayed in front-end code. Now we're going to build our *own REST API servers to output data which can be displayed in front-end layouts. The difference is, by creating our own servers, we can manipulate our own in-house data, to be used to manage operations in our own bespoke projects and environments.
Back-end server apps typically run in hosted environments that you can rent for a few dollars a month, but for the sake of this tutorial, you can initially run the server apps directly on whatever desktop PC you have available (Windows, Mac, or Linux).
In order to build the server portion of your app, you'll need to download and install Python. Go to python.org/downloads and download any recent version of Python that will run on your computer. Installation takes less than a minute.
Once Python is installed on your computer, open a command line and type the following (you can run 'cmd' on Windows to get to your command line). This will install the 'Flask' server library in your Python installation:
'pip install flask'Once that's complete, save the following code to a text file named 'flask1.py'. Note that the first line imports the Flask library we just installed. Notice also that there's a function definiton named home(), which simply returns the static (unchanging) text string "Hello, World!":
from flask import Flask app = Flask(__name__) @app.route('/') def home(): return "Hello, World!" if __name__ == '__main__': app.run()Now on your command line type:
python flask1.pyThen open your browser to the URL below, and you'll see the "Hello, World!" message:
http://127.0.0.1:5000
Next, save the code below to a file named 'flask_time.py'. You can see that this program imports the Python 'datetime' library, along with the Flask library. We didn't have to 'pip install' the datetime library, because it's included in the 'standard library' that ships with Python. You've already seen the datetime library in use on front-end applications earlier in the tutorial - it just contains a bunch of functions to deal with dates and times. In this code, we'll use it to get the current time, which we'll assign the variable label 'now'. Then we'll use the '.strftime' method of the 'now' object to format the datetime value into a nicely printable year-month-day-hour-minute-second format. Notice that the value returned from the home() function is a concatenated f-string containing the current_time value (with the variable enclosed in curly brackets), just as you've seen performed previously in front-end code:
from flask import Flask from datetime import datetime app = Flask(__name__) @app.route('/') def home(): now = datetime.now() current_time = now.strftime("%Y-%m-%d %H:%M:%S") return f"The current time is {current_time}" if __name__ == '__main__': app.run()Run the app above by typing the following at your command line:
python flask2.pyThen open your browser to URL below, and you'll see the current time. Refresh the page a few times, and you'll see the displayed time change:
http://127.0.0.1:5000
So the previous two examples are doing something potentially useful, but what we really want to use the server to do, instead of displaying output directly, is to return *data values that a front-end application can display. And typically, we want to return that data in *json format. For example, here' a little flask server app which returns the current time along with a randomly chosen animal name from a Python list. Save this code as 'flask_random_animal_api.py':
from flask import Flask, jsonify from flask_cors import CORS from datetime import datetime import random app = Flask(__name__) CORS(app) # This will enable CORS for all routes @app.route('/data') def data(): now = datetime.now() current_time = now.strftime("%Y-%m-%d %H:%M:%S") animals = [ "Lion", "Tiger", "Elephant", "Giraffe", "Zebra", "Monkey", "Panda", "Kangaroo", "Penguin", "Dolphin", "Shark", "Whale", "Eagle", "Owl", "Hawk", "Falcon", "Bear", "Wolf", "Fox", "Rabbit", "Deer", "Leopard", "Cheetah", "Hippo", "Rhino", "Crocodile", "Alligator", "Bison", "Buffalo", "Moose" ] chosen_animal = random.choice(animals) response = { "current_time": current_time, "random_animal": chosen_animal } return jsonify(response) if __name__ == '__main__': app.run()In the code above, we use a new libary handle CORS. CORS is beyond the scope of this tutorial, but before we can run the example above, we need to install the library:
pip install flask-corsNow we can run 'python flask_random_animal_api.py' at the command line, and open the browser to the following URL (be sure to include the /data path):
http://127.0.0.1:5000/data
Let's take a look at the code above and see what's happening:
- First, the flask and jsonify modules are imported from the flask library. Then CORS module is imported from the flask_cors library. Then the datetime and random modules are imported from the Python standard library. BTW, flask, flask-cors, and any other Python libraries you install with 'pip install', will stay installed on the computer you're working on, until you manually remove or change them.
- Next, the current time is formatted and assigned to the variable label 'current_time', the same way you've seen in the previous example.
- Next, a list of animals is enclosed in a Python list. Remember, in a Python list, each string is surrounded by quotes, separated by commas, and the whole list is enclosed in square brackets. A random choice from that list is assigned the variable label 'chosen_animal'.
- The varable label 'response' is assigned to a dictionary containing both those variable values, with keys named 'current_time' and 'random_animal'. Remember, dictionaries consist of key-value pairs, where the key is a label, and the value is the data assigned to that label, all enclosed in curly brackets
- Finally, the values in the resulting dictionary are returned by the app. The 'jsonify' function that we imported from the flask library at the beginning of the script, is used here to ensure the data is returned in a valid json format.
Now go http://127.0.0.1:5000/data in your browser and you'll see the data displayed in json format. Refresh the browser page, and you'll see an updated datetime value along with a different random animal name, each time you refresh.
WHAT YOU'VE JUST DONE IS CREATED A REST API. That REST API works just like the publicly available APIs that were used by front-end code examples earlier in the tutorial. This is bit of a silly REST API, but it demonstrates the basics of how REST APIs are created.
So now let's make a front-end to display the json data returned by our server. Save this code as 'flask_random_animal_api.html':
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Current Time and Random Animal</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">Current Time and Random Animal</h2> <div id="output" class="alert alert-primary text-center"> Loading... </div> <div class="text-center mt-4"> <button id="refresh-button" class="btn btn-primary">Refresh</button> </div> </div> <script type="text/python"> from browser import document, ajax def on_complete(req): data = req.json current_time = data['current_time'] random_animal = data['random_animal'] output = f"The current time is {current_time}. Your random animal is: {random_animal}" document['output'].text = output def fetch_data(event=None): ajax.get('http://127.0.0.1:5000/data', oncomplete=on_complete) # Bind the fetch_data function to the button click event document['refresh-button'].bind('click', fetch_data) # Fetch data when the page loads fetch_data() </script> </body> </html>
Here are a few things to note about the front-end code:
- The HTML structure includes Bootstrap for styling, and a container named 'output' to display the data, just as in all the previous examples you've seen.
- A button with the ID 'refresh-button' is added to allow the user to refresh the displayed values.
- The Brython script section fetches the JSON data from the REST API we created in Flask, and displays it in the 'output' div. The fetch_data function makes an AJAX request to http://127.0.0.1:5000/data and calls on_complete when the request completes.
- The on_complete function extracts the current_time and random_animal values from the JSON response and updates the output div with this information.
- The fetch_data function is bound to the button click event, so it is called whenever the user clicks the "Refresh" button.
- The fetch_data function is also called when the page loads to display initial data.
Open the file above in your browser, and click the button in the app a few times. You'll see a nicely formatted display of data from our current time/random animal API, displayed in the app.
Congratulations, you've just created your first FULL STACK app (both a front-end and a back-end working together), entirely with Python code.
So far, we've opened all our .html front-end files by clicking them directly in the file explorer of the local machine. Well, normally this is not how web apps work. Instead, users from anywhere on the Internet can go to the URL of a web app, and see the front end which is served at that web site address (as opposed to clicking an HTML file stored on their own hard drive). We can use flask to simply serve the front-end .html file we've created, to make it part of our full-stack creation. Save the following code as flask_random_animal_api_serve_html.py, run it on your command line with 'python flask_random_animal_api_serve_html.py', and open your browser to http://127.0.0.1:5000/:
from flask import Flask, jsonify, send_from_directory from flask_cors import CORS from datetime import datetime import random import os app = Flask(__name__) CORS(app) # This will enable CORS for all routes @app.route('/') def index(): return send_from_directory('.', 'flask_random_animal_api.html') @app.route('/data') def data(): now = datetime.now() current_time = now.strftime("%Y-%m-%d %H:%M:%S") animals = [ "Lion", "Tiger", "Elephant", "Giraffe", "Zebra", "Monkey", "Panda", "Kangaroo", "Penguin", "Dolphin", "Shark", "Whale", "Eagle", "Owl", "Hawk", "Falcon", "Bear", "Wolf", "Fox", "Rabbit", "Deer", "Leopard", "Cheetah", "Hippo", "Rhino", "Crocodile", "Alligator", "Bison", "Buffalo", "Moose" ] chosen_animal = random.choice(animals) response = { "current_time": current_time, "random_animal": chosen_animal } return jsonify(response) if __name__ == '__main__': app.run()
Notice in code above that we have a 'route' set up for '/' and another route set up for '/data'. At the '/' route, we have a function definition which sends our .html file as the return value:
return send_from_directory('.', 'flask_random_animal_api.html')So when the user goes to the server URL with '/' at the end (http://127.0.0.1:5000/) they see the .html page displayed in their browser.
If the user goes to the server URL with '/data' at the end (http://127.0.0.1:5000/data) they'll see the json data returned by the 'data' function definition. That's the URL where the front-end app performs its AJAX call to retrieve datetime and random animals names (you *can go to that /data URL in your browser, while the server is running, and see the raw data, but users just want to visit the home page and see the nicely formatted front-end display).
With this, we have a complete full-stack app. This was all accomplished entirely using Python code on the back-end and the front-end.
Here's another full-stack example to help you get more familiar with the code patterns used to create APIs, to retrieve data from those APIs on the front-end, and to serve front-end pages. This code adds one more technique which you haven't seen yet: it takes 2 pieces of info collected from the user (a name, typed into a text field, and a greeting message, selected from a dropdown selector), and sends those values to the back-end server app, where they're combined with a random flattering word from a list, and then a full concatenated flattering sentence is returned back to the front-end, via an AJAX query, to be viewed by the user.
Note that we could certainly produce the exact same concatenated string result containing some flattering text, using only front-end code, without ever needing a server for this app. The point with this example, however, is to learn how values collected from the user can be posted to the back-end, where the server processes that info, and returns some value to a front-end API request, and a final value which has been computationally manipulated by that full round-trip process can be displayed for the user to see.
Save this back-end code to a file named 'flatter.py':
from flask import Flask, jsonify, request, send_from_directory from flask_cors import CORS import random app = Flask(__name__) CORS(app) # This will enable CORS for all routes flattering_words = ["dearest", "beautiful", "enchanting", "wonderful", "amazing", "gorgeous", "charming"] @app.route('/') def index(): return send_from_directory('.', 'flatter.html') @app.route('/greet', methods=['POST']) def greet(): data = request.get_json() name = data.get('name', 'World') greeting = data.get('greeting', 'Hello') flattering_word = random.choice(flattering_words) full_greeting = f"{greeting.capitalize()} {flattering_word} {name}!" return jsonify({"greeting": full_greeting}) if __name__ == '__main__': app.run()Save this front-end code to a file named 'flatter.html':
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flattery App</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">Flattery</h2> <div class="form-group"> <label for="greeting-select">Select a greeting:</label> <select id="greeting-select" class="form-control"> <option value="Hi">Hi</option> <option value="Hello">Hello</option> <option value="Greetings">Greetings</option> <option value="Bonjour">Bonjour</option> <option value="Hey">Hey</option> </select> </div> <div class="form-group"> <label for="name-input">Enter your name:</label> <input type="text" id="name-input" class="form-control" placeholder="Your Name"> </div> <div class="text-center mt-4"> <button id="greet-button" class="btn btn-primary">Greet Me</button> </div> <div id="output" class="alert alert-primary text-center mt-4" style="display:none;"></div> </div> <script type="text/python"> from browser import document, ajax, window def on_complete(req): data = window.JSON.parse(req.text) # Use JSON.parse to handle JSON response greeting = data['greeting'] output_div = document['output'] output_div.text = greeting output_div.style.display = 'block' def send_greeting(event): name = document['name-input'].value greeting = document['greeting-select'].value ajax.post( '/greet', data=window.JSON.stringify({'name': name, 'greeting': greeting}), headers={'Content-Type': 'application/json'}, oncomplete=on_complete ) # Bind the send_greeting function to the button click event document['greet-button'].bind('click', send_greeting) </script> </body> </html>Run the server app by typing 'python flatter.py' in your operating system command console, and go to http://127.0.0.1/5000 to see the app in your browser.
Notice that the send_greeting function definition uses brython's 'ajax.post' method, which is used to send data to the '/greet' route handled by the server. This is a REST API call, which sends json data to the server, rather than retrieving it, as we've seen in other examples so far in the tutorial. Notice along with sending some concatenated json data, that the ajax.post request also sends some 'headers' which tell the server that the data is json content. When the ajax.post request is completed, the on_complete function is executed, which parses the data returned by the server (stored in the 'data' variable). Everything else in the code above is similar to examples you've seen previously in this tutorial.
All this may not seem too exciting at first glance, but it forms the core of a fully capable full-stack methodology which can be used to build just about any sort of imaginable interaction and functionality between front-end and back-end pieces of a web app.
So far, our back-end server applications have been limited to using hard-coded data, where values have been retrieved from Python lists stored directly in the application code. For a server to be able to store live data sent by users, or otherwise collected by computational means, we need to use a 'database'.
Databases store values in columns and rows called 'tables', in a way that can be visualized similarly to spreadsheet grids. Database tables are each given names, and each column in a table is given a name (much like column headers in a spreadsheet). Each of the columns in a table is designed to hold only one pre-defined type of data (i.e., a text string, number, date, true-false 'boolean' value, json data structure, etc.).
Databases are extremely fast at storing and retrieving rows of data which match a given search 'query'. Database queries are typically written in a language called 'SQL'. The entire SQL language has only one reason for being, which is to work with data in databases. SQL queries are written to retrieve rows of data which match a specified logic, for example, 'select all the rows from the Users table where the name contains John, the birthday is after 1950, and the test result is greater than 85'.
Databases and the SQL language are particularly good at combining related values which are linked between tables. For example, while collecting a list of User rows from the example query above, we could ask the database to include all the account Sales transaction information for each user, even if that sales info is stored in a separate table from the User account info, and combine the resulting information into rows of results with a specified column structure. As long as database tables are designed to have one unique linked 'key' column, shared between tables, info from any of those linked tables can be combined. For tables to be linked, they just need to contain a shared unique ID column. For example, as long as a Users table and a Sales table both contain a linked UserID column, info from both those tables can be collected together into a single search query.
Information from that sort of linking can go many levels deep, so that, for example, we could ask the database to return only results in which rows from the User account table match given parameters, and only include Users for which Red shoes from a specified manufacturer are found in the users' Sales table rows, and only for red shoe sales which have a specified delivery date found in a Shipping table, etc.
The 'schema' of a database includes the table names which are saved in the database, along with the structure of column names and data types found in each table. Designing a table schema which can sensibly store and link all the data needed in a application, is one of the most important parts of building any sort of software, no matter the language or tools used to build the software.
SQL language usage can get messy, because it's typically combined with the code of whatever other multiple programming languages are being used to build an application - and very often, the ways those languages store and deal with different sorts of data may not align neatly. So, most typical full-stack web applications use at least JavaScript, SQL, and at least one additional server programming language (Python, PHP, Java, Ruby, etc.), along with libraries, frameworks, drivers, data structures such as json, protocols such as AJAX, and lots of other complicated coding machinery to make everything work together. And to make matters even just a bit worse, each type of database system (Sqlite, MySQL, MSSQL, Postgres, etc.) all use their own slightly different versions of SQL language. Ugh.
Python helps to make all that machinery and language wrangling much simpler, with 'ORM' (Object Relational Mapper) libraries that replace SQL query language with pure Python code. The most popular Python ORM is called 'SQLAlchemy'. There are many, and much simpler ORM libraries available in the Python ecosystem, but we'll dive right into SQLAlchemy because it's extremely powerful, very popular, it supports all the most common database systems used in the industry, and perhaps just as import, AI code generators (chatGPT, Claude, etc.) are fantastically capable of writing any SQLAlchemy code you ask for.
To install SQLAlchemy, run the following at your command line:
pip install sqlalchemyNow save the following code to a file named 'sqlalchemy1.py', and run it on the command line with 'python sqlalchemy1.py':
from sqlalchemy import create_engine, Column, Integer, String, Date from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker import datetime # Create an engine that stores data in the local directory's sqlite database file. engine = create_engine('sqlite:///users.db', echo=True) # Create a base class for our classes definitions Base = declarative_base() # Define the User class which will be mapped to the 'users' table class User(Base): __tablename__ = 'users' userID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String) birthday = Column(Date) favorite_food = Column(String) # Create the 'users' table in the database Base.metadata.create_all(engine) # Create a new session to interact with the database Session = sessionmaker(bind=engine) session = Session() # Create a new user instance new_user = User( name="John Doe", birthday=datetime.date(1990, 1, 1), favorite_food="Pizza" ) # Add the new user to the session and commit it to the database session.add(new_user) session.commit() # Query the database to check if the user was added users = session.query(User).all() for user in users: print(f"ID: {user.userID}, Name: {user.name}, Birthday: {user.birthday}, Food: {user.favorite_food}")
That's a lot of boilerplate, but just like with the front-end code you've seen previously, it's best to just see it all up front, and get used to focusing on the important parts.
You can see in the code above that the selected database 'engine' is 'sqlite'. Sqlite is a small single-file database system which comes built into Python, so using it doesn't require any installation. Sqlite is one of the most popular database systems in the world. It's made for small environments, but it can handle significant volumes of data. It's common practice in the Python ecosystem to build software using sqlite, and then switch to a bigger production database system such as Postgres or MSSQL, when publishing the application live for millions of users. SQLAlchemy makes that 'migration' process between databases incredibly simple. All you need to do is change the connection string line, to connect your code to the production database system, and no matter what type of RDBMS systems you're switching between, all your application code in your app can stay exactly the same.
When you run that app above, you'll see a lot of feedback:
CREATE TABLE users ( "userID" INTEGER NOT NULL, name VARCHAR, birthday DATE, favorite_food VARCHAR, PRIMARY KEY ("userID") ) 2024-07-25 10:58:32,451 INFO sqlalchemy.engine.base.Engine () 2024-07-25 10:58:32,462 INFO sqlalchemy.engine.base.Engine COMMIT 2024-07-25 10:58:32,464 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 2024-07-25 10:58:32,465 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, birthday, favorite_food) VALUES (?, ?, ?) 2024-07-25 10:58:32,465 INFO sqlalchemy.engine.base.Engine ('John Doe', '1990-01-01', 'Pizza') 2024-07-25 10:58:32,467 INFO sqlalchemy.engine.base.Engine COMMIT 2024-07-25 10:58:32,475 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 2024-07-25 10:58:32,475 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name, users.birthday AS users_birthday, users.favorite_food AS users_favorite_food FROM users 2024-07-25 10:58:32,475 INFO sqlalchemy.engine.base.Engine () Name: John Doe, Birthday: 1990-01-01, Food: PizzaMuch of the feedback you see is the SQL code generated by SQLAlchemy, for the specific database system you're connected to. You can turn off that verbose output by switching the line echo=True to echo=False.
Another important section of the code above is the class definition of the User table:
class User(Base): __tablename__ = 'users' userID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String) birthday = Column(Date) favorite_food = Column(String)You can see that 4 columns have been created for that table schema: 'userID', 'name', 'birthday', and 'favorite_food'. You can see that the datatypes of those columns are Integer, String, Date, and String. You can also see that the userID column has been set as the 'primary_key', and that the database will create an autoincremented value for that column in each row stored in this table. That provides a unique identifier value for each row, which can be used to link with other tables which also contain userID values.
Another section to pay attention to in the example above is the code which creates a new row in the User table. This code adds a user with the name 'John Doe', a birthday of 1990-1-1, and a favorite_food of 'Pizza':
new_user = User( name="John Doe", birthday=datetime.date(1990, 1, 1), favorite_food="Pizza" )Also note the following bit of code, which queries the User table for all rows, and then uses a 'for' loop to print out concatenated strings for each row in the database. Notice the use of variable names inside curly braces again - you've seen that technique, where variable values are joined with static text, already many times already in this tutorial. It's that use of consistent language and data manipulation techniques, all using standard Python code patterns, which makes developing with Brython, Flask, and SQLAlchemy a very pleasant/fluid experience:
users = session.query(User).all() for user in users: print(f"ID: {user.userID}, Name: {user.name}, Birthday: {user.birthday}, Food: {user.favorite_food}")Finally, notice in the full example code above that a 'session' variable is created, and that the session.add(new_user) and session.commit() methods are used to add new user data. The session.query(User) piece is used to perform a search query. For every operation you perform with a database in SQLAlchemy, you'll create a session, perform some queries, and then commit and close the session.
Now that you've seen a bit about how SQLAlchemy works and what the code looks like, let's create another table to store information about foods. We'll link the favorite_food column in the User table, to foods in the foods table, via a column in the foods table named foodID. Notice the 'ForeignKey' link in the favorite_food column of the User table, which links to the foodID column in the foods table (foods.foodID):
from sqlalchemy import create_engine, Column, Integer, String, Date, ForeignKey, Float from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship import datetime # Create an engine that stores data in the local directory's sqlite database file. engine = create_engine('sqlite:///users_and_foods.db', echo=True) # Create a base class for our classes definitions Base = declarative_base() # Define the Foods class which will be mapped to the 'foods' table class Food(Base): __tablename__ = 'foods' foodID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) calories = Column(Float) protein = Column(Float) carbs = Column(Float) # Define the User class which will be mapped to the 'users' table class User(Base): __tablename__ = 'users' userID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) birthday = Column(Date) favorite_food = Column(Integer, ForeignKey('foods.foodID')) food = relationship('Food', backref='users') # Create the 'users' and 'foods' tables in the database Base.metadata.create_all(engine) # Create a new session to interact with the database Session = sessionmaker(bind=engine) session = Session() # Create new food instances pizza = Food(name="Pizza", calories=266, protein=11, carbs=33) burger = Food(name="Burger", calories=295, protein=17, carbs=30) pasta = Food(name="Pasta", calories=131, protein=5, carbs=25) # Add the new food instances to the session and commit them to the database session.add_all([pizza, burger, pasta]) session.commit() # Create a new user instance new_user = User( name="John Doe", birthday=datetime.date(1990, 1, 1), favorite_food=pizza.foodID # Link to the favorite food by its ID ) # Add the new user to the session and commit it to the database session.add(new_user) session.commit() # Query the database to check if the user and foods were added users = session.query(User).all() for user in users: print(f"""ID: {user.userID}, Name: {user.name}, Birthday: {user.birthday}, Favorite Food: {user.food.name}""") foods = session.query(Food).all() for food in foods: print(f"""ID: {food.foodID}, Name: {food.name}, Calories: {food.calories}, Protein: {food.protein}, Carbs: {food.carbs}""")
With the code above, we've created two tables, one containing user information, and the other containing food information. We can store all the information about foods (name, calories, protein, carbs, etc.), in the food database, and then query both the user table and the food table, and any links between them.
Here's a slight extension of the code above which adds more food rows and two additional users, each with different favorite foods than John Doe. It also includes a query which displays all the users who's favorite foods have protein greater than 10 and carbs less than or equal to 30. That's the sort of thing you can do with relational tables!
from sqlalchemy import create_engine, Column, Integer, String, Date, ForeignKey, Float from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship import datetime # Create an engine that stores data in the local directory's sqlite database file. engine = create_engine('sqlite:///users_and_foods.db', echo=True) # Create a base class for our classes definitions Base = declarative_base() # Define the Foods class which will be mapped to the 'foods' table class Food(Base): __tablename__ = 'foods' foodID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) calories = Column(Float) protein = Column(Float) carbs = Column(Float) # Define the User class which will be mapped to the 'users' table class User(Base): __tablename__ = 'users' userID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) birthday = Column(Date) favorite_food = Column(Integer, ForeignKey('foods.foodID')) food = relationship('Food', backref='users') # Create the 'users' and 'foods' tables in the database Base.metadata.create_all(engine) # Create a new session to interact with the database Session = sessionmaker(bind=engine) session = Session() # Create new food instances pizza = Food(name="Pizza", calories=266, protein=11, carbs=33) burger = Food(name="Burger", calories=295, protein=17, carbs=30) pasta = Food(name="Pasta", calories=131, protein=5, carbs=25) salad = Food(name="Salad", calories=50, protein=2, carbs=10) # Add the new food instances to the session and commit them to the database session.add_all([pizza, burger, pasta, salad]) session.commit() # Create new user instances john_doe = User( name="John Doe", birthday=datetime.date(1990, 1, 1), favorite_food=pizza.foodID # Link to the favorite food by its ID ) jane_smith = User( name="Jane Smith", birthday=datetime.date(1985, 5, 15), favorite_food=burger.foodID # Link to the favorite food by its ID ) alice_jones = User( name="Alice Jones", birthday=datetime.date(1992, 7, 20), favorite_food=salad.foodID # Link to the favorite food by its ID ) # Add the new user instances to the session and commit them to the database session.add_all([john_doe, jane_smith, alice_jones]) session.commit() # Query the database to check if the users and foods were added users = session.query(User).all() for user in users: print(f"ID: {user.userID}, Name: {user.name}, Birthday: {user.birthday}, Favorite Food: {user.food.name}") foods = session.query(Food).all() for food in foods: print(f"""ID: {food.foodID}, Name: {food.name}, Calories: {food.calories}, Protein: {food.protein}, Carbs: {food.carbs}""") # Query to display all users whose favorite foods have protein > 10 and carbs <= 30 filtered_users = session.query(User).join(Food).filter(Food.protein > 10, Food.carbs <= 30).all() print("\nFiltered Users (Favorite foods with protein > 10 and carbs <= 30):") for user in filtered_users: print(f""""ID: {user.userID}, Name: {user.name}, Favorite Food: {user.food.name}, Protein: {user.food.protein}, Carbs: {user.food.carbs}""")
And we can see right away that Jane Smith eats a high protein, low carb diet:
Filtered Users (Favorite foods with protein > 10 and carbs <= 30): ID: 3, Name: Jane Smith, Favorite Food: Burger, Protein: 17.0, Carbs: 30.0NOTE: the entire sqlite database in this example is stored in a single file. If you stop the server program from running, you can transfer all the data just by copying the users_and_foods.db file. Delete the users_and_foods.db file with:
del users_and_foods.db
It's really important to understand that results of SQLAlchemy queries are simply returned as native python data structures such as lists, dictionaries, individual values, etc. Your app can assign variable labels to query results, can slice lists of values, can run through rows of dictionary values using 'for' loops, etc. Use the data in query results exactly as you would any data structures that could be hard-coded in your app. The difference, of course is that data in a database can be entered, saved, changed, and manipulated live, by users of your app.
So using text strings from a database query is no different than using text strings which are entered into the UI by a user, for example. You can concatenate variable values with static text, perform math and logic calculations, etc., and send the results back to users via a RESP API call.
Let's take our SQLAlchemy database example above and integrate it. Save this back-end code to a file named sqlalchemy_flask.py, and run it:
from flask import Flask, jsonify, request, send_from_directory from flask_cors import CORS from sqlalchemy import create_engine, Column, Integer, String, Date, ForeignKey, Float from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, scoped_session, relationship import random import datetime import json app = Flask(__name__) CORS(app) # Create an engine that stores data in the local directory's sqlite database file with thread check disabled engine = create_engine('sqlite:///users_and_foods.db?check_same_thread=False', echo=True) Base = declarative_base() session_factory = sessionmaker(bind=engine) Session = scoped_session(session_factory) class Food(Base): __tablename__ = 'foods' foodID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) calories = Column(Float) protein = Column(Float) carbs = Column(Float) class User(Base): __tablename__ = 'users' userID = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) birthday = Column(Date) favorite_food = Column(Integer, ForeignKey('foods.foodID')) food = relationship('Food', backref='users') Base.metadata.create_all(engine) flattering_words = ["dearest", "beautiful", "enchanting", "wonderful", "amazing", "gorgeous", "charming"] @app.route('/') def index(): return send_from_directory('.', 'sqlalchemy_flask.html') @app.route('/foods', methods=['POST']) def add_food(): session = Session() data = request.json new_food = Food( name=data['name'], calories=data['calories'], protein=data['protein'], carbs=data['carbs'] ) session.add(new_food) session.commit() return jsonify({"message": "Food added successfully"}), 201 @app.route('/users', methods=['POST']) def add_user(): session = Session() data = request.json new_user = User( name=data['name'], birthday=datetime.datetime.strptime(data['birthday'], '%Y-%m-%d'), favorite_food=data['favorite_food'] ) session.add(new_user) session.commit() return jsonify({"message": "User added successfully"}), 201 @app.route('/foods', methods=['GET']) def get_foods(): session = Session() foods = session.query(Food).all() return jsonify([{ 'foodID': food.foodID, 'name': food.name, 'calories': food.calories, 'protein': food.protein, 'carbs': food.carbs } for food in foods]) @app.route('/users', methods=['GET']) def get_users(): session = Session() query = session.query(User) filters = request.args.get('filters') sort_by = request.args.get('sort_by') limit = request.args.get('limit') if filters: try: filters = json.loads(filters) for key, value in filters.items(): query = query.filter(getattr(User, key) == value) except json.JSONDecodeError: return jsonify({"error": "Invalid filters format"}), 400 if sort_by: query = query.order_by(getattr(User, sort_by)) if limit: query = query.limit(int(limit)) users = query.all() return jsonify([{ 'userID': user.userID, 'name': user.name, 'birthday': user.birthday.strftime('%Y-%m-%d'), 'favorite_food': user.food.name } for user in users]) if __name__ == '__main__': app.run()Save this front-end code to a file named sqlalchemy_flask.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Flask, SQLAlchemy & Brython App</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython.min.js"></script> <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/brython@3.11.0/brython_stdlib.js"></script> </head> <body onload="brython()"> <div class="container mt-4"> <h2 class="text-center">Flask, SQLAlchemy & Brython App</h2> <div class="form-group"> <label for="food-name">Food Name:</label> <input type="text" id="food-name" class="form-control"> <label for="food-calories">Calories:</label> <input type="number" id="food-calories" class="form-control"> <label for="food-protein">Protein:</label> <input type="number" id="food-protein" class="form-control"> <label for="food-carbs">Carbs:</label> <input type="number" id="food-carbs" class="form-control"> <button id="add-food-button" class="btn btn-primary mt-2">Add Food</button> </div> <div class="form-group"> <label for="user-name">User Name:</label> <input type="text" id="user-name" class="form-control"> <label for="user-birthday">Birthday (YYYY-MM-DD):</label> <input type="date" id="user-birthday" class="form-control"> <label for="user-favorite-food">Favorite Food ID:</label> <input type="number" id="user-favorite-food" class="form-control"> <button id="add-user-button" class="btn btn-primary mt-2">Add User</button> </div> <div class="form-group"> <button id="get-foods-button" class="btn btn-secondary mt-2">Get All Foods</button> </div> <div class="form-group"> <label for="user-filters">Filters (e.g., {"name": "John Doe"}):</label> <input type="text" id="user-filters" class="form-control"> <label for="user-sort-by">Sort By:</label> <input type="text" id="user-sort-by" class="form-control"> <label for="user-limit">Limit:</label> <input type="number" id="user-limit" class="form-control"> <button id="get-users-button" class="btn btn-secondary mt-2">Get Users</button> </div> <div id="output" class="mt-4"></div> </div> <script type="text/python"> from browser import document, ajax, window def display_result(data): output_div = document['output'] output_div.innerHTML = '<pre>' + data + '</pre>' def add_food(event): name = document['food-name'].value calories = document['food-calories'].value protein = document['food-protein'].value carbs = document['food-carbs'].value data = { 'name': name, 'calories': calories, 'protein': protein, 'carbs': carbs } ajax.post('/foods', data=window.JSON.stringify(data), headers={'Content-Type': 'application/json'}, oncomplete=lambda req: display_result(req.text)) def add_user(event): name = document['user-name'].value birthday = document['user-birthday'].value favorite_food = document['user-favorite-food'].value data = { 'name': name, 'birthday': birthday, 'favorite_food': favorite_food } ajax.post('/users', data=window.JSON.stringify(data), headers={'Content-Type': 'application/json'}, oncomplete=lambda req: display_result(req.text)) def get_foods(event): ajax.get('/foods', oncomplete=lambda req: display_result(req.text)) def get_users(event): filters = document['user-filters'].value sort_by = document['user-sort-by'].value limit = document['user-limit'].value params = { 'filters': filters, 'sort_by': sort_by, 'limit': limit } query_string = '&'.join([f'{key}={value}' for key, value in params.items() if value]) url = '/users?' + query_string ajax.get(url, oncomplete=lambda req: display_result(req.text)) document['add-food-button'].bind('click', add_food) document['add-user-button'].bind('click', add_user) document['get-foods-button'].bind('click', get_foods) document['get-users-button'].bind('click', get_users) </script> </body> </html>Run the app by typing 'python sqlalchemy_flask.py' at your command line, and open your browser to http://127.0.0.1:5000
By now, your head may be spinning, and it may seem like this is all quite complicated. Keep in mind, what you want to focus on right now is to just be aware of all the parts. The truly enormous benefit that everyone in the world currently has available, which has never existed before in software development, is that AI LLMs like chatGPT can literally write all the code you'll ever need, for any piece of the systems we've covered in this tutorial.
Try pasting these prompts into chatGPT:
Please create the simplest possible SQLAlchemy example, which creates a User table with userID, name, birthday, and favorite food columns. Please add anther table called Foods, to the SQLAlchemy schema. Adjust the schema so that the favorite_food column of the User table links to the foodID column of the foods table. Include columns for calories and a few other useful values in the foods table. Please add two more users, all with a different favorite from from John Doe. Please provide the code for a query which displays all the users who's favorite foods have protein greater than 10 and carbs less than or equal to 30. Please build Flask routes for this database, with functions to handle REST API calls to add new foods, add new users, return all food rows, return rows from the User database which can be filtered, sorted and limited by any column. Please provide the code for a brython front end to send all those requests, and display all results, including any rows in Bootstrap tablesNot only will the AI create fully functional, working code, but it will also provide complete explanations about how everything works.
Focus on understanding all the parts of all the libraries you want to use, and how they work together, then learn how to ask questions of the LLMS to get them to understand exactly what code you need to have written. The key is to provide every possible bit of context, description, detail and technical explanation about the data and tools you need to work with. Begin with this approach, and you already know enough to start creating applications. You can learn along the way, have the AI fix its own mistakes, and explain any detail about anything you don't understand. This is truly the new way of doing things, and it can improve your learning curve by 10x-1000x.
So far, you've run all these tutorial applications on your local computer. So how do we publish them online? Although it is possible to set up your home Internet router to open ports and enable your PC to accept requests from the Internet, there are better ways to serve your applications to the world.
'Hosting' companies such as A2hosting, Contabo, and many others provide virtual machines you can rent for a just a few dollars a month, to host your applications.
When choosing a hosting type, you'll generally want to get started with inexpensive 'VPS' (Virtual Private Server) accounts. VPS hosting is very affordable, but extremely powerful and scalable. The down side with VPS hosting is that you shouldn't expect any help from your hosting company when it comes to setting up your software, but chatGPT can be a huge help learning how to use a VPS system.
In general, VPS accounts simply provide a complete computer system, with an operating system installed, which you can use just like a computer at home. The big difference is that they're hosted on extremely fast server computers which are attached to super fast Internet connections.
You're going to need to choose the operating system that your application runs on. Linux OS is the most popular OS for running web applications, but there's definitely a learning curve to using Linux, which is beyond the scope of this tutorial. If you want to dive into Linux, you'll particularly want to learn how to use 'apt' to install requirements, 'tmux' to run multiple console sessions, 'pico' to edit text files, and 'SSH' to connect to the command line of your server computer, along with all the normal OS and file navigation command such as 'ls', 'cd', 'copy', 'rm', and others.
Learning Linux will enable faster and cheaper hosting options, as well as industry standard hosting practices with the widest variety of options, but if Linux is beyond your current possible scope of learning, you can rent a Windows VPS, to which you'll get screen share access. Shared desktop access on a VPS account provides an interface which is just like sitting in front of a local PC, where you can run your full-stack apps, and they'll be served to the world. Windows VPS is generally more expensive than Linux VPS. You can get Linux VPS from A2, starting at $2.99 monthly, and Window VPS from Contabo starting at about $15 for a usable setup.
If you're going to get deeper into building web apps, learning all about Linux is certainly a great next step to add to your to-do list!
Learn to replace the Brython code examples in this tutorial with JavaScript jQuery, and you'll be smack dab in the center of the way web applications have most often been built during the past decade and a half. After finishing this tutorial, it's not much of a jump at all to learn how to complete your front-end coding in JavaScript. From there, you may want to learn to replace all the front-end machinery entirely, with frameworks like React, Vue, Svelte, and others which have been incredibly popular in recent years.
The best way to continue from here is to use AI as your guide. ChatGPT, Claude, Perplexity, Llama, Deepseek, etc. can answer your questions, write code, provide further tutorials, and help you understand what tools are available to complete any project goal you have in mind. The path towards creating useful and powerful software has been dramatically simplified since 2022, and it will only continue to get easier to develop complex software solutions. Take what you've learned in this tutorial about the pieces and parts needed to begin full-stack development, and ask your AI guides about how to move forward with any goal you have. Start by learning about all the libraries and tools available in the Python ecosystem: explore more about Brython, Flask and SQLAlchemy, and learn about all the alternatives to those libraries that are available for free. Continue learning more about Bootstrap and dive into SQL, JavaScript, and every other tool available that will help you achieve goals - they are all interchangeable and interoperable. Learn about Linux and the whole ecosystem around that OS. Learn about all the support channels available online, such as StackOverflow and good old Google search.
Take a look at some of this author's other tutorials:
pythonanvil.com (this is the author's go-to Python framework for big production projects, and this tutorial is comprehensive, 200+ pages)
streamlitpython.com (this covers a popular and productive Python framework which can be very useful for data science visualations and building all sort of small in-house apps)
learnsqlpage.com (this framework uses a dialect of SQL to generate full stack apps - it's very productive, powerful, and easy to learn)
The world of software development is wide open and easier to learn about than it's ever been. Good luck and have fun!