Beginner Concepts
Last Updated: May 2022
This tutorial introduces important concepts for first-time or beginner Tethys developers. The topics covered include:
The App Class
Model View Controller
Introduction to the View
Django Template Language
Introduction to the Controller
Templating Gizmos
Custom Styles and CSS
Linking Between Pages
App Navigation
1. App Class
The app class, located in app.py
is the primary configuration file for Tethys apps. All app classes inherit from the TethysAppBase
class.
Open
app.py
in your favorite Python IDE or text editor.Change the theme color of your app by changing the value of the
color
property of theDamInventory
class. Use a site like color-hex to find an appropriate hexadecimal RGB color.You can also change the icon of your app. Find a new image online (square images work best) and save it in the
public/images/
directory of your app. Then change the value of theicon
property of theDamInventory
class to match the name of the image.
Tip
For more details about the app class, see the App Base Class API.
Warning
DO NOT change the value of the index
, package
, or root_url
properties of your app class unless you know what you are doing. Doing so will break your app.
2. App Settings
Other settings for your app can be configured in the app settings. App settings are stored in a database, meaning they can be changed dynamically by the administrator of your portal. Some app settings, like Name and Description, correspond with properties in the app.py
script.
To access the app settings, click on the Settings button (gear icon) at the top right-hand corner of your app.
Change the Name and Description of your app by changing their respective values on the app settings page. Press the
Save
button, located at the bottom of the app settings page. Then navigate back to your app to see the changes.
You can also create custom settings for your app that can be configured on the app settings page:
Open the
app.py
and add thecustom_settings()
method to theDamInventory
class. Don't for get to importCustomSetting
:from tethys_sdk.app_settings import CustomSetting ... class DamInventory(TethysAppBase): """ Tethys app class for Dam Inventory. """ ... def custom_settings(self): """ Example custom_settings method. """ custom_settings = ( CustomSetting( name='max_dams', type=CustomSetting.TYPE_INTEGER, description='Maximum number of dams that can be created in the app.', required=False ), ) return custom_settings
Warning
Ellipsis in code blocks in Tethys tutorials indicate code that is not shown for brevity. When there are ellipsis in the code, DO NOT COPY AND PASTE THE BLOCK VERBATIM.
Save changes to
app.py
.The development server should automatically restart when it detects changes to files. However if it does not restart, you can manually restart it by pressing
CTRL-C
to stop the server followed by thetethys manage start
command to start it again.Navigate to the settings page of your app and scroll down to the Custom Settings section and you should see an entry for the
max_dams
settings. Enter a value and save changes to the setting. You will learn how to use this custom setting in the app later on in the tutorial.
Tip
For more information about app settings, see the App Settings API.
3. Model View Controller
Tethys apps are developed using the Model View Controller (MVC) software architecture pattern. Model refers to the data model and associated code, View refers to the representations of the data, and Controller refers of the code that coordinates data from the Model for rendering in the View. In Tethys apps, the Model is usually an SQL database or files and the code for accessing them, the Views are most often the templates or HTML files, and Controllers are implemented as Python functions or classes.
Tip
For more information about the MVC pattern, see Key Concepts.
4. Views
Views for Tethys apps are constructed using the standard web programming tools: HTML, JavaScript, and CSS. Additionally, HTML templates can use the Django Template Language, because Tethys Platform is build on Django. This allows you to coding logic into your HTML documents, using template tags, making the web pages of your app dynamic and reusable.
Open
/templates/dam_inventory/home.html
and replace it's contents with the following:{% extends "dam_inventory/base.html" %} {% load tethys_gizmos %} {% block app_content %} {% gizmo dam_inventory_map %} {% endblock %} {% block app_actions %} {% gizmo add_dam_button %} {% endblock %}
Tip
Django Template Language: If you are familiar with HTML, the contents of this file may seem strange. That's because the file is actually a Django template, which contains special syntax (i.e.: {% ... %}
and {{ ... }}
to make the template dynamic. Django templates can contain variables, filters, and tags.
Variables. Variables are denoted by double curly brace syntax like this: {{ variable }}
. Template variables are replaced by the value of the variable. Dot notation can be used to access attributes of an object, keys of dictionaries, and items in lists or tuples: {{ my_object.attribute }}
, {{ my_dict.key }}
, and {{ my_list.3 }}
.
Filters. Variables can be modified by filters which look like this: {{ variable|filter:argument }}
. Filters modify the value of the variable output such as formatting dates, formatting numbers, changing the letter case, or concatenating multiple variables.
Tags. Tags use curly-brace-percent-sign syntax like this: {% tag %}
. Tags perform many different functions including creating text, controlling flow, or loading external information to be used in the app. Some commonly used tags include for
, if
, block
, and extends
.
Blocks. The block tags in the Tethys templates are used to override the content in the different areas of the app base template. For example, any HTML written inside the app_content
block will render in the app content area of the app.
For a better explanation of the Django Template Language and the blocks available in Tethys apps see the App Templating API.
5. Controllers
Basic controllers consist of a Python function that takes a request
object as an argument. The request
object contains all the information about the incoming request including any data being passed to the server, information about the user that is logged in, and the HTTP headers. Each controller function is associated with one view or template. Any variable assigned to the context
variable in a controller becomes a variable on the template specified in the render
function.
Open
controllers.py
define thedam_inventory_map
andadd_dam_button
gizmos in your home controller:from django.shortcuts import render from tethys_sdk.gizmos import MapView, Button from tethys_sdk.routing import controller @controller def home(request): """ Controller for the app home page. """ dam_inventory_map = MapView( height='100%', width='100%', layers=[], basemap=['OpenStreetMap'], ) add_dam_button = Button( display_text='Add Dam', name='add-dam-button', icon='plus-square', style='success' ) context = { 'dam_inventory_map': dam_inventory_map, 'add_dam_button': add_dam_button } return render(request, 'dam_inventory/home.html', context)
Save your changes to
controllers.py
andhome.html
and refresh the page to view the map.
Tip
Gizmos: The home.html
template used a Tethys template tag, gizmo
, to insert a map and a button with only one line of code: {% gizmo dam_inventory_map %}
. Gizmo tags require one argument, an object that defines the options for the gizmo. These gizmo options must be defined in the controller for that view. In the example above we define the options objects for the two gizmos on the home.html
template and pass them to the template through the context dictionary.
For more details on the Map View or Button Gizmos see: Map View and Button and Button Group For more information about Gizmos in general see the Template Gizmos API.
6. Custom Styles
It would look nicer if the map gizmo filled the entire app content area. To do this, we will need to add custom CSS or style rules to remove the padding around the inner-app-content
area.
Create a new file
/public/css/map.css
and add the following contents:#inner-app-content { padding: 0; } #app-content, #inner-app-content, #map_view_outer_container { height: 100%; }
Load the styles on the
/templates/dam_inventory/home.html
template by adding a link to thepublic/css/map.css
to it. To do this addstatic
to the load statement at the top of the template and add thestyles
block to the end of the file:{% load tethys_gizmos static %} ... {% block styles %} {{ block.super }} <link href="{% static 'dam_inventory/css/map.css' %}" rel="stylesheet"/> {% endblock %}
Save your changes to
map.css
andhome.html
and refresh the page to view the changes. The map should fill the content area now. Notice how the map dynamically resizes if the screen size changes.
Important
Don't forget the {{ block.super }}
! The {{ block.super }}
statement loads all previously loaded styles in this block. If you forget the {{ block.super }}
, it will result in a broken page with no styles applied.
7. Create a New Page
Creating a new page in your app consists of three steps: (1) create a new template, (2) add a new controller to controllers.py
, and (3) define the routing using the controller
decorator.
Create a new file
/templates/dam_inventory/add_dam.html
and add the following contents:{% extends "dam_inventory/base.html" %}
This is the simplest template you can create in a Tethys app, which amounts to a blank Tethys app page. You must still extend the
base.html
to retain the styling of an app page.Create a new controller function called
add_dam
at the bottom of thecontrollers.py
:@controller(url='dams/add') def add_dam(request): """ Controller for the Add Dam page. """ context = {} return render(request, 'dam_inventory/add_dam.html', context)
This is the most basic controller function you can write: a function that accepts an argument called
request
and a return value that is the result of therender
function. Therender
function renders the Django template into valid HTML using therequest
andcontext
provided.Notice the use of the
url
argument in thecontroller
decorator. Thecontroller
decorator creates a route that maps a URL to this controller function. The default URL that would have been generated without this argument would have been'add-dam'
. Theurl
argument is used to provide a custom URL for a controller. URLs are defined relative to the root URL of the app. The full URL for theadd_dam
controller as shown above is'/apps/dam-inventory/dams/add/'
. Also note that the name of the route created by thecontroller
decorator is, by default, the same as the function name (add_dam
). The name of the route will be important when we need to reference it in a template.At this point you should be able to access the new page by entering its URL (http://localhost:8000/apps/dam-inventory/dams/add/) into the address bar of your browser. It is not a very exciting page, because it is blank.
Tip
New Page Pattern: Adding new pages is an exercise of the Model View Controller pattern. Generally, the steps are:
Modify the model as necessary to support the data for the new page
Create a new HTML template
Create a new controller function
8. Link to New Page
Finally, you can also link to the page from another page using a button.
Modify the
add_dam_button
on the Home page to link to the newly created page (don't forget the import):from django.shortcuts import reverse ... @controller def home(request): ... add_dam_button = Button( display_text='Add Dam', name='add-dam-button', icon='plus-square', style='success', href=reverse('dam_inventory:add_dam') )
9. Build Out New Page
Modify the
template/dam_inventory/add_dam.html
with a title in the app content area and addAdd
andCancel
buttons to the app actions area:{% extends "dam_inventory/base.html" %} {% load tethys_gizmos %} {% block app_content %} <h1>Add Dam</h1> {% endblock %} {% block app_actions %} {% gizmo cancel_button %} {% gizmo add_button %} {% endblock %}
Define the options for the
Add
andCancel
button gizmos in theadd_dam
controller incontrollers.py
. Also add the variables to the context so they are available to the template:@controller(url='dams/add') def add_dam(request): """ Controller for the Add Dam page. """ add_button = Button( display_text='Add', name='add-button', icon='plus-square', style='success' ) cancel_button = Button( display_text='Cancel', name='cancel-button', href=reverse('dam_inventory:home') ) context = { 'add_button': add_button, 'cancel_button': cancel_button, } return render(request, 'dam_inventory/add_dam.html', context)
11. Solution
This concludes the Beginner Tutorial. You can view the solution on GitHub at https://github.com/tethysplatform/tethysapp-dam_inventory or clone it as follows:
git clone https://github.com/tethysplatform/tethysapp-dam_inventory.git cd tethysapp-dam_inventory git checkout -b beginner-solution beginner-4.2