mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Fix blacken-docs errors and warnings, refs #1718
This commit is contained in:
parent
36573638b0
commit
92b26673d8
4 changed files with 289 additions and 141 deletions
|
|
@ -44,9 +44,12 @@ aggregates and collations. For example:
|
|||
from datasette import hookimpl
|
||||
import random
|
||||
|
||||
|
||||
@hookimpl
|
||||
def prepare_connection(conn):
|
||||
conn.create_function('random_integer', 2, random.randint)
|
||||
conn.create_function(
|
||||
"random_integer", 2, random.randint
|
||||
)
|
||||
|
||||
This registers a SQL function called ``random_integer`` which takes two
|
||||
arguments and can be called like this::
|
||||
|
|
@ -72,9 +75,10 @@ example:
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def prepare_jinja2_environment(env):
|
||||
env.filters['uppercase'] = lambda u: u.upper()
|
||||
env.filters["uppercase"] = lambda u: u.upper()
|
||||
|
||||
You can now use this filter in your custom templates like so::
|
||||
|
||||
|
|
@ -127,9 +131,7 @@ Here's an example plugin that adds a ``"user_agent"`` variable to the template c
|
|||
|
||||
@hookimpl
|
||||
def extra_template_vars(request):
|
||||
return {
|
||||
"user_agent": request.headers.get("user-agent")
|
||||
}
|
||||
return {"user_agent": request.headers.get("user-agent")}
|
||||
|
||||
This example returns an awaitable function which adds a list of ``hidden_table_names`` to the context:
|
||||
|
||||
|
|
@ -140,9 +142,12 @@ This example returns an awaitable function which adds a list of ``hidden_table_n
|
|||
async def hidden_table_names():
|
||||
if database:
|
||||
db = datasette.databases[database]
|
||||
return {"hidden_table_names": await db.hidden_table_names()}
|
||||
return {
|
||||
"hidden_table_names": await db.hidden_table_names()
|
||||
}
|
||||
else:
|
||||
return {}
|
||||
|
||||
return hidden_table_names
|
||||
|
||||
And here's an example which adds a ``sql_first(sql_query)`` function which executes a SQL statement and returns the first column of the first row of results:
|
||||
|
|
@ -152,8 +157,15 @@ And here's an example which adds a ``sql_first(sql_query)`` function which execu
|
|||
@hookimpl
|
||||
def extra_template_vars(datasette, database):
|
||||
async def sql_first(sql, dbname=None):
|
||||
dbname = dbname or database or next(iter(datasette.databases.keys()))
|
||||
return (await datasette.execute(dbname, sql)).rows[0][0]
|
||||
dbname = (
|
||||
dbname
|
||||
or database
|
||||
or next(iter(datasette.databases.keys()))
|
||||
)
|
||||
return (await datasette.execute(dbname, sql)).rows[
|
||||
0
|
||||
][0]
|
||||
|
||||
return {"sql_first": sql_first}
|
||||
|
||||
You can then use the new function in a template like so::
|
||||
|
|
@ -178,6 +190,7 @@ This can be a list of URLs:
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def extra_css_urls():
|
||||
return [
|
||||
|
|
@ -191,10 +204,12 @@ Or a list of dictionaries defining both a URL and an
|
|||
|
||||
@hookimpl
|
||||
def extra_css_urls():
|
||||
return [{
|
||||
"url": "https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css",
|
||||
"sri": "sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4",
|
||||
}]
|
||||
return [
|
||||
{
|
||||
"url": "https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css",
|
||||
"sri": "sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4",
|
||||
}
|
||||
]
|
||||
|
||||
This function can also return an awaitable function, useful if it needs to run any async code:
|
||||
|
||||
|
|
@ -204,7 +219,9 @@ This function can also return an awaitable function, useful if it needs to run a
|
|||
def extra_css_urls(datasette):
|
||||
async def inner():
|
||||
db = datasette.get_database()
|
||||
results = await db.execute("select url from css_files")
|
||||
results = await db.execute(
|
||||
"select url from css_files"
|
||||
)
|
||||
return [r[0] for r in results]
|
||||
|
||||
return inner
|
||||
|
|
@ -225,12 +242,15 @@ return a list of URLs, a list of dictionaries or an awaitable function that retu
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def extra_js_urls():
|
||||
return [{
|
||||
"url": "https://code.jquery.com/jquery-3.3.1.slim.min.js",
|
||||
"sri": "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",
|
||||
}]
|
||||
return [
|
||||
{
|
||||
"url": "https://code.jquery.com/jquery-3.3.1.slim.min.js",
|
||||
"sri": "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",
|
||||
}
|
||||
]
|
||||
|
||||
You can also return URLs to files from your plugin's ``static/`` directory, if
|
||||
you have one:
|
||||
|
|
@ -239,9 +259,7 @@ you have one:
|
|||
|
||||
@hookimpl
|
||||
def extra_js_urls():
|
||||
return [
|
||||
"/-/static-plugins/your-plugin/app.js"
|
||||
]
|
||||
return ["/-/static-plugins/your-plugin/app.js"]
|
||||
|
||||
Note that `your-plugin` here should be the hyphenated plugin name - the name that is displayed in the list on the `/-/plugins` debug page.
|
||||
|
||||
|
|
@ -251,9 +269,11 @@ If your code uses `JavaScript modules <https://developer.mozilla.org/en-US/docs/
|
|||
|
||||
@hookimpl
|
||||
def extra_js_urls():
|
||||
return [{
|
||||
"url": "/-/static-plugins/your-plugin/app.js",
|
||||
"module": True
|
||||
return [
|
||||
{
|
||||
"url": "/-/static-plugins/your-plugin/app.js",
|
||||
"module": True,
|
||||
}
|
||||
]
|
||||
|
||||
Examples: `datasette-cluster-map <https://datasette.io/plugins/datasette-cluster-map>`_, `datasette-vega <https://datasette.io/plugins/datasette-vega>`_
|
||||
|
|
@ -281,7 +301,7 @@ Use a dictionary if you want to specify that the code should be placed in a ``<s
|
|||
def extra_body_script():
|
||||
return {
|
||||
"module": True,
|
||||
"script": "console.log('Your JavaScript goes here...')"
|
||||
"script": "console.log('Your JavaScript goes here...')",
|
||||
}
|
||||
|
||||
This will add the following to the end of your page:
|
||||
|
|
@ -311,7 +331,9 @@ Let's say you want to build a plugin that adds a ``datasette publish my_hosting_
|
|||
.. code-block:: python
|
||||
|
||||
from datasette import hookimpl
|
||||
from datasette.publish.common import add_common_publish_arguments_and_options
|
||||
from datasette.publish.common import (
|
||||
add_common_publish_arguments_and_options,
|
||||
)
|
||||
import click
|
||||
|
||||
|
||||
|
|
@ -345,7 +367,7 @@ Let's say you want to build a plugin that adds a ``datasette publish my_hosting_
|
|||
about_url,
|
||||
api_key,
|
||||
):
|
||||
# Your implementation goes here
|
||||
...
|
||||
|
||||
Examples: `datasette-publish-fly <https://datasette.io/plugins/datasette-publish-fly>`_, `datasette-publish-vercel <https://datasette.io/plugins/datasette-publish-vercel>`_
|
||||
|
||||
|
|
@ -400,7 +422,9 @@ If the value matches that pattern, the plugin returns an HTML link element:
|
|||
if not isinstance(value, str):
|
||||
return None
|
||||
stripped = value.strip()
|
||||
if not stripped.startswith("{") and stripped.endswith("}"):
|
||||
if not stripped.startswith("{") and stripped.endswith(
|
||||
"}"
|
||||
):
|
||||
return None
|
||||
try:
|
||||
data = json.loads(value)
|
||||
|
|
@ -412,14 +436,18 @@ If the value matches that pattern, the plugin returns an HTML link element:
|
|||
return None
|
||||
href = data["href"]
|
||||
if not (
|
||||
href.startswith("/") or href.startswith("http://")
|
||||
href.startswith("/")
|
||||
or href.startswith("http://")
|
||||
or href.startswith("https://")
|
||||
):
|
||||
return None
|
||||
return markupsafe.Markup('<a href="{href}">{label}</a>'.format(
|
||||
href=markupsafe.escape(data["href"]),
|
||||
label=markupsafe.escape(data["label"] or "") or " "
|
||||
))
|
||||
return markupsafe.Markup(
|
||||
'<a href="{href}">{label}</a>'.format(
|
||||
href=markupsafe.escape(data["href"]),
|
||||
label=markupsafe.escape(data["label"] or "")
|
||||
or " ",
|
||||
)
|
||||
)
|
||||
|
||||
Examples: `datasette-render-binary <https://datasette.io/plugins/datasette-render-binary>`_, `datasette-render-markdown <https://datasette.io/plugins/datasette-render-markdown>`__, `datasette-json-html <https://datasette.io/plugins/datasette-json-html>`__
|
||||
|
||||
|
|
@ -516,7 +544,7 @@ Here is a more complex example:
|
|||
return Response(
|
||||
"\n".join(lines),
|
||||
content_type="text/plain; charset=utf-8",
|
||||
headers={"x-sqlite-version": result.first()[0]}
|
||||
headers={"x-sqlite-version": result.first()[0]},
|
||||
)
|
||||
|
||||
And here is an example ``can_render`` function which returns ``True`` only if the query results contain the columns ``atom_id``, ``atom_title`` and ``atom_updated``:
|
||||
|
|
@ -524,7 +552,11 @@ And here is an example ``can_render`` function which returns ``True`` only if th
|
|||
.. code-block:: python
|
||||
|
||||
def can_render_demo(columns):
|
||||
return {"atom_id", "atom_title", "atom_updated"}.issubset(columns)
|
||||
return {
|
||||
"atom_id",
|
||||
"atom_title",
|
||||
"atom_updated",
|
||||
}.issubset(columns)
|
||||
|
||||
Examples: `datasette-atom <https://datasette.io/plugins/datasette-atom>`_, `datasette-ics <https://datasette.io/plugins/datasette-ics>`_, `datasette-geojson <https://datasette.io/plugins/datasette-geojson>`__
|
||||
|
||||
|
|
@ -548,16 +580,14 @@ Return a list of ``(regex, view_function)`` pairs, something like this:
|
|||
|
||||
async def hello_from(request):
|
||||
name = request.url_vars["name"]
|
||||
return Response.html("Hello from {}".format(
|
||||
html.escape(name)
|
||||
))
|
||||
return Response.html(
|
||||
"Hello from {}".format(html.escape(name))
|
||||
)
|
||||
|
||||
|
||||
@hookimpl
|
||||
def register_routes():
|
||||
return [
|
||||
(r"^/hello-from/(?P<name>.*)$", hello_from)
|
||||
]
|
||||
return [(r"^/hello-from/(?P<name>.*)$", hello_from)]
|
||||
|
||||
The view functions can take a number of different optional arguments. The corresponding argument will be passed to your function depending on its named parameters - a form of dependency injection.
|
||||
|
||||
|
|
@ -606,10 +636,13 @@ This example registers a new ``datasette verify file1.db file2.db`` command that
|
|||
import click
|
||||
import sqlite3
|
||||
|
||||
|
||||
@hookimpl
|
||||
def register_commands(cli):
|
||||
@cli.command()
|
||||
@click.argument("files", type=click.Path(exists=True), nargs=-1)
|
||||
@click.argument(
|
||||
"files", type=click.Path(exists=True), nargs=-1
|
||||
)
|
||||
def verify(files):
|
||||
"Verify that files can be opened by Datasette"
|
||||
for file in files:
|
||||
|
|
@ -617,7 +650,9 @@ This example registers a new ``datasette verify file1.db file2.db`` command that
|
|||
try:
|
||||
conn.execute("select * from sqlite_master")
|
||||
except sqlite3.DatabaseError:
|
||||
raise click.ClickException("Invalid database: {}".format(file))
|
||||
raise click.ClickException(
|
||||
"Invalid database: {}".format(file)
|
||||
)
|
||||
|
||||
The new command can then be executed like so::
|
||||
|
||||
|
|
@ -656,15 +691,18 @@ Each Facet subclass implements a new type of facet operation. The class should l
|
|||
async def suggest(self):
|
||||
# Use self.sql and self.params to suggest some facets
|
||||
suggested_facets = []
|
||||
suggested_facets.append({
|
||||
"name": column, # Or other unique name
|
||||
# Construct the URL that will enable this facet:
|
||||
"toggle_url": self.ds.absolute_url(
|
||||
self.request, path_with_added_args(
|
||||
self.request, {"_facet": column}
|
||||
)
|
||||
),
|
||||
})
|
||||
suggested_facets.append(
|
||||
{
|
||||
"name": column, # Or other unique name
|
||||
# Construct the URL that will enable this facet:
|
||||
"toggle_url": self.ds.absolute_url(
|
||||
self.request,
|
||||
path_with_added_args(
|
||||
self.request, {"_facet": column}
|
||||
),
|
||||
),
|
||||
}
|
||||
)
|
||||
return suggested_facets
|
||||
|
||||
async def facet_results(self):
|
||||
|
|
@ -678,18 +716,25 @@ Each Facet subclass implements a new type of facet operation. The class should l
|
|||
try:
|
||||
facet_results_values = []
|
||||
# More calculations...
|
||||
facet_results_values.append({
|
||||
"value": value,
|
||||
"label": label,
|
||||
"count": count,
|
||||
"toggle_url": self.ds.absolute_url(self.request, toggle_path),
|
||||
"selected": selected,
|
||||
})
|
||||
facet_results.append({
|
||||
"name": column,
|
||||
"results": facet_results_values,
|
||||
"truncated": len(facet_rows_results) > facet_size,
|
||||
})
|
||||
facet_results_values.append(
|
||||
{
|
||||
"value": value,
|
||||
"label": label,
|
||||
"count": count,
|
||||
"toggle_url": self.ds.absolute_url(
|
||||
self.request, toggle_path
|
||||
),
|
||||
"selected": selected,
|
||||
}
|
||||
)
|
||||
facet_results.append(
|
||||
{
|
||||
"name": column,
|
||||
"results": facet_results_values,
|
||||
"truncated": len(facet_rows_results)
|
||||
> facet_size,
|
||||
}
|
||||
)
|
||||
except QueryInterrupted:
|
||||
facets_timed_out.append(column)
|
||||
|
||||
|
|
@ -728,21 +773,33 @@ This example plugin adds a ``x-databases`` HTTP header listing the currently att
|
|||
def asgi_wrapper(datasette):
|
||||
def wrap_with_databases_header(app):
|
||||
@wraps(app)
|
||||
async def add_x_databases_header(scope, receive, send):
|
||||
async def add_x_databases_header(
|
||||
scope, receive, send
|
||||
):
|
||||
async def wrapped_send(event):
|
||||
if event["type"] == "http.response.start":
|
||||
original_headers = event.get("headers") or []
|
||||
original_headers = (
|
||||
event.get("headers") or []
|
||||
)
|
||||
event = {
|
||||
"type": event["type"],
|
||||
"status": event["status"],
|
||||
"headers": original_headers + [
|
||||
[b"x-databases",
|
||||
", ".join(datasette.databases.keys()).encode("utf-8")]
|
||||
"headers": original_headers
|
||||
+ [
|
||||
[
|
||||
b"x-databases",
|
||||
", ".join(
|
||||
datasette.databases.keys()
|
||||
).encode("utf-8"),
|
||||
]
|
||||
],
|
||||
}
|
||||
await send(event)
|
||||
|
||||
await app(scope, receive, wrapped_send)
|
||||
|
||||
return add_x_databases_header
|
||||
|
||||
return wrap_with_databases_header
|
||||
|
||||
Examples: `datasette-cors <https://datasette.io/plugins/datasette-cors>`__, `datasette-pyinstrument <https://datasette.io/plugins/datasette-pyinstrument>`__
|
||||
|
|
@ -759,7 +816,9 @@ This hook fires when the Datasette application server first starts up. You can i
|
|||
@hookimpl
|
||||
def startup(datasette):
|
||||
config = datasette.plugin_config("my-plugin") or {}
|
||||
assert "required-setting" in config, "my-plugin requires setting required-setting"
|
||||
assert (
|
||||
"required-setting" in config
|
||||
), "my-plugin requires setting required-setting"
|
||||
|
||||
Or you can return an async function which will be awaited on startup. Use this option if you need to make any database queries:
|
||||
|
||||
|
|
@ -770,9 +829,12 @@ Or you can return an async function which will be awaited on startup. Use this o
|
|||
async def inner():
|
||||
db = datasette.get_database()
|
||||
if "my_table" not in await db.table_names():
|
||||
await db.execute_write("""
|
||||
await db.execute_write(
|
||||
"""
|
||||
create table my_table (mycol text)
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
return inner
|
||||
|
||||
Potential use-cases:
|
||||
|
|
@ -815,6 +877,7 @@ Ues this hook to return a dictionary of additional :ref:`canned query <canned_qu
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def canned_queries(datasette, database):
|
||||
if database == "mydb":
|
||||
|
|
@ -830,15 +893,20 @@ The hook can alternatively return an awaitable function that returns a list. Her
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def canned_queries(datasette, database):
|
||||
async def inner():
|
||||
db = datasette.get_database(database)
|
||||
if await db.table_exists("saved_queries"):
|
||||
results = await db.execute("select name, sql from saved_queries")
|
||||
return {result["name"]: {
|
||||
"sql": result["sql"]
|
||||
} for result in results}
|
||||
results = await db.execute(
|
||||
"select name, sql from saved_queries"
|
||||
)
|
||||
return {
|
||||
result["name"]: {"sql": result["sql"]}
|
||||
for result in results
|
||||
}
|
||||
|
||||
return inner
|
||||
|
||||
The actor parameter can be used to include the currently authenticated actor in your decision. Here's an example that returns saved queries that were saved by that actor:
|
||||
|
|
@ -847,19 +915,23 @@ The actor parameter can be used to include the currently authenticated actor in
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def canned_queries(datasette, database, actor):
|
||||
async def inner():
|
||||
db = datasette.get_database(database)
|
||||
if actor is not None and await db.table_exists("saved_queries"):
|
||||
if actor is not None and await db.table_exists(
|
||||
"saved_queries"
|
||||
):
|
||||
results = await db.execute(
|
||||
"select name, sql from saved_queries where actor_id = :id", {
|
||||
"id": actor["id"]
|
||||
}
|
||||
"select name, sql from saved_queries where actor_id = :id",
|
||||
{"id": actor["id"]},
|
||||
)
|
||||
return {result["name"]: {
|
||||
"sql": result["sql"]
|
||||
} for result in results}
|
||||
return {
|
||||
result["name"]: {"sql": result["sql"]}
|
||||
for result in results
|
||||
}
|
||||
|
||||
return inner
|
||||
|
||||
Example: `datasette-saved-queries <https://datasette.io/plugins/datasette-saved-queries>`__
|
||||
|
|
@ -888,9 +960,12 @@ Here's an example that authenticates the actor based on an incoming API key:
|
|||
|
||||
SECRET_KEY = "this-is-a-secret"
|
||||
|
||||
|
||||
@hookimpl
|
||||
def actor_from_request(datasette, request):
|
||||
authorization = request.headers.get("authorization") or ""
|
||||
authorization = (
|
||||
request.headers.get("authorization") or ""
|
||||
)
|
||||
expected = "Bearer {}".format(SECRET_KEY)
|
||||
|
||||
if secrets.compare_digest(authorization, expected):
|
||||
|
|
@ -906,6 +981,7 @@ Instead of returning a dictionary, this function can return an awaitable functio
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def actor_from_request(datasette, request):
|
||||
async def inner():
|
||||
|
|
@ -914,7 +990,8 @@ Instead of returning a dictionary, this function can return an awaitable functio
|
|||
return None
|
||||
# Look up ?_token=xxx in sessions table
|
||||
result = await datasette.get_database().execute(
|
||||
"select count(*) from sessions where token = ?", [token]
|
||||
"select count(*) from sessions where token = ?",
|
||||
[token],
|
||||
)
|
||||
if result.first()[0]:
|
||||
return {"token": token}
|
||||
|
|
@ -952,7 +1029,7 @@ The hook should return an instance of ``datasette.filters.FilterArguments`` whic
|
|||
where_clauses=["id > :max_id"],
|
||||
params={"max_id": 5},
|
||||
human_descriptions=["max_id is greater than 5"],
|
||||
extra_context={}
|
||||
extra_context={},
|
||||
)
|
||||
|
||||
The arguments to the ``FilterArguments`` class constructor are as follows:
|
||||
|
|
@ -973,10 +1050,13 @@ This example plugin causes 0 results to be returned if ``?_nothing=1`` is added
|
|||
from datasette import hookimpl
|
||||
from datasette.filters import FilterArguments
|
||||
|
||||
|
||||
@hookimpl
|
||||
def filters_from_request(self, request):
|
||||
if request.args.get("_nothing"):
|
||||
return FilterArguments(["1 = 0"], human_descriptions=["NOTHING"])
|
||||
return FilterArguments(
|
||||
["1 = 0"], human_descriptions=["NOTHING"]
|
||||
)
|
||||
|
||||
Example: `datasette-leaflet-freedraw <https://datasette.io/plugins/datasette-leaflet-freedraw>`_
|
||||
|
||||
|
|
@ -1006,6 +1086,7 @@ Here's an example plugin which randomly selects if a permission should be allowe
|
|||
from datasette import hookimpl
|
||||
import random
|
||||
|
||||
|
||||
@hookimpl
|
||||
def permission_allowed(action):
|
||||
if action != "view-instance":
|
||||
|
|
@ -1024,11 +1105,16 @@ Here's an example that allows users to view the ``admin_log`` table only if thei
|
|||
async def inner():
|
||||
if action == "execute-sql" and resource == "staff":
|
||||
return False
|
||||
if action == "view-table" and resource == ("staff", "admin_log"):
|
||||
if action == "view-table" and resource == (
|
||||
"staff",
|
||||
"admin_log",
|
||||
):
|
||||
if not actor:
|
||||
return False
|
||||
user_id = actor["id"]
|
||||
return await datasette.get_database("staff").execute(
|
||||
return await datasette.get_database(
|
||||
"staff"
|
||||
).execute(
|
||||
"select count(*) from admin_users where user_id = :user_id",
|
||||
{"user_id": user_id},
|
||||
)
|
||||
|
|
@ -1059,18 +1145,21 @@ This example registers two new magic parameters: ``:_request_http_version`` retu
|
|||
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
def uuid(key, request):
|
||||
if key == "new":
|
||||
return str(uuid4())
|
||||
else:
|
||||
raise KeyError
|
||||
|
||||
|
||||
def request(key, request):
|
||||
if key == "http_version":
|
||||
return request.scope["http_version"]
|
||||
else:
|
||||
raise KeyError
|
||||
|
||||
|
||||
@hookimpl
|
||||
def register_magic_parameters(datasette):
|
||||
return [
|
||||
|
|
@ -1103,9 +1192,12 @@ This example returns a redirect to a ``/-/login`` page:
|
|||
from datasette import hookimpl
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
||||
@hookimpl
|
||||
def forbidden(request, message):
|
||||
return Response.redirect("/-/login?=" + urlencode({"message": message}))
|
||||
return Response.redirect(
|
||||
"/-/login?=" + urlencode({"message": message})
|
||||
)
|
||||
|
||||
The function can alternatively return an awaitable function if it needs to make any asynchronous method calls. This example renders a template:
|
||||
|
||||
|
|
@ -1114,10 +1206,15 @@ The function can alternatively return an awaitable function if it needs to make
|
|||
from datasette import hookimpl
|
||||
from datasette.utils.asgi import Response
|
||||
|
||||
|
||||
@hookimpl
|
||||
def forbidden(datasette):
|
||||
async def inner():
|
||||
return Response.html(await datasette.render_template("forbidden.html"))
|
||||
return Response.html(
|
||||
await datasette.render_template(
|
||||
"forbidden.html"
|
||||
)
|
||||
)
|
||||
|
||||
return inner
|
||||
|
||||
|
|
@ -1147,11 +1244,17 @@ This example adds a new menu item but only if the signed in user is ``"root"``:
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def menu_links(datasette, actor):
|
||||
if actor and actor.get("id") == "root":
|
||||
return [
|
||||
{"href": datasette.urls.path("/-/edit-schema"), "label": "Edit schema"},
|
||||
{
|
||||
"href": datasette.urls.path(
|
||||
"/-/edit-schema"
|
||||
),
|
||||
"label": "Edit schema",
|
||||
},
|
||||
]
|
||||
|
||||
Using :ref:`internals_datasette_urls` here ensures that links in the menu will take the :ref:`setting_base_url` setting into account.
|
||||
|
|
@ -1188,13 +1291,20 @@ This example adds a new table action if the signed in user is ``"root"``:
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def table_actions(datasette, actor):
|
||||
if actor and actor.get("id") == "root":
|
||||
return [{
|
||||
"href": datasette.urls.path("/-/edit-schema/{}/{}".format(database, table)),
|
||||
"label": "Edit schema for this table",
|
||||
}]
|
||||
return [
|
||||
{
|
||||
"href": datasette.urls.path(
|
||||
"/-/edit-schema/{}/{}".format(
|
||||
database, table
|
||||
)
|
||||
),
|
||||
"label": "Edit schema for this table",
|
||||
}
|
||||
]
|
||||
|
||||
Example: `datasette-graphql <https://datasette.io/plugins/datasette-graphql>`_
|
||||
|
||||
|
|
@ -1238,6 +1348,7 @@ This example will disable CSRF protection for that specific URL path:
|
|||
|
||||
from datasette import hookimpl
|
||||
|
||||
|
||||
@hookimpl
|
||||
def skip_csrf(scope):
|
||||
return scope["path"] == "/submit-comment"
|
||||
|
|
@ -1278,7 +1389,9 @@ This hook is responsible for returning a dictionary corresponding to Datasette :
|
|||
"description": get_instance_description(datasette),
|
||||
"databases": [],
|
||||
}
|
||||
for db_name, db_data_dict in get_my_database_meta(datasette, database, table, key):
|
||||
for db_name, db_data_dict in get_my_database_meta(
|
||||
datasette, database, table, key
|
||||
):
|
||||
metadata["databases"][db_name] = db_data_dict
|
||||
# whatever we return here will be merged with any other plugins using this hook and
|
||||
# will be overwritten by a local metadata.yaml if one exists!
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue