home / github

Menu
  • Search all tables
  • GraphQL API

issue_comments

Table actions
  • GraphQL API for issue_comments

49 rows where "updated_at" is on date 2020-10-30 sorted by updated_at descending

✖
✖

✎ View and edit SQL

This data as json, CSV (advanced)

Suggested facets: issue_url, updated_at (date)

issue 11

  • load_template hook doesn't work for include/extends 18
  • Navigation menu plus plugin hook 10
  • load_template() plugin hook 6
  • Default menu links should check a real permission 5
  • Table actions menu plus plugin hook 3
  • Table actions menu on view pages, not on query pages 2
  • Mechanism for plugins to add action menu items for various things 1
  • Better way of representing binary data in .csv output 1
  • Nav menu plus menu_links() hook 1
  • Messages should be displayed full width 1
  • Remove load_template plugin hook 1

user 2

  • simonw 47
  • codecov[bot] 2

author_association 2

  • OWNER 47
  • NONE 2
id html_url issue_url node_id user created_at updated_at ▲ author_association body reactions issue performed_via_github_app
719834200 https://github.com/simonw/datasette/issues/1073#issuecomment-719834200 https://api.github.com/repos/simonw/datasette/issues/1073 MDEyOklzc3VlQ29tbWVudDcxOTgzNDIwMA== simonw 9599 2020-10-30T22:52:48Z 2020-10-30T22:52:48Z OWNER

Should mostly be a case of backing out the changes from this commit: 81dea4b07ab2b6f4eaaf248307d2b588472054a1

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Remove load_template plugin hook 733560417  
719833744 https://github.com/simonw/datasette/issues/1072#issuecomment-719833744 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgzMzc0NA== simonw 9599 2020-10-30T22:50:57Z 2020-10-30T22:50:57Z OWNER

Yeah I'm going to remove the load_template plugin hook and see if it's possible to build the edit templates extension against prepare_jinja2_environment instead.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719833070 https://github.com/simonw/datasette/issues/1072#issuecomment-719833070 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgzMzA3MA== simonw 9599 2020-10-30T22:48:04Z 2020-10-30T22:48:04Z OWNER

https://github.com/simonw/datasette/blob/a2a709072059c6b3da365df9a332ca744c2079e9/datasette/app.py#L310-L318

So yeah that plugin hook can probably modify the list of loaders available to the Environment.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719832853 https://github.com/simonw/datasette/issues/1072#issuecomment-719832853 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgzMjg1Mw== simonw 9599 2020-10-30T22:47:12Z 2020-10-30T22:47:12Z OWNER

Maybe I should ditch this hook entirely in favour of the existing prepare_jinja2_environment hook. Could that add new template loaders?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719832651 https://github.com/simonw/datasette/issues/1072#issuecomment-719832651 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgzMjY1MQ== simonw 9599 2020-10-30T22:46:25Z 2020-10-30T22:46:25Z OWNER

I tried using a FunctionLoader and got this error on startup: python File "/Users/simon/Dropbox/Development/datasette/datasette/app.py", line 989, in __init__ for filepath in self.ds.jinja_env.list_templates() File "/Users/simon/.local/share/virtualenvs/datasette-edit-templates-agoZyE3x/lib/python3.8/site-packages/jinja2/environment.py", line 810, in list_templates names = self.loader.list_templates() File "/Users/simon/.local/share/virtualenvs/datasette-edit-templates-agoZyE3x/lib/python3.8/site-packages/jinja2/loaders.py", line 434, in list_templates found.update(loader.list_templates()) File "/Users/simon/.local/share/virtualenvs/datasette-edit-templates-agoZyE3x/lib/python3.8/site-packages/jinja2/loaders.py", line 99, in list_templates raise TypeError("this loader cannot iterate over all templates") TypeError: this loader cannot iterate over all templates So if I'm going to define a custom Jinja loader I'll need to teach plugins to answer the "list templates" query.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719819331 https://github.com/simonw/datasette/issues/1072#issuecomment-719819331 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxOTMzMQ== simonw 9599 2020-10-30T22:00:43Z 2020-10-30T22:00:43Z OWNER

