kopia lustrzana https://github.com/simonw/datasette
raise_404() function for use in custom templates, closes #964
rodzic
30b98e4d29
commit
699be7dea9
|
@ -1053,19 +1053,27 @@ class DatasetteRouter:
|
|||
headers["Location"] = location
|
||||
return ""
|
||||
|
||||
def raise_404(message=""):
|
||||
raise NotFoundExplicit(message)
|
||||
|
||||
context.update(
|
||||
{
|
||||
"custom_header": custom_header,
|
||||
"custom_status": custom_status,
|
||||
"custom_redirect": custom_redirect,
|
||||
"raise_404": raise_404,
|
||||
}
|
||||
)
|
||||
body = await self.ds.render_template(
|
||||
template,
|
||||
context,
|
||||
request=request,
|
||||
view_name="page",
|
||||
)
|
||||
try:
|
||||
body = await self.ds.render_template(
|
||||
template,
|
||||
context,
|
||||
request=request,
|
||||
view_name="page",
|
||||
)
|
||||
except NotFoundExplicit as e:
|
||||
await self.handle_500(request, send, e)
|
||||
return
|
||||
# Pull content-type out into separate parameter
|
||||
content_type = "text/html; charset=utf-8"
|
||||
matches = [k for k in headers if k.lower() == "content-type"]
|
||||
|
@ -1199,3 +1207,7 @@ def route_pattern_from_filepath(filepath):
|
|||
else:
|
||||
re_bits.append(re.escape(bit))
|
||||
return re.compile("".join(re_bits))
|
||||
|
||||
|
||||
class NotFoundExplicit(NotFound):
|
||||
pass
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.. _customization:
|
||||
|
||||
Customization
|
||||
=============
|
||||
Custom pages and templates
|
||||
==========================
|
||||
|
||||
Datasette provides a number of ways of customizing the way data is displayed.
|
||||
|
||||
|
@ -12,7 +12,9 @@ When you launch Datasette, you can specify a custom metadata file like this::
|
|||
|
||||
datasette mydb.db --metadata metadata.json
|
||||
|
||||
Your ``metadata.json`` file can include links that look like this::
|
||||
Your ``metadata.json`` file can include links that look like this:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"extra_css_urls": [
|
||||
|
@ -25,7 +27,9 @@ Your ``metadata.json`` file can include links that look like this::
|
|||
|
||||
The extra CSS and JavaScript files will be linked in the ``<head>`` of every page.
|
||||
|
||||
You can also specify a SRI (subresource integrity hash) for these assets::
|
||||
You can also specify a SRI (subresource integrity hash) for these assets:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"extra_css_urls": [
|
||||
|
@ -51,27 +55,39 @@ CSS classes on the <body>
|
|||
Every default template includes CSS classes in the body designed to support
|
||||
custom styling.
|
||||
|
||||
The index template (the top level page at ``/``) gets this::
|
||||
The index template (the top level page at ``/``) gets this:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body class="index">
|
||||
|
||||
The database template (``/dbname``) gets this::
|
||||
The database template (``/dbname``) gets this:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body class="db db-dbname">
|
||||
|
||||
The custom SQL template (``/dbname?sql=...``) gets this::
|
||||
The custom SQL template (``/dbname?sql=...``) gets this:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body class="query db-dbname">
|
||||
|
||||
A canned query template (``/dbname/queryname``) gets this::
|
||||
A canned query template (``/dbname/queryname``) gets this:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body class="query db-dbname query-queryname">
|
||||
|
||||
The table template (``/dbname/tablename``) gets::
|
||||
The table template (``/dbname/tablename``) gets:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body class="table db-dbname table-tablename">
|
||||
|
||||
The row template (``/dbname/tablename/rowid``) gets::
|
||||
The row template (``/dbname/tablename/rowid``) gets:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<body class="row db-dbname table-tablename">
|
||||
|
||||
|
@ -92,7 +108,9 @@ Some examples::
|
|||
"no $ characters" => "no--characters-59e024"
|
||||
|
||||
``<td>`` and ``<th>`` elements also get custom CSS classes reflecting the
|
||||
database column they are representing, for example::
|
||||
database column they are representing, for example:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -131,7 +149,9 @@ The following URLs will now serve the content from those CSS and JS files::
|
|||
http://localhost:8001/static/styles.css
|
||||
http://localhost:8001/static/app.js
|
||||
|
||||
You can reference those files from ``metadata.json`` like so::
|
||||
You can reference those files from ``metadata.json`` like so:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"extra_css_urls": [
|
||||
|
@ -229,7 +249,9 @@ used that - but if that template had not been found, it would have tried for
|
|||
|
||||
It is possible to extend the default templates using Jinja template
|
||||
inheritance. If you want to customize EVERY row template with some additional
|
||||
content you can do so by creating a ``row.html`` template like this::
|
||||
content you can do so by creating a ``row.html`` template like this:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% extends "default:row.html" %}
|
||||
|
||||
|
@ -258,7 +280,9 @@ of a specific column.
|
|||
If you want to output the rendered HTML version of a column, including any
|
||||
links to foreign keys, you can use ``{{ row.display("column_name") }}``.
|
||||
|
||||
Here is an example of a custom ``_table.html`` template::
|
||||
Here is an example of a custom ``_table.html`` template:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for row in display_rows %}
|
||||
<div>
|
||||
|
@ -294,7 +318,7 @@ For example, to capture any request to a URL matching ``/about/*``, you would cr
|
|||
|
||||
A hit to ``/about/news`` would render that template and pass in a variable called ``slug`` with a value of ``"news"``.
|
||||
|
||||
If you use this mechanism don't forget to return a 404 status code if the page should not be considered a valid page. You can do this using ``{{ custom_status(404) }}`` described below.
|
||||
If you use this mechanism don't forget to return a 404 if the referenced content could not be found. You can do this using ``{{ raise_404() }}`` described below.
|
||||
|
||||
Templates defined using custom page routes work particularly well with the ``sql()`` template function from `datasette-template-sql <https://github.com/simonw/datasette-template-sql>`__ or the ``graphql()`` template function from `datasette-graphql <https://github.com/simonw/datasette-graphql#the-graphql-template-function>`__.
|
||||
|
||||
|
@ -305,7 +329,9 @@ Custom headers and status codes
|
|||
|
||||
Custom pages default to being served with a content-type of ``text/html; charset=utf-8`` and a ``200`` status code. You can change these by calling a custom function from within your template.
|
||||
|
||||
For example, to serve a custom page with a ``418 I'm a teapot`` HTTP status code, create a file in ``pages/teapot.html`` containing the following::
|
||||
For example, to serve a custom page with a ``418 I'm a teapot`` HTTP status code, create a file in ``pages/teapot.html`` containing the following:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ custom_status(418) }}
|
||||
<html>
|
||||
|
@ -315,7 +341,9 @@ For example, to serve a custom page with a ``418 I'm a teapot`` HTTP status code
|
|||
</body>
|
||||
</html>
|
||||
|
||||
To serve a custom HTTP header, add a ``custom_header(name, value)`` function call. For example::
|
||||
To serve a custom HTTP header, add a ``custom_header(name, value)`` function call. For example:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ custom_status(418) }}
|
||||
{{ custom_header("x-teapot", "I am") }}
|
||||
|
@ -335,17 +363,36 @@ You can verify this is working using ``curl`` like this::
|
|||
x-teapot: I am
|
||||
content-type: text/html; charset=utf-8
|
||||
|
||||
.. _custom_pages_404:
|
||||
|
||||
Returning 404s
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
To indicate that content could not be found and display the default 404 page you can use the ``raise_404(message)`` function:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if not rows %}
|
||||
{{ raise_404("Content not found") }}
|
||||
{% endif %}
|
||||
|
||||
If you call ``raise_404()`` the other content in your template will be ignored.
|
||||
|
||||
.. _custom_pages_redirects:
|
||||
|
||||
Custom redirects
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
You can use the ``custom_redirect(location)`` function to redirect users to another page, for example in a file called ``pages/datasette.html``::
|
||||
You can use the ``custom_redirect(location)`` function to redirect users to another page, for example in a file called ``pages/datasette.html``:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ custom_redirect("https://github.com/simonw/datasette") }}
|
||||
|
||||
Now requests to ``http://localhost:8001/datasette`` will result in a redirect.
|
||||
|
||||
These redirects are served with a ``301 Found`` status code by default. You can send a ``301 Moved Permanently`` code by passing ``301`` as the second argument to the function::
|
||||
These redirects are served with a ``301 Found`` status code by default. You can send a ``301 Moved Permanently`` code by passing ``301`` as the second argument to the function:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ custom_redirect("https://github.com/simonw/datasette", 301) }}
|
||||
|
|
|
@ -26,7 +26,11 @@ def custom_pages_client(tmp_path_factory):
|
|||
'{{ custom_redirect("/example", 301) }}', "utf-8"
|
||||
)
|
||||
(pages_dir / "route_{name}.html").write_text(
|
||||
"<p>Hello from {{ name }}</p>", "utf-8"
|
||||
"""
|
||||
{% if name == "OhNo" %}{{ raise_404("Oh no") }}{% endif %}
|
||||
<p>Hello from {{ name }}</p>
|
||||
""",
|
||||
"utf-8",
|
||||
)
|
||||
nested_dir = pages_dir / "nested"
|
||||
nested_dir.mkdir()
|
||||
|
@ -91,4 +95,11 @@ def test_redirect2(custom_pages_client):
|
|||
def test_custom_route_pattern(custom_pages_client):
|
||||
response = custom_pages_client.get("/route_Sally")
|
||||
assert response.status == 200
|
||||
assert response.text == "<p>Hello from Sally</p>"
|
||||
assert response.text.strip() == "<p>Hello from Sally</p>"
|
||||
|
||||
|
||||
def test_custom_route_pattern_404(custom_pages_client):
|
||||
response = custom_pages_client.get("/route_OhNo")
|
||||
assert response.status == 404
|
||||
assert "<h1>Error 404</h1>" in response.text
|
||||
assert ">Oh no</" in response.text
|
||||
|
|
Ładowanie…
Reference in New Issue