CursorBackend
The cursor backend connects your models to a MySQL or MariaDB database.
Overview
Installing Dependencies
clearskies uses PyMySQL to manage the database connection and make queries. This is not installed by default, but is a named extra that you can install when needed via:
pip install clear-skies[mysql]
Connecting to your server
The backend fetches its cursor from the dependency injection container under the name cursor (configurable via the cursor_dependency_name argument). The value must be a clearskies cursor, i.e. an instance of clearskies.cursors.Cursor, and not a raw DB-API cursor or connection. By default, cursor is clearskies.cursors.from_environment.Mysql(), which reads its connection details from the environment (environment variables or a .env file):
| Name | Default | Value |
|---|---|---|
| DATABASE_HOST | The hostname where the database can be found | |
| DATABASE_USERNAME | The username to connect as | |
| DATABASE_PASSWORD | The password to connect with | |
| DATABASE_NAME | The name of the database to use | |
| DATABASE_PORT | 3306 | The network port to connect to |
| DATABASE_CERT_PATH | Path to a certificate to use: enables SSL over the connection | |
| DATABASE_AUTOCOMMIT | Enables autocommit when set to a non-empty value | |
| DATABASE_CONNECT_TIMEOUT | 2 | Connection timeout in seconds |
The names of these environment variables can be changed via the *_environment_key arguments of clearskies.cursors.from_environment.Mysql. clearskies.cursors.from_environment.Postgresql (same variables) and clearskies.cursors.from_environment.Sqlite (DATABASE_NAME only) are also available.
To use a different cursor, bind one of the classes from clearskies.cursors to cursor. For example, to connect to a PostgreSQL server (requires psycopg to be installed) with explicit settings:
import clearskies
wsgi = clearskies.contexts.WsgiRef(
some_application,
bindings={
"cursor": clearskies.cursors.Postgresql(
hostname="db.example.com",
username="example-user",
password="<YOUR_PASSWORD>",
database="appdb",
),
},
)
clearskies.cursors.Mysql and clearskies.cursors.Sqlite work the same way. The MySQL and PostgreSQL cursors also accept a port_forwarding argument (see clearskies.cursors.port_forwarding). If different models need different databases, bind each cursor under its own name and point the backend at it:
class Report(clearskies.Model):
backend = clearskies.backends.CursorBackend(cursor_dependency_name="reporting_cursor")
cli = clearskies.contexts.Cli(
some_application,
bindings={"reporting_cursor": clearskies.cursors.Sqlite(database="reports.db")},
)
The cursors don’t read from a secret manager directly. To pull credentials from one, provide the cursor from an AdditionalConfig class, which can have the secrets dependency injected:
import clearskies
class DatabaseConfig(clearskies.di.AdditionalConfig):
def provide_cursor(self, secrets):
return clearskies.cursors.Mysql(
hostname="db.example.com",
username="example-user",
password=secrets.get("/path/to/database/password"),
database="appdb",
)
wsgi = clearskies.contexts.WsgiRef(
some_application,
additional_configs=[DatabaseConfig()],
bindings={
"secrets": clearskies.secrets.Akeyless(access_id="p-abc123", access_type="aws_iam"),
},
)
Connecting models to tables
The table name for your model comes from calling the destination_name class method of the model class. By default, this takes the class name, converts it to snake case, and then pluralizes it. So, if you have a model class named UserPreference then the cursor backend will look for a table called user_preferences. If this isn’t what you want, then you can simply override destination_name to return whatever table you want:
class UserPreference(clearskies.Model):
@classmethod
def destination_name(cls):
return "some_other_table_name"
Additionally, the cursor backend accepts an argument called table_prefix which, if provided, will be prefixed to your table name. Finally, you can declare a dependency called global_table_prefix which will automatically be added to every table name. In the following example, the table name will be user_configuration_preferences due to:
- The
destination_namemethod sets the table name topreferences - The
table_prefixargument to the CursorBackend constructor adds a prefix ofconfiguration_ - The
global_table_prefixbinding sets a prefix ofuser_, wihch goes before everything else.
import clearskies
class UserPreference(clearskies.Model):
id_column_name = "id"
backend = clearskies.backends.CursorBackend(table_prefix="configuration_")
id = clearskies.columns.Uuid()
@classmethod
def destination_name(cls):
return "preferences"
cli = clearskies.contexts.Cli(
clearskies.endpoints.Callable(
lambda user_preferences: user_preferences.create(no_data=True).id,
),
classes=[UserPreference],
bindings={
"global_table_prefix": "user_",
},
)
cursor_dependency_name
Optional
table_prefix
Optional
can_create
Optional
Whether creating new records is allowed for this backend.
When set to False, any attempt to create a record will raise a ValueError. This can be set as a class attribute or passed to the constructor.
can_update
Optional
Whether updating existing records is allowed for this backend.
When set to False, any attempt to update a record will raise a ValueError. This can be set as a class attribute or passed to the constructor.
can_delete
Optional
Whether deleting records is allowed for this backend.
When set to False, any attempt to delete a record will raise a ValueError. This can be set as a class attribute or passed to the constructor.
can_query
Optional
Whether querying/reading records is allowed for this backend.
When set to False, any attempt to query records (via iteration, count, find, etc.) will raise a ValueError. This can be set as a class attribute or passed to the constructor.