I'll try getting that to work. If I can't get it to work I'll drop the plugin hook for the moment.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719819234 https://github.com/simonw/datasette/issues/1072#issuecomment-719819234 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxOTIzNA== simonw 9599 2020-10-30T22:00:21Z 2020-10-30T22:00:21Z OWNER

There might be a way to save this. Async template loading can't be supported, but what if you could define a load_template() hook which returned a sync function that returned templates...

Then the datasette-edit-templates plugin could reply to load_template by loading all DB templates into memory and returning a load_template sync function that looked up the values in those already-loaded templates.

It could even maintain an in-memory cache that gets updated when a template is edited.

If I do this, I could remove the ability to return an async function from load_template() but add that in the future should Jinja implement a mechanism for async template loading.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719814279 https://github.com/simonw/datasette/issues/1072#issuecomment-719814279 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxNDI3OQ== simonw 9599 2020-10-30T21:45:33Z 2020-10-30T21:45:33Z OWNER

Sadly I'm going to bump load_template from Datasette 0.51 - I don't think I should block the release on resolving this issue.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719813970 https://github.com/simonw/datasette/issues/1072#issuecomment-719813970 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxMzk3MA== simonw 9599 2020-10-30T21:44:40Z 2020-10-30T21:44:40Z OWNER

I'm pretty sure that run_in_executor() workaround won't work. https://github.com/django/asgiref/blob/7becc9daca2628c46af1cb7e46b4c47c1ea27adf/asgiref/sync.py#L83 for example says "You cannot use AsyncToSync in the same thread as an async event loop".

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719813212 https://github.com/simonw/datasette/issues/1072#issuecomment-719813212 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxMzIxMg== simonw 9599 2020-10-30T21:42:35Z 2020-10-30T21:42:35Z OWNER

Filed a feature request here: https://github.com/pallets/jinja/issues/1304

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719811312 https://github.com/simonw/datasette/issues/1072#issuecomment-719811312 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxMTMxMg== simonw 9599 2020-10-30T21:36:49Z 2020-10-30T21:36:49Z OWNER

There's one other option: in datasette-edit-templates I could maybe use asyncio.get_event_loop().run_in_executor(...) to load the templates asynchronously within the Jinja template loader mechanism.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719810533 https://github.com/simonw/datasette/issues/1072#issuecomment-719810533 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxMDUzMw== simonw 9599 2020-10-30T21:34:38Z 2020-10-30T21:34:38Z OWNER

... no wait, my comments above assume that I'm just building the datasette-edit-templates plugin. Does this work as a general solution for all of Datasette? I don't think it does.

This may mean I need to delay the whole feature.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719810023 https://github.com/simonw/datasette/issues/1072#issuecomment-719810023 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgxMDAyMw== simonw 9599 2020-10-30T21:33:06Z 2020-10-30T21:33:06Z OWNER

The ideal solution is for Jinja to offer async template loading. I'll file a feature request, then I'll implement the second option above (async load all templates from the DB before each render).

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719809780 https://github.com/simonw/datasette/issues/1072#issuecomment-719809780 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgwOTc4MA== simonw 9599 2020-10-30T21:32:28Z 2020-10-30T21:32:28Z OWNER

Here's an alternative that would definitely work and would be a lot simpler, at the cost of a fair amount of RAM:

  1. Before rendering the template, load ALL of the most-recent-versions of the templates that are stored in the DB. Use those to populate a DictLoader.
  2. Render the template.

This does mean loading template bodies that we won't use. Provided an instance has less than 100 templates I imagine this will work just fine.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719809259 https://github.com/simonw/datasette/issues/1072#issuecomment-719809259 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgwOTI1OQ== simonw 9599 2020-10-30T21:31:10Z 2020-10-30T21:31:10Z OWNER

How can we tell what template Jinja will need to render?

One approach that could work:

  1. Set up a dummy template loader which records the name of the template that was requested
  2. Load the template
  3. Now we know the list of templates that were requested. Async load those
  4. The dummy template loader can now return the ones we have loaded. Load the template again.
  5. Did it request any more templates? If so, load those, and repeat.
  6. Keep on with this loop until a template load (which might even have to be a render) fails to request any templates that we have not yet loaded.
  7. Render the template.

This is GROSS. It feels like a huge waste of CPU, and it could lead to very weird behaviour if any template variables have side effects.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719807502 https://github.com/simonw/datasette/issues/1072#issuecomment-719807502 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgwNzUwMg== simonw 9599 2020-10-30T21:26:49Z 2020-10-30T21:26:49Z OWNER

It looks like Jinja does not have a mechanism for asynchronous template loading - the loader API is synchronous.

One option may be to figure out which templates are needed (including inherited templates and includes) before rendering the template. Then async load those templates from the database into a DictLoader, then pass that DictLoader to Jinja.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719803880 https://github.com/simonw/datasette/issues/1072#issuecomment-719803880 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTgwMzg4MA== simonw 9599 2020-10-30T21:17:11Z 2020-10-30T21:17:11Z OWNER

Example from the Jinja docs: https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.BaseLoader

```python from jinja2 import BaseLoader, TemplateNotFound from os.path import join, exists, getmtime

class MyLoader(BaseLoader):

def __init__(self, path):
    self.path = path

def get_source(self, environment, template):
    path = join(self.path, template)
    if not exists(path):
        raise TemplateNotFound(template)
    mtime = getmtime(path)
    with file(path) as f:
        source = f.read().decode('utf-8')
    return source, path, lambda: mtime == getmtime(path)

Also available: `jinja2.FunctionLoader(load_func)` which lets me pass it a function like this one:

def load_template(name): ... if name == 'index.html': ... return '...' ... loader = FunctionLoader(load_template) ```

Just one catch: I need to be able to load templates asynchronously, because they live in the database. Let's hope Jinja has a mechanism for that!

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719785005 https://github.com/simonw/datasette/issues/1072#issuecomment-719785005 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTc4NTAwNQ== simonw 9599 2020-10-30T20:36:22Z 2020-10-30T20:36:22Z OWNER

It should be easy enough to show a comment that says which original template names were considered, but I may not be able to show which one was actually used (or which ones came from plugins).

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719784606 https://github.com/simonw/datasette/issues/1072#issuecomment-719784606 https://api.github.com/repos/simonw/datasette/issues/1072 MDEyOklzc3VlQ29tbWVudDcxOTc4NDYwNg== simonw 9599 2020-10-30T20:35:33Z 2020-10-30T20:35:33Z OWNER

To fix this I think I need to move the load_template implementation into a Jinja template loader.

I'm not sure I'll be able to keep the Templates considered comment working though:

https://github.com/simonw/datasette/blob/a2a709072059c6b3da365df9a332ca744c2079e9/datasette/app.py#L745-L750

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template hook doesn't work for include/extends 733499930  
719777499 https://github.com/simonw/datasette/issues/1071#issuecomment-719777499 https://api.github.com/repos/simonw/datasette/issues/1071 MDEyOklzc3VlQ29tbWVudDcxOTc3NzQ5OQ== simonw 9599 2020-10-30T20:20:01Z 2020-10-30T20:20:01Z OWNER

Fixed: https://latest.datasette.io/-/messages

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Messages should be displayed full width 733485423  
719657478 https://github.com/simonw/datasette/pull/1069#issuecomment-719657478 https://api.github.com/repos/simonw/datasette/issues/1069 MDEyOklzc3VlQ29tbWVudDcxOTY1NzQ3OA== codecov[bot] 22429695 2020-10-30T16:31:21Z 2020-10-30T17:46:36Z NONE

Codecov Report

Merging #1069 into main will increase coverage by 0.01%. The diff coverage is 95.83%.

```diff @@ Coverage Diff @@

main #1069 +/-

========================================== + Coverage 91.30% 91.32% +0.01%
========================================== Files 29 29
Lines 3736 3756 +20
========================================== + Hits 3411 3430 +19
- Misses 325 326 +1
```

| Impacted Files | Coverage Δ | | |---|---|---| | datasette/views/base.py | 93.94% <ø> (-0.04%) | :arrow_down: | | datasette/app.py | 96.38% <95.45%> (-0.05%) | :arrow_down: | | datasette/hookspecs.py | 100.00% <100.00%> (ø) | |


Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 222f79b...92f3840. Read the comment docs.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template() plugin hook 733303548  
719672967 https://github.com/simonw/datasette/pull/1069#issuecomment-719672967 https://api.github.com/repos/simonw/datasette/issues/1069 MDEyOklzc3VlQ29tbWVudDcxOTY3Mjk2Nw== simonw 9599 2020-10-30T16:58:01Z 2020-10-30T16:58:01Z OWNER

OK, new hook specification is: python @hookspec(firstresult=True) def load_template(template, request, datasette): "Load the specified template, returning the template code as a string"

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template() plugin hook 733303548  
719670714 https://github.com/simonw/datasette/pull/1069#issuecomment-719670714 https://api.github.com/repos/simonw/datasette/issues/1069 MDEyOklzc3VlQ29tbWVudDcxOTY3MDcxNA== simonw 9599 2020-10-30T16:53:56Z 2020-10-30T16:53:56Z OWNER

I'm having second thoughts about the design of the plugin hook.

Consider the following: python plugin_template_source = pm.hook.load_template( template=template_name, database=context.get("database"), table=context.get("table"), columns=context.get("columns"), view_name=self.name, request=request, datasette=self.ds, ) It's a bit gross that database, table and columns are pulled out of the context like that. This doesn't make sense for pages that are rendered by plugins, for example.

So maybe for the first release of this plugin hook I should cut it down to just seeing template, request and datasette. I can add the table/view/etc stuff back in later if it turns out to be necessary.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template() plugin hook 733303548  
719666912 https://github.com/simonw/datasette/pull/1069#issuecomment-719666912 https://api.github.com/repos/simonw/datasette/issues/1069 MDEyOklzc3VlQ29tbWVudDcxOTY2NjkxMg== simonw 9599 2020-10-30T16:47:44Z 2020-10-30T16:47:44Z OWNER

Bringing over a comment from #1042:

I'd like to do this all in the datasette.render_template() method to ensure it's available to plugins as well, not just core code that uses the BaseView class.

This code is the problem:

https://github.com/simonw/datasette/blob/d3e9b0aecb6f8e9b2befd9c654ccb7ce852db3e7/datasette/views/base.py#L114-L133

I think I'll fix this by moving the select_templates mechanism into datasette.render_templates().

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template() plugin hook 733303548  
719664530 https://github.com/simonw/datasette/pull/1069#issuecomment-719664530 https://api.github.com/repos/simonw/datasette/issues/1069 MDEyOklzc3VlQ29tbWVudDcxOTY2NDUzMA== simonw 9599 2020-10-30T16:43:40Z 2020-10-30T16:43:40Z OWNER

I should include an example in the documentation that shows loading templates from a database table.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template() plugin hook 733303548  
719640430 https://github.com/simonw/datasette/pull/1069#issuecomment-719640430 https://api.github.com/repos/simonw/datasette/issues/1069 MDEyOklzc3VlQ29tbWVudDcxOTY0MDQzMA== simonw 9599 2020-10-30T16:01:13Z 2020-10-30T16:01:13Z OWNER

Next steps: build a demonstration plugin against this.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
load_template() plugin hook 733303548  
719630745 https://github.com/simonw/datasette/issues/1068#issuecomment-719630745 https://api.github.com/repos/simonw/datasette/issues/1068 MDEyOklzc3VlQ29tbWVudDcxOTYzMDc0NQ== simonw 9599 2020-10-30T15:44:13Z 2020-10-30T15:44:13Z OWNER

Documentation: https://docs.datasette.io/en/latest/authentication.html#debug-menu

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default menu links should check a real permission  732939921  
719332460 https://github.com/simonw/datasette/issues/1068#issuecomment-719332460 https://api.github.com/repos/simonw/datasette/issues/1068 MDEyOklzc3VlQ29tbWVudDcxOTMzMjQ2MA== simonw 9599 2020-10-30T07:13:10Z 2020-10-30T07:13:10Z OWNER

I mainly want this so I can add that debug menu to my Dogsheep.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default menu links should check a real permission  732939921  
719331236 https://github.com/simonw/datasette/issues/1068#issuecomment-719331236 https://api.github.com/repos/simonw/datasette/issues/1068 MDEyOklzc3VlQ29tbWVudDcxOTMzMTIzNg== simonw 9599 2020-10-30T07:11:58Z 2020-10-30T07:11:58Z OWNER

Document the new permission here: https://docs.datasette.io/en/stable/authentication.html#built-in-permissions

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default menu links should check a real permission  732939921  
719329219 https://github.com/simonw/datasette/issues/1068#issuecomment-719329219 https://api.github.com/repos/simonw/datasette/issues/1068 MDEyOklzc3VlQ29tbWVudDcxOTMyOTIxOQ== simonw 9599 2020-10-30T07:09:59Z 2020-10-30T07:09:59Z OWNER

Permission idea: debug-menu

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default menu links should check a real permission  732939921  
719328661 https://github.com/simonw/datasette/issues/1068#issuecomment-719328661 https://api.github.com/repos/simonw/datasette/issues/1068 MDEyOklzc3VlQ29tbWVudDcxOTMyODY2MQ== simonw 9599 2020-10-30T07:09:30Z 2020-10-30T07:09:30Z OWNER

Then this can make it available to root: https://github.com/simonw/datasette/blob/18a64fbb29271ce607937110bbdb55488c43f4e0/datasette/default_permissions.py

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default menu links should check a real permission  732939921  
719322666 https://github.com/simonw/datasette/issues/1067#issuecomment-719322666 https://api.github.com/repos/simonw/datasette/issues/1067 MDEyOklzc3VlQ29tbWVudDcxOTMyMjY2Ng== simonw 9599 2020-10-30T07:04:02Z 2020-10-30T07:04:02Z OWNER

Maybe rename it to actions_menu and have it work for database, view, table and query pages using different arguments on each.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table actions menu on view pages, not on query pages 732905360  
719320948 https://github.com/simonw/datasette/issues/1067#issuecomment-719320948 https://api.github.com/repos/simonw/datasette/issues/1067 MDEyOklzc3VlQ29tbWVudDcxOTMyMDk0OA== simonw 9599 2020-10-30T07:02:37Z 2020-10-30T07:02:37Z OWNER

Yes, this should be possible - no point restricting what plugin authors can do with the feature. Will need to add some extra arguments to the plugin hook for this.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table actions menu on view pages, not on query pages 732905360  
719195346 https://github.com/simonw/datasette/issues/690#issuecomment-719195346 https://api.github.com/repos/simonw/datasette/issues/690 MDEyOklzc3VlQ29tbWVudDcxOTE5NTM0Ng== simonw 9599 2020-10-30T05:20:42Z 2020-10-30T05:20:42Z OWNER

I've now added two new plugin hooks: menu_links() and table_actions().

I'm going to close this issue. Further work (on column actions and and database actions) can happen in separate tickets, but I won't include them in Datasette 0.51 since they're much less interesting than table and instance actions.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Mechanism for plugins to add action menu items for various things 573755726  
719194756 https://github.com/simonw/datasette/issues/1066#issuecomment-719194756 https://api.github.com/repos/simonw/datasette/issues/1066 MDEyOklzc3VlQ29tbWVudDcxOTE5NDc1Ng== simonw 9599 2020-10-30T05:18:35Z 2020-10-30T05:18:35Z OWNER

Documentation: https://docs.datasette.io/en/latest/plugin_hooks.html#table-actions-datasette-actor-database-table

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table actions menu plus plugin hook 732859030  
719194619 https://github.com/simonw/datasette/issues/1066#issuecomment-719194619 https://api.github.com/repos/simonw/datasette/issues/1066 MDEyOklzc3VlQ29tbWVudDcxOTE5NDYxOQ== simonw 9599 2020-10-30T05:18:04Z 2020-10-30T05:18:04Z OWNER

The cog only appears if at least one table action has been registered by a plugin. It looks like this:

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table actions menu plus plugin hook 732859030  
719154646 https://github.com/simonw/datasette/issues/1066#issuecomment-719154646 https://api.github.com/repos/simonw/datasette/issues/1066 MDEyOklzc3VlQ29tbWVudDcxOTE1NDY0Ng== simonw 9599 2020-10-30T03:48:15Z 2020-10-30T03:48:15Z OWNER

This will use a very similar implementation to the navigation menu in #1064 - similar plugin hook and I'll use a <details><summary> to implement it.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Table actions menu plus plugin hook 732859030  
719153773 https://github.com/simonw/datasette/pull/1065#issuecomment-719153773 https://api.github.com/repos/simonw/datasette/issues/1065 MDEyOklzc3VlQ29tbWVudDcxOTE1Mzc3Mw== codecov[bot] 22429695 2020-10-30T03:44:57Z 2020-10-30T03:44:57Z NONE

Codecov Report

Merging #1065 into main will increase coverage by 0.03%. The diff coverage is 100.00%.

```diff @@ Coverage Diff @@

main #1065 +/-

========================================== + Coverage 91.23% 91.27% +0.03%
========================================== Files 28 29 +1
Lines 3710 3724 +14
========================================== + Hits 3385 3399 +14
Misses 325 325
```

| Impacted Files | Coverage Δ | | |---|---|---| | datasette/plugins.py | 82.35% <ø> (ø) | | | datasette/app.py | 96.42% <100.00%> (+0.03%) | :arrow_up: | | datasette/default_menu_links.py | 100.00% <100.00%> (ø) | | | datasette/hookspecs.py | 100.00% <100.00%> (ø) | |


Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 1a861be...5f118b5. Read the comment docs.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Nav menu plus menu_links() hook 732856937  
719117185 https://github.com/simonw/datasette/issues/1064#issuecomment-719117185 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTExNzE4NQ== simonw 9599 2020-10-30T01:35:17Z 2020-10-30T01:35:17Z OWNER

I'm going to go with a list of {"label": ..., "href": ...} as the first iteration of this. The logout link will not be returned as part of the plugin output. A default plugin will provide the debug tools if the user is logged in as root.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719111597 https://github.com/simonw/datasette/issues/1064#issuecomment-719111597 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTExMTU5Nw== simonw 9599 2020-10-30T01:15:05Z 2020-10-30T01:15:05Z OWNER

I'm torn on this one. I think I have a very slight preference for plugins returning structured objects as opposed to HTML. Less likely to regret that choice in the future?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719111373 https://github.com/simonw/datasette/issues/1064#issuecomment-719111373 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTExMTM3Mw== simonw 9599 2020-10-30T01:14:13Z 2020-10-30T01:14:13Z OWNER

Plugins returning HTML makes more sense for some of the other areas that plugins will be able to inject content - e.g. injecting content on the table or row page above the table.

If I'm going to have that as a pattern though it may make sense to use HTML here, since that will be consistent with other places that plugins can inject additional content.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719110808 https://github.com/simonw/datasette/issues/1064#issuecomment-719110808 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTExMDgwOA== simonw 9599 2020-10-30T01:12:09Z 2020-10-30T01:12:19Z OWNER

Or... plugins could return HTML - maybe optionally using helper functions to generate common HTML such that plugins which use the helpers can have their HTML modified in the future.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719110582 https://github.com/simonw/datasette/issues/1064#issuecomment-719110582 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTExMDU4Mg== simonw 9599 2020-10-30T01:11:13Z 2020-10-30T01:11:13Z OWNER

Should plugins be able to add forms like the logout form here, or should they be restricted to adding navigation links?

I can't think of a reason a plugin would need to add a form. The logout form is a special case to protect against logout-csrf attacks.

So I think plugins get to return a list of dictionaries, each with a label and an href:

python return [{ "label": "Upload CSVs", "href": datasette.urls.path("/-/upload-csvs") }] But... is there an argument for returning headings, to divide up the menu?

I think so. I also like the idea that a default plugin checks for the root user and outputs links to the different debugging tools - maybe those should be wrapped in a section heading.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719109770 https://github.com/simonw/datasette/issues/1064#issuecomment-719109770 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTEwOTc3MA== simonw 9599 2020-10-30T01:08:14Z 2020-10-30T01:08:14Z OWNER

How should the plugin hook work?

Here's the first version of the HTML: html <div class="nav-menu-inner"> <ul> <li><a href="{{ urls.instance() }}">Home</a></li> <li><a href="{{ urls.path('/-/plugins') }}">Installed plugins</a></li> <li><a href="{{ urls.path('/-/versions') }}">Software versions</a></li> <li><a href="{{ urls.path('/-/metadata') }}">Metadata</a></li> {% if show_logout %} <form action="{{ urls.logout() }}" method="post"> <input type="hidden" name="csrftoken" value="{{ csrftoken() }}"> <button class="button-as-link">Log out</button> </form>{% endif %} </ul> </div>

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719106174 https://github.com/simonw/datasette/issues/1064#issuecomment-719106174 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTEwNjE3NA== simonw 9599 2020-10-30T00:55:12Z 2020-10-30T00:55:12Z OWNER

So what should go in this menu?

If the user is logged in as root, I'll link to the various debug pages.

If they're not logged in at all I don't think the menu should appear.

If they are logged in as anyone, it should display to give them access to the "log out" button.

Plugins can add links to it. If those plugins add links, the menu will display.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719105641 https://github.com/simonw/datasette/issues/1064#issuecomment-719105641 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTEwNTY0MQ== simonw 9599 2020-10-30T00:53:00Z 2020-10-30T00:53:00Z OWNER

Tips for making this accessible: https://css-tricks.com/accessible-svgs/

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719104883 https://github.com/simonw/datasette/issues/1064#issuecomment-719104883 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTEwNDg4Mw== simonw 9599 2020-10-30T00:50:01Z 2020-10-30T00:52:29Z OWNER

Here's what the prototype looks like so far:

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719105197 https://github.com/simonw/datasette/issues/1064#issuecomment-719105197 https://api.github.com/repos/simonw/datasette/issues/1064 MDEyOklzc3VlQ29tbWVudDcxOTEwNTE5Nw== simonw 9599 2020-10-30T00:51:16Z 2020-10-30T00:51:16Z OWNER

I used a <details><summary> for this: https://github.com/simonw/datasette/blob/0d7ac764861d84be24d661cf4104ce61ea11a82a/datasette/templates/base.html#L16-L36

I added a bit of JavaScript so that clicking outside the menu would close it: https://github.com/simonw/datasette/blob/0d7ac764861d84be24d661cf4104ce61ea11a82a/datasette/templates/base.html#L59-L74

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Navigation menu plus plugin hook 732798913  
719094027 https://github.com/simonw/datasette/issues/1034#issuecomment-719094027 https://api.github.com/repos/simonw/datasette/issues/1034 MDEyOklzc3VlQ29tbWVudDcxOTA5NDAyNw== simonw 9599 2020-10-30T00:11:17Z 2020-10-30T00:11:17Z OWNER

Demos:

https://latest.datasette.io/fixtures/binary_data.csv?_size=max

csv rowid,data 1,http://latest.datasette.io/fixtures/binary_data/1.blob?_blob_column=data 2,http://latest.datasette.io/fixtures/binary_data/2.blob?_blob_column=data 3,

https://latest.datasette.io/fixtures.csv?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max

csv rowid,data 1,http://latest.datasette.io/fixtures.blob?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max&_blob_column=data&_blob_hash=f3088978da8f9aea479ffc7f631370b968d2e855eeb172bea7f6c7a04262bb6d 2,http://latest.datasette.io/fixtures.blob?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max&_blob_column=data&_blob_hash=b835b0483cedb86130b9a2c280880bf5fadc5318ddf8c18d0df5204d40df1724 3,

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Better way of representing binary data in .csv output 725184645  

Advanced export

JSON shape: default, array, newline-delimited, object

CSV options:

CREATE TABLE [issue_comments] (
   [html_url] TEXT,
   [issue_url] TEXT,
   [id] INTEGER PRIMARY KEY,
   [node_id] TEXT,
   [user] INTEGER REFERENCES [users]([id]),
   [created_at] TEXT,
   [updated_at] TEXT,
   [author_association] TEXT,
   [body] TEXT,
   [reactions] TEXT,
   [issue] INTEGER REFERENCES [issues]([id])
, [performed_via_github_app] TEXT);
CREATE INDEX [idx_issue_comments_issue]
                ON [issue_comments] ([issue]);
CREATE INDEX [idx_issue_comments_user]
                ON [issue_comments] ([user]);
Powered by Datasette · Queries took 1238.54ms · About: github-to-sqlite
  • Sort ascending
  • Sort descending
  • Facet by this
  • Hide this column
  • Show all columns
  • Show not-blank rows