Migrating Apps from Tethys 3 to 4
Last Updated: December 2022
This guide describes how to migrate Tethys 3 apps to work in Tethys 4. There are several "breaking" changes that were introduced in Tethys 4 that may cause apps developed in Tethys 3 to not load or function properly. Use the tips below to help you make the changes necessary for the app to function properly in Tethys 4.
Note
To migrate an app developed for Tethys 2 to Tethys 4, you will need to first complete the steps found in the Migrating Apps from Tethys 2 to 3 guide.
Index Controller
The index
property of the app class is used to tell Tethys which controller should be used for the home page of the app. In Tethys 4, the app namespace portion (the part before the :
) should be dropped.
For example, a Tethys app with an index
property looking like this:
class MyFirstApp(TethysAppBase):
index = "my_first_app:home"
should be changed to this:
class MyFirstApp(TethysAppBase):
index = "home"
Controller Decorators
The url_maps()
method is being deprecated in favor of the simpler controller
decorator method introduced in Tethys 4. The url_maps
method is temporarily avilable in Tethys 4 to allow for easier app migration, but support for the ``url_maps`` method will be dropped in Tethys 4.1.0.
It is strongly recommended to migrate apps to use the new controller
decorator approach and remove the url_maps()
method from the app.py
. If you still wish to declare UrlMaps
in the app.py
, use the new register_url_maps()
method and then remove the url_maps()
method (see: Register URL Maps Method). The console will display warnings for apps still using the url_maps
method in Tethys 4 to encourage migration to one of the new methods described above as soon as possible. Don't wait for Tethys 4.1 to migrate.
Use the following tips to help you migrate:
Review the Routing API documentation to become familiar with the
controller
decorator.If your app has a lot of controllers, use the
url_maps()
inapp.py
to make a list of them. There should be one controller function or class for eachUrlMap
listed.Add the
controller
decorator to each controller function or class in your app.If the default URL or name generated by the
controller
decorator don't match what is set in theUrlMap
, override it by setting theurl
andname
arguments of the controller decorator.If your controller uses any other decorators, remove them and use the appropriate arguments in the
controller
decorator instead.Remove the
url_maps()
method from theapp.py
.
Note
Tethys 4 also introduces the consumer
and handler
decorators that function equivalently for consumers and handler functions. See the Routing API documentation for more details.
Search Path
Tethys will only search for the controller
, consumer
, and handler
decorators in modules named controllers.py
or consumers.py
or any module located in packages named controllers
and consumers
. If your app has controllers located in modules with different names than these defaults, the recommended migration is to move the modules into a package named either controllers
or consumers
.
However, you may also use the controller_modules
property of the app class to define addtiional search locations. For example:
class MyFirstApp(TethysAppBase):
...
controller_modules = [
'custom_controllers', # For a module named custom_controller.py in the same directory as app.py
'rest', # For a package named "rest" in the same directory as app.py containing modules with controllers
]
Workspaces
It is recommended that you use the app_workspace
and user_workspace
arguments of the controller
decorator to acquire workspaces in Tethys 4.
For example, the following controllers:
from tethys_sdk.workspaces import app_workspace
from .app import MyFirstApp as app
def controller_a(request):
"""Gets user workspace from old app class method."""
user_workspace = app.get_user_workspace(request.user)
uw_path = user_workspace.path
...
@app_workspace
def controller_b(request, app_workspace):
"""Gets app workspace from the app_workspace decorator."""
aw_path = app_workspace.path
...
should be refactored to use the controller
decorator as follows:
from tethys_sdk.routing import controller
@controller(user_workspace=True)
def controller_a(request, user_workspace):
"""Gets user workspace from the controller decorator."""
uw_path = user_workspace.path
...
@controller(app_workspace=True)
def controller_b(request, app_workspace):
"""Gets app workspace from the controller decorator."""
aw_path = app_workspace.path
...
Note
In rare cases when the controller
decorator cannot be used to acquire workspaces, you may use the get_app_workspace()
and get_user_workspace()
methods of the app class. These methods are no longer deprecated. However, they will raise an exception if the quotas feature is enabled and the user or app workspace is out of storage space. The controller
decorator automatically handles these exceptions, but when using the get_app_workspace()
and get_user_workspace()
directly, you will need to handle those exceptions.
Templates
In Tethys 4, the staticfiles
template library needs to be changed to static
:
For example, the following load
statement:
{% load staticfiles %}
needs to be changed to this:
{% load static %}
Theme and Styles
The frontend CSS framework, Bootstrap, was upgraded from version 3 to version 5 in Tethys Platform 4. As a result, any Boostrap components that are used in templates will need to be updated to use the Bootstrap 5 syntax so they function and look as expected. This section describes how to update the most common Bootstrap components that are used in Tethys Apps.
Note
The app base templates and Template Gizmos have all been updated to use Bootstrap 5. You should only need to upgrade Bootstrap code that is contained in your app. The Gizmos that are used by your app will be automatically upgraded.
Bootstrap Icons
The glyphicons that were included in Bootstrap 3 were moved to a separate library called Bootstrap Icons. The Bootstrap Icons use a different syntax than glyphicons, so any glyphicons that are used in your app will not show up in Tethys Platform 4.
However, Tethys Platform 4 includes the Boostrap Icons library in the base template for apps, so the only change that you should need to make is to update the icon to an equivalent Bootstrap Icon:
<i class="bi bi-home"></i>
The Bootstrap Icons library has many more icons than the Bootstrap 3 glyphicon library, however the names of many icons have changed. For example glyphicon-pencil
featured a filled pencil icon, but bi-pencil
is an outlined pencil icon. To use the equivalent Bootstrap Icon, you will need to use the bi-pencil-fill
icon. Fortunately, the Bootstrap Icons website has an excellent search capability that makes it easy to find equivalent icons.
For example, if your app had the following glyphicons:
<span class="glyphicon glyhpicon-home" aria-hidden="true"></span>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
<span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
you could update them to use the following equivalent Bootstrap Icons:
<i class="bi bi-house-door-fill"></i>
<i class="bi bi-trash"></i>
<i class="bi bi-save"></i>
Data Attributes
All Boostrap related data attributes on HTML elements now include a bs
namespace. For example, data-target
needs to be changed to data-bs-target
. Use the following tips to help you migrate data attributes appropriately:
Perform a project-wide search on your app source code for
data-
to find instances of data attributes.Review the tips below for Tooltips, Dropdowns, and Modals.
Review the Bootstrap 5 documentation for details about changes to other components that your app uses that are not listed below.
Tooltips
Bootstrap Tooltip components have the following data attributes that need to be updated:
data-toggle
:data-bs-toggle
data-placement
:data-bs-placement
For example, this button with an old-style tooltip:
<button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Tooltip on bottom">
Tooltip on bottom
</button>
needs to be updated to this:
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="bottom" title="Tooltip on bottom">
Tooltip on bottom
</button>
Dropdowns
Bootstrap Dropdown components have the following data attributes that need to be updated:
data-toggle
:data-bs-toggle
In addition, the <span> element with class carot should be removed.
For example, this old-style dropdown:
<div class="dropdown">
<button id="dLabel" type="button" data-toggle="dropdown" aria-expanded="false">
Dropdown trigger
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dLabel">
...
</ul>
</div>
needs to be updated to this:
<div class="dropdown">
<button id="dLabel" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown trigger
</button>
<ul class="dropdown-menu" aria-labelledby="dLabel">
...
</ul>
</div>
Modals
Bootstrap Modal components have the following data attributes that need to be updated:
data-dismiss
:data-bs-dismiss
data-toggle
:data-bs-toggle
data-target
:data-bs-target
In addition the class of the close button should be changed from close
to btn-close
and the ×
should be removed. The modal title and close button also need to be reordered (title first, button second).
For example, the following old-style modal:
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#exampleModal">
Launch demo modal
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
needs to be updated to this:
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
Launch demo modal
</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
References in Gizmos
Although Gizmos have been updated to use Bootstrap 5, some arguments passed to them may include Bootstrap 3 values. For example, the Button
and TextInput
Gizmos have arguments that accept the names of icons to be displayed on them that need to be updated to Boostrap Icons values. Other arguments to check are attributes
, classes
, and style
that may have old Bootstrap 3 values that need to be updated to use Bootstrap 5 values. Use the following tips to help you migrate:
Do a project-wide search for
"glphyicon"
and update any icon arguments for Gizmos to the name of equivalent Bootstrap Icons (without thebi
).Check for old Bootstrap data attributes in the
attributes
arguments of Gizmos (see: Data Attributes).Check for old Bootstrap classes in the
classes
arguments of Gizmos (e.g.btn-default
).Check for old Bootstrap values in the
style
arguments of some Gizmos (e.g.:default
).
For example, to update this Button Gizmo to have the equivalent style and icon:
add_button = Button(
display_text='Add',
icon='glyphicon glyphicon-plus',
style='success',
)
change the value of icon
(without the bi
portion) of the Bootstrap Icon:
add_button = Button(
display_text='Add',
icon='plus-circle-fill',
style='success',
)
Gizmos
Static Dependencies
Most of the static dependencies of Gizmos, the CSS and JavaScript libraries they depend on, have been updated. As a result, some funtionality may have changed. If your app interacts with Gizmos using JavaScript, you may need to make some minor changes to the code to work with the new versions. This will be enirely dependent on what functionality you use.
For example, the MapView gizmo depends on the OpenLayers JavaScript library. If your app uses the getMap()
JavaScript API method of the MapView Gizmo and then manipulates the map object (add layers, change map settings, etc), it is likely using the OpenLayers API to do so. If your app doesn't seem to be functioning correctly, check the developer console in your web browser for errors.
Note
This suggestion only applies to your custom JavaScript code. The Gizmos have been updated to work with the new versions of the libraries. If you use Gizmos and don't use JavaScript to manipulate them, then this likely doesn't apply to you.
MapView Gizmo
The basemap
argument of the MapView
Gizmo needs to be specified as a list, even if only one basemap is listed. For example a MapView
Gizmo specifing only 'OpenStreetMap'
basemap like this:
map_view_options = MapView(
height='500px',
width='100%',
layers=[...],
view=view_options,
basemap='OpenStreetMap',
)
should be updated to this:
map_view_options = MapView(
height='500px',
width='100%',
layers=[...],
view=view_options,
basemap=['OpenStreetMap'],
)
WebSockets
The URLs Tethys generates for WebSockets now begin with /apps/
to be consistent with the other URLs generated by Tethys. Update any JavaScript that uses these URLs to connect to WebSockets.
For example, the following code in a Tethys 3 app:
let websocket = new WebSocket('ws://' + window.location.host + '/dam-inventory/dams/notifications/ws/');
needs to be updated to this in Tethys 4:
let websocket = new WebSocket('ws://' + window.location.host + '/apps/dam-inventory/dams/notifications/ws/');
Schedulers
In Tethys 4, job Schedulers should be assigned to apps using Scheduler app settings. If your app uses job schedulers:
Create a Scheduler app setting by defining the
scheduler_settings()
method on the app class. See Scheduler Settings.Define a Scheduler Service and assign it to the app setting. See Scheduler Service.
Use the
get_scheduler()
app class method to get the Scheduler from the setting. See Using Scheduler Settings.
Other Resources
It is not possible to anticipate every migration step that will be needed. The suggestions are for the cases that will be most commonly encountered. The following resources may provide additional guidance for migrating your app(s).
Tethys is powered by Django. If your app makes uses of Django features directly, you may need to perform additional migration steps. See Upgrading Django to a newer version and Django 3.2 Release Notes.
Apps are styled using Bootstrap, a frontend CSS framework. If your app uses Bootstrap components other than those described above, use these links to help you migrate: Migrating to v4 | Bootstrap and Migrating to v5 | Bootstrap.
Upgrade Older Apps
To upgrade apps that are several versions behind, complete the app migration process for each version in order. For example, to upgrade an app developed in Tethys 2 to Tethys 4, first complete the "2 to 3" migration instructions and then complete the "3 to 4" instructions.