5,578 rows sorted by issue

View and edit SQL

Suggested facets: reactions, created_at (date), updated_at (date)

author_association

id html_url issue_url node_id user created_at updated_at author_association body reactions issue ▼ performed_via_github_app
338523957 https://github.com/simonw/datasette/issues/1#issuecomment-338523957 https://api.github.com/repos/simonw/datasette/issues/1 MDEyOklzc3VlQ29tbWVudDMzODUyMzk1Nw== simonw 9599 2017-10-23T01:09:05Z 2017-10-24T02:42:12Z OWNER

I also need to solve for weird primary keys. If it’s a single integer or a single char field that’s easy. But what if it is a compound key with more than one chat field? What delimiter can I use that will definitely be safe?

Let’s say I use hyphen. Now I need to find a durable encoding for any hyphens that might exist in the key fields themselves.

How about I use URLencoding for every non-alpha-numeric character? That will turn hyphens into (I think) %2D. It should also solve for unicode characters, but it means the vast majority of keys (integers) will display neatly, including a compound key of eg 5678-345

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Addressable pages for every row in a table 267513424  
338524454 https://github.com/simonw/datasette/issues/1#issuecomment-338524454 https://api.github.com/repos/simonw/datasette/issues/1 MDEyOklzc3VlQ29tbWVudDMzODUyNDQ1NA== simonw 9599 2017-10-23T01:15:24Z 2017-10-23T01:15:24Z OWNER

Table rendering logic needs to detect the primary key field and turn it into a hyperlink. If there is a compound primary key it should add an extra column at the start of the table which displays the compound key as a link

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Addressable pages for every row in a table 267513424  
338857568 https://github.com/simonw/datasette/issues/1#issuecomment-338857568 https://api.github.com/repos/simonw/datasette/issues/1 MDEyOklzc3VlQ29tbWVudDMzODg1NzU2OA== simonw 9599 2017-10-24T02:57:12Z 2017-10-24T02:57:12Z OWNER

I can find the primary keys using:

PRAGMA table_info(myTable)
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Addressable pages for every row in a table 267513424  
338861511 https://github.com/simonw/datasette/issues/1#issuecomment-338861511 https://api.github.com/repos/simonw/datasette/issues/1 MDEyOklzc3VlQ29tbWVudDMzODg2MTUxMQ== simonw 9599 2017-10-24T03:24:17Z 2017-10-24T03:24:17Z OWNER

Some tables won't have primary keys, in which case I won't generate pages for individual records.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Addressable pages for every row in a table 267513424  
338872286 https://github.com/simonw/datasette/issues/1#issuecomment-338872286 https://api.github.com/repos/simonw/datasette/issues/1 MDEyOklzc3VlQ29tbWVudDMzODg3MjI4Ng== simonw 9599 2017-10-24T04:46:06Z 2017-10-24T04:46:06Z OWNER

I'm going to use , as the separator between elements of a compound primary key. If those elements themselves include a comma I will use %2C in its place.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Addressable pages for every row in a table 267513424  
338882207 https://github.com/simonw/datasette/issues/1#issuecomment-338882207 https://api.github.com/repos/simonw/datasette/issues/1 MDEyOklzc3VlQ29tbWVudDMzODg4MjIwNw== simonw 9599 2017-10-24T05:56:04Z 2017-10-24T05:56:04Z OWNER

Next step: generate links to these.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Addressable pages for every row in a table 267513424  
338526148 https://github.com/simonw/datasette/issues/3#issuecomment-338526148 https://api.github.com/repos/simonw/datasette/issues/3 MDEyOklzc3VlQ29tbWVudDMzODUyNjE0OA== simonw 9599 2017-10-23T01:35:17Z 2017-10-23T01:35:17Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make individual column valuables addressable, with smart content types 267515678  
338530389 https://github.com/simonw/datasette/issues/4#issuecomment-338530389 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODUzMDM4OQ== simonw 9599 2017-10-23T02:15:41Z 2017-10-23T02:15:41Z OWNER

This means I need a good solution for these compile time options while running in development mode

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338530480 https://github.com/simonw/datasette/issues/4#issuecomment-338530480 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODUzMDQ4MA== simonw 9599 2017-10-23T02:16:33Z 2017-10-23T02:16:33Z OWNER

How about when the service starts up it checks for a compile.json file and, if it is missing, creates it using the same code we run at compile time normally

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338531827 https://github.com/simonw/datasette/issues/4#issuecomment-338531827 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODUzMTgyNw== simonw 9599 2017-10-23T02:28:31Z 2017-10-23T02:29:05Z OWNER

Many of the applications I want to implement with this would benefit from having permanent real URLs.

So let’s have both. The sha1 urls will serve far future cache headers (and an etag derived from their path). The non sha1 URLs will serve 302 uncached redirects to the sha1 locations.

We will have a setting that lets people opt out of this behavior.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338789734 https://github.com/simonw/datasette/issues/4#issuecomment-338789734 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODc4OTczNA== simonw 9599 2017-10-23T20:40:25Z 2017-10-23T21:10:19Z OWNER

URL design:

 /database/table.json - redirects to /database-6753f4a/table.json

So we always redirect to the version with the truncated hash in the URL.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338797522 https://github.com/simonw/datasette/issues/4#issuecomment-338797522 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODc5NzUyMg== simonw 9599 2017-10-23T21:09:33Z 2017-10-23T21:09:33Z OWNER

https://stackoverflow.com/a/18134919/6083 is a good answer about how many characters of the hash are needed to be unique. I say we default to 7 characters, like git does - but allow extras to be configured.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338799438 https://github.com/simonw/datasette/issues/4#issuecomment-338799438 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODc5OTQzOA== simonw 9599 2017-10-23T21:17:25Z 2017-10-23T21:17:25Z OWNER

Can I take advantage of HTTP/2 so even if you get redirected I start serving you the correct resource straight away?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338804173 https://github.com/simonw/datasette/issues/4#issuecomment-338804173 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODgwNDE3Mw== simonw 9599 2017-10-23T21:36:37Z 2017-10-23T21:36:37Z OWNER

Looks like the easiest way to implement HTTP/2 server push today is to run behind Cloudflare and use this:

Link: </asset/to/push.js>; rel=preload; as=script

https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/

Here's the W3C draft: https://w3c.github.io/preload/

From https://w3c.github.io/preload/#as-attribute it looks like I should use as=fetch if the content is intended for consumption by fetch() or XMLHTTPRequest.

Unclear if I should throw as=fetch crossorigin in there. Need to experiment on that.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338806718 https://github.com/simonw/datasette/issues/4#issuecomment-338806718 https://api.github.com/repos/simonw/datasette/issues/4 MDEyOklzc3VlQ29tbWVudDMzODgwNjcxOA== simonw 9599 2017-10-23T21:47:53Z 2017-10-23T21:47:53Z OWNER

Here's what the homepage of cloudflare.com does (with newlines added within the link header for clarity):

$ curl -i 'https://www.cloudflare.com/' 
HTTP/1.1 200 OK
Date: Mon, 23 Oct 2017 21:45:58 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
link:
    </vendor/bitdashplayer.min.css>; rel=preload; as=style,
    </vendor/bitdash-controls.min.css>; rel=preload; as=style,
    </video/marketing-video/cloudflare-marketing-video.mpd>; rel=preload,
    </video/marketing-video/cloudflare-marketing-video.m3u8>; rel=preload,
    </video/marketing-video/video_0_800000/dash/init.mp4>; rel=preload; as=video,
    </video/marketing-video/audio_0_128000/dash/init.mp4>; rel=preload; as=video,
    </video/marketing-video/video_0_800000/dash/segment_0.m4s>; rel=preload; as=video,
    </video/marketing-video/audio_0_128000/dash/segment_0.m4s>; rel=preload; as=video,
    </video/new-short-optimized-22.mp4>; rel=preload; as=video,
    </video/marketing-video/poster.jpg>; rel=preload; as=image

The original header looked like this:

link: </vendor/bitdashplayer.min.css>; rel=preload; as=style, </vendor/bitdash-controls.min.css>; rel=preload; as=style, </video/marketing-video/cloudflare-marketing-video.mpd>; rel=preload, </video/marketing-video/cloudflare-marketing-video.m3u8>; rel=preload, </video/marketing-video/video_0_800000/dash/init.mp4>; rel=preload; as=video, </video/marketing-video/audio_0_128000/dash/init.mp4>; rel=preload; as=video, </video/marketing-video/video_0_800000/dash/segment_0.m4s>; rel=preload; as=video, </video/marketing-video/audio_0_128000/dash/segment_0.m4s>; rel=preload; as=video, </video/new-short-optimized-22.mp4>; rel=preload; as=video, </video/marketing-video/poster.jpg>; rel=preload; as=image
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make URLs immutable 267515836  
338524857 https://github.com/simonw/datasette/issues/5#issuecomment-338524857 https://api.github.com/repos/simonw/datasette/issues/5 MDEyOklzc3VlQ29tbWVudDMzODUyNDg1Nw== simonw 9599 2017-10-23T01:20:30Z 2017-10-23T01:20:30Z OWNER

https://stackoverflow.com/a/14468878/6083

Looks like I should order by compound primary key and implement cursor-based pagination.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Implement sensible query pagination 267516066  
339027711 https://github.com/simonw/datasette/issues/5#issuecomment-339027711 https://api.github.com/repos/simonw/datasette/issues/5 MDEyOklzc3VlQ29tbWVudDMzOTAyNzcxMQ== simonw 9599 2017-10-24T15:21:30Z 2017-10-24T15:21:30Z OWNER

I have code to detect primary keys on tables... but what should I do for tables that lack primary keys? How should I even sort them?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Implement sensible query pagination 267516066  
339028979 https://github.com/simonw/datasette/issues/5#issuecomment-339028979 https://api.github.com/repos/simonw/datasette/issues/5 MDEyOklzc3VlQ29tbWVudDMzOTAyODk3OQ== simonw 9599 2017-10-24T15:25:08Z 2017-10-24T15:25:08Z OWNER

Looks like I can use the SQLite specific “rowid” in that case. It isn’t guaranteed to stay consistent across a VACUUM but that’s ok because we are immutable anyway.

https://www.sqlite.org/lang_createtable.html#rowid

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Implement sensible query pagination 267516066  
338853083 https://github.com/simonw/datasette/issues/7#issuecomment-338853083 https://api.github.com/repos/simonw/datasette/issues/7 MDEyOklzc3VlQ29tbWVudDMzODg1MzA4Mw== simonw 9599 2017-10-24T02:27:25Z 2017-10-24T02:27:25Z OWNER

Fixed in 9d219140694551453bfa528e0624919eb065f9d6

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Framework where by every page is JSON plus a template 267516650  
338697223 https://github.com/simonw/datasette/issues/8#issuecomment-338697223 https://api.github.com/repos/simonw/datasette/issues/8 MDEyOklzc3VlQ29tbWVudDMzODY5NzIyMw== simonw 9599 2017-10-23T15:28:11Z 2017-10-23T15:28:11Z OWNER

Now returning this:

{
    "error": "attempt to write a readonly database",
    "ok": false
}
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Attempting an INSERT or UPDATE should return a sane error message 267517314  
338863155 https://github.com/simonw/datasette/issues/9#issuecomment-338863155 https://api.github.com/repos/simonw/datasette/issues/9 MDEyOklzc3VlQ29tbWVudDMzODg2MzE1NQ== simonw 9599 2017-10-24T03:36:58Z 2017-10-24T03:36:58Z OWNER

I’m going to use py.test and start with all tests in a single tests.py module

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Initial test suite 267517348  
338882110 https://github.com/simonw/datasette/issues/9#issuecomment-338882110 https://api.github.com/repos/simonw/datasette/issues/9 MDEyOklzc3VlQ29tbWVudDMzODg4MjExMA== simonw 9599 2017-10-24T05:55:33Z 2017-10-24T05:55:33Z OWNER

Well, I've started it at least.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Initial test suite 267517348  
341938424 https://github.com/simonw/datasette/issues/10#issuecomment-341938424 https://api.github.com/repos/simonw/datasette/issues/10 MDEyOklzc3VlQ29tbWVudDM0MTkzODQyNA== simonw 9599 2017-11-04T23:48:57Z 2017-11-04T23:48:57Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Set up Travis 267517381  
338530704 https://github.com/simonw/datasette/issues/11#issuecomment-338530704 https://api.github.com/repos/simonw/datasette/issues/11 MDEyOklzc3VlQ29tbWVudDMzODUzMDcwNA== simonw 9599 2017-10-23T02:18:36Z 2017-10-23T02:18:36Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Code that generates compile-time properties about the database  267522549  
348245757 https://github.com/simonw/datasette/issues/12#issuecomment-348245757 https://api.github.com/repos/simonw/datasette/issues/12 MDEyOklzc3VlQ29tbWVudDM0ODI0NTc1Nw== simonw 9599 2017-11-30T16:39:45Z 2017-11-30T16:39:45Z OWNER

It is now possible to over-ride templates on a per-database / per-row or per-
table basis.

When you access e.g. /mydatabase/mytable Datasette will look for the following:

- table-mydatabase-mytable.html
- table.html

If you provided a --template-dir argument to datasette serve it will look in
that directory first.

The lookup rules are as follows:

Index page (/):
    index.html

Database page (/mydatabase):
    database-mydatabase.html
    database.html

Table page (/mydatabase/mytable):
    table-mydatabase-mytable.html
    table.html

Row page (/mydatabase/mytable/id):
    row-mydatabase-mytable.html
    row.html

If a table name has spaces or other unexpected characters in it, the template
filename will follow the same rules as our custom <body> CSS classes
introduced in 8ab3a16 - for example, a table called "Food Trucks"
will attempt to load the following templates:

table-mydatabase-Food-Trucks-399138.html
table.html

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:

{% extends "default:row.html" %}

{% block content %}
<h1>EXTRA HTML AT THE TOP OF THE CONTENT BLOCK</h1>
<p>This line renders the original block:</p>
{{ super() }}
{% endblock %}
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Make it so you can override templates 267523511  
344462608 https://github.com/simonw/datasette/issues/13#issuecomment-344462608 https://api.github.com/repos/simonw/datasette/issues/13 MDEyOklzc3VlQ29tbWVudDM0NDQ2MjYwOA== simonw 9599 2017-11-15T02:04:51Z 2017-11-15T02:04:51Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Add a syntax highlighting SQL editor 267542338  
343675165 https://github.com/simonw/datasette/issues/14#issuecomment-343675165 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM0MzY3NTE2NQ== simonw 9599 2017-11-11T16:07:10Z 2017-11-11T16:07:10Z OWNER

The plugin system can also allow alternative providers for the publish command - e.g. maybe hook up hyper.sh as an option for publishing containers.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
344438724 https://github.com/simonw/datasette/issues/14#issuecomment-344438724 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM0NDQzODcyNA== simonw 9599 2017-11-14T23:47:54Z 2017-11-14T23:47:54Z OWNER

Plugins should be able to interact with the build step. This would give plugins an opportunity to modify the SQL databases and help prepare them for serving - for example, a full-text search plugin might create additional FTS tables, or a mapping plugin might pre-calculate a bunch of geohashes for tables that have latitude/longitude values. Plugins could really take advantage of the immutable nature of the dataset here.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
345067498 https://github.com/simonw/datasette/issues/14#issuecomment-345067498 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM0NTA2NzQ5OA== simonw 9599 2017-11-16T21:25:32Z 2017-11-16T21:26:22Z OWNER

For visualizations, Google Maps should be made available as a plugin. The default visualizations can use Leaflet and Open Street Map, but there's no reason to not make Google Maps available as a plugin, especially if the plugin can provide a mechanism for configuring the necessary API key.

I'm particularly excited in the Google Maps heatmap visualization https://developers.google.com/maps/documentation/javascript/heatmaplayer as seen on http://mochimachine.org/wasteland/

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
345893877 https://github.com/simonw/datasette/issues/14#issuecomment-345893877 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM0NTg5Mzg3Nw== simonw 9599 2017-11-21T02:11:27Z 2017-11-21T02:11:27Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
346244871 https://github.com/simonw/datasette/issues/14#issuecomment-346244871 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM0NjI0NDg3MQ== jacobian 21148 2017-11-22T05:06:30Z 2017-11-22T05:06:30Z CONTRIBUTOR

I'd also suggest taking a look at stevedore, which has a ton of tools for doing plugin stuff. I've had good luck with it in the past.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
346406009 https://github.com/simonw/datasette/issues/14#issuecomment-346406009 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM0NjQwNjAwOQ== simonw 9599 2017-11-22T16:39:08Z 2017-11-22T16:39:08Z OWNER

Oh thanks, that definitely looks like an interesting option.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381442233 https://github.com/simonw/datasette/issues/14#issuecomment-381442233 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ0MjIzMw== simonw 9599 2018-04-15T22:13:06Z 2018-04-15T22:13:06Z OWNER

I started a thread on Twitter asking people for good examples of Python projects with a strong plugin ecosystem: https://twitter.com/simonw/status/985377670388105216

The most impressive example that came back was pytest - which now has nearly 400 plugins: https://plugincompat.herokuapp.com/

The pytest plugin infrastructure is available as an independent package called pluggy - which appears to offer everything I need for Datasette. I'm going to give that a go and see how well it works: https://pluggy.readthedocs.io/en/latest/

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381442494 https://github.com/simonw/datasette/issues/14#issuecomment-381442494 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ0MjQ5NA== simonw 9599 2018-04-15T22:17:59Z 2018-04-15T22:17:59Z OWNER

Datasette 1.0 will be the release of Datasette that attempts to provide a stable plugin API: https://github.com/simonw/datasette/milestone/7

There's a lot of work to be done before then, but as a starting point I'm going to support two very simple extension mechanisms:

  • Template system plugins - where the hook gets passed the Jinja environment and can freely register new template tags and filters
  • SQLite connection plugins - where the hook gets passed a new SQLite connection and can register custom SQLite functions

The template system hook will go near here:

https://github.com/simonw/datasette/blob/efbb4e83374a2c795e436c72fa79f70da72309b8/datasette/app.py#L1225-L1228

The SQLite connection hook will go near here:

https://github.com/simonw/datasette/blob/efbb4e83374a2c795e436c72fa79f70da72309b8/datasette/app.py#L1094-L1098

These two feel simple enough that I'm not worried that I might design an API that I later regret.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381443728 https://github.com/simonw/datasette/issues/14#issuecomment-381443728 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ0MzcyOA== simonw 9599 2018-04-15T22:39:00Z 2018-04-15T22:39:00Z OWNER

Tox is a good example of a project that uses pluggy in the way I want to use it (function hooks rather than classes): https://github.com/tox-dev/tox/blob/master/tox/hookspecs.py

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381446392 https://github.com/simonw/datasette/issues/14#issuecomment-381446392 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ0NjM5Mg== simonw 9599 2018-04-15T23:22:40Z 2018-04-16T05:25:57Z OWNER

OK, from that prototype in f2720b0c6b7172ebe8820 it looks like pluggy provides a solid path forward.

Next steps:

  • Build a demo plugin that uses setuptools entrypoints to register with the datasette plugin manager via pluggy
  • Figure out a mechanism for registering plugins without first needing to publish them to PyPI. Can I load plugins from a special plugins/ directory similar to the --template-dir=templates/ option already supported by Datasette? #211
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381446511 https://github.com/simonw/datasette/issues/14#issuecomment-381446511 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ0NjUxMQ== simonw 9599 2018-04-15T23:25:04Z 2018-04-15T23:25:04Z OWNER

Here's a demo of the convert_units() SQL function I prototyped in f2720b0c6b7172ebe88

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381446906 https://github.com/simonw/datasette/issues/14#issuecomment-381446906 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ0NjkwNg== simonw 9599 2018-04-15T23:31:58Z 2018-04-15T23:34:10Z OWNER

Once I've got the plugins mechanism stable and people start releasing plugins it would be useful to have a dedicated Trove classifier on PyPI for Datasette plugins - Framework :: Datasette for example.

This would help me build a Datasette equivalent of the http://plugincompat.herokuapp.com/ site, which works by scanning PyPI for items with the Framework :: Pytest classifier:

https://github.com/pytest-dev/plugincompat/blob/8bdf1a6fb82807091ece0c68c196103ee8270194/update_index.py#L52-L53

It looks like the mechanism for requesting new PyPI classifiers is to file a ticket against warehouse, like these ones: https://github.com/pypa/warehouse/issues/3570 and https://github.com/pypa/warehouse/issues/2881

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381450394 https://github.com/simonw/datasette/issues/14#issuecomment-381450394 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ1MDM5NA== simonw 9599 2018-04-16T00:27:23Z 2018-04-16T00:27:23Z OWNER

I created https://github.com/simonw/datasette-plugin-demos which is now published to PyPI and can be installed with pip install datasette-plugin-demos - I've confirmed that if you DO install it my Datasette plugins branch picks up the plugins, and select random_integer(1, 4) works as it should.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381450591 https://github.com/simonw/datasette/issues/14#issuecomment-381450591 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTQ1MDU5MQ== simonw 9599 2018-04-16T00:30:22Z 2018-04-16T00:34:42Z OWNER

Slight code design problem... when I tried installing my branch in a fresh virtual environment I got this error, because setup.py now depends on pluggy (from importing __version__):

      File "/private/var/folders/jj/fngnv0810tn2lt_kd3911pdc0000gp/T/pip-req-build-dftqdezt/setup.py", line 2, in <module>
        from datasette import __version__
      File "/private/var/folders/jj/fngnv0810tn2lt_kd3911pdc0000gp/T/pip-req-build-dftqdezt/datasette/__init__.py", line 2, in <module>
        from .hookspecs import hookimpl # noqa
      File "/private/var/folders/jj/fngnv0810tn2lt_kd3911pdc0000gp/T/pip-req-build-dftqdezt/datasette/hookspecs.py", line 1, in <module>
        from pluggy import HookimplMarker
    ModuleNotFoundError: No module named 'pluggy'

Looks like I've run into point 6 on https://packaging.python.org/guides/single-sourcing-package-version/ :

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381611738 https://github.com/simonw/datasette/issues/14#issuecomment-381611738 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTYxMTczOA== simonw 9599 2018-04-16T14:07:30Z 2018-04-16T14:07:30Z OWNER

I should check if it's possible to have two template registration function plugins in a single plugin module. If it isn't maybe I should use class plugins instead of module plugins.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381621338 https://github.com/simonw/datasette/issues/14#issuecomment-381621338 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTYyMTMzOA== simonw 9599 2018-04-16T14:36:27Z 2018-04-16T14:36:27Z OWNER

Annoyingly, the following only results in the last of the two prepare_connection hooks being registered:

from datasette import hookimpl
import pint
import random

ureg = pint.UnitRegistry()


@hookimpl
def prepare_connection(conn):
    def convert_units(amount, from_, to_):
        "select convert_units(100, 'm', 'ft');"
        return (amount * ureg(from_)).to(to_).to_tuple()[0]
    conn.create_function('convert_units', 3, convert_units)


@hookimpl
def prepare_connection(conn):
    conn.create_function('random_integer', 2, random.randint)
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381622793 https://github.com/simonw/datasette/issues/14#issuecomment-381622793 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTYyMjc5Mw== simonw 9599 2018-04-16T14:40:39Z 2018-04-17T01:47:15Z OWNER

I think that's OK. The two plugins I've implemented so far (prepare_connection and prepare_jinja2_environment) both make sense if they can only be defined once-per-plugin. For the moment I'll assume I can define future hooks to work well with the same limitation.

The syntactic sugar idea in #220 can help here too.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
381809998 https://github.com/simonw/datasette/issues/14#issuecomment-381809998 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MTgwOTk5OA== simonw 9599 2018-04-17T02:23:39Z 2018-04-17T02:23:39Z OWNER

I just shipped Datasette 0.19 with where I'm at so far: https://github.com/simonw/datasette/releases/tag/0.19

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
382256729 https://github.com/simonw/datasette/issues/14#issuecomment-382256729 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MjI1NjcyOQ== simonw 9599 2018-04-18T04:29:29Z 2018-04-18T04:30:14Z OWNER

I added a mechanism for plugins to serve static files and define custom CSS and JS URLs in #214 - see new documentation on http://datasette.readthedocs.io/en/latest/plugins.html#static-assets and http://datasette.readthedocs.io/en/latest/plugins.html#extra-css-urls

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
383139889 https://github.com/simonw/datasette/issues/14#issuecomment-383139889 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MzEzOTg4OQ== simonw 9599 2018-04-20T15:51:47Z 2018-04-20T15:51:47Z OWNER

I released everything we have so far in Datasette 0.20 and built and released an example plugin, datasette-cluster-map. Here's my blog entry about it: https://simonwillison.net/2018/Apr/20/datasette-plugins/

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
383140111 https://github.com/simonw/datasette/issues/14#issuecomment-383140111 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDM4MzE0MDExMQ== simonw 9599 2018-04-20T15:52:33Z 2018-04-20T15:52:33Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
491944613 https://github.com/simonw/datasette/issues/14#issuecomment-491944613 https://api.github.com/repos/simonw/datasette/issues/14 MDEyOklzc3VlQ29tbWVudDQ5MTk0NDYxMw== simonw 9599 2019-05-13T18:58:19Z 2019-05-13T18:58:19Z OWNER

We've grown a bunch of plugin hooks over the past two years: https://datasette.readthedocs.io/en/latest/plugins.html#plugin-hooks

Since the plugin system will never be 100% "finished", I'm closing this in favor of the label: https://github.com/simonw/datasette/labels/plugins

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Datasette Plugins 267707940  
338768860 https://github.com/simonw/datasette/issues/16#issuecomment-338768860 https://api.github.com/repos/simonw/datasette/issues/16 MDEyOklzc3VlQ29tbWVudDMzODc2ODg2MA== simonw 9599 2017-10-23T19:23:29Z 2017-10-23T19:23:29Z OWNER

I could use the table-reflow mechanism demonstrated here: http://demos.jquerymobile.com/1.4.3/table-reflow/

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default HTML/CSS needs to look reasonable and be responsive 267726219  
339420462 https://github.com/simonw/datasette/issues/16#issuecomment-339420462 https://api.github.com/repos/simonw/datasette/issues/16 MDEyOklzc3VlQ29tbWVudDMzOTQyMDQ2Mg== simonw 9599 2017-10-25T18:10:51Z 2017-10-25T18:10:51Z OWNER

https://sitesforprofit.com/responsive-table-plugins-and-patterns has some useful links.

I really like the pattern from https://css-tricks.com/responsive-data-tables/

/* 
Max width before this PARTICULAR table gets nasty
This query will take effect for any screen smaller than 760px
and also iPads specifically.
*/
@media 
only screen and (max-width: 760px),
(min-device-width: 768px) and (max-device-width: 1024px)  {

    /* Force table to not be like tables anymore */
    table, thead, tbody, th, td, tr { 
        display: block; 
    }

    /* Hide table headers (but not display: none;, for accessibility) */
    thead tr { 
        position: absolute;
        top: -9999px;
        left: -9999px;
    }

    tr { border: 1px solid #ccc; }

    td { 
        /* Behave  like a "row" */
        border: none;
        border-bottom: 1px solid #eee; 
        position: relative;
        padding-left: 50%; 
    }

    td:before { 
        /* Now like a table header */
        position: absolute;
        /* Top/left values mimic padding */
        top: 6px;
        left: 6px;
        width: 45%; 
        padding-right: 10px; 
        white-space: nowrap;
    }

    /*
    Label the data
    */
    td:nth-of-type(1):before { content: "First Name"; }
    td:nth-of-type(2):before { content: "Last Name"; }
    td:nth-of-type(3):before { content: "Job Title"; }
    td:nth-of-type(4):before { content: "Favorite Color"; }
    td:nth-of-type(5):before { content: "Wars of Trek?"; }
    td:nth-of-type(6):before { content: "Porn Name"; }
    td:nth-of-type(7):before { content: "Date of Birth"; }
    td:nth-of-type(8):before { content: "Dream Vacation City"; }
    td:nth-of-type(9):before { content: "GPA"; }
    td:nth-of-type(10):before { content: "Arbitrary Data"; }
}
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default HTML/CSS needs to look reasonable and be responsive 267726219  
342032943 https://github.com/simonw/datasette/issues/16#issuecomment-342032943 https://api.github.com/repos/simonw/datasette/issues/16 MDEyOklzc3VlQ29tbWVudDM0MjAzMjk0Mw== simonw 9599 2017-11-06T02:50:07Z 2017-11-06T02:50:07Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default HTML/CSS needs to look reasonable and be responsive 267726219  
343643332 https://github.com/simonw/datasette/issues/16#issuecomment-343643332 https://api.github.com/repos/simonw/datasette/issues/16 MDEyOklzc3VlQ29tbWVudDM0MzY0MzMzMg== simonw 9599 2017-11-11T06:00:04Z 2017-11-11T06:00:04Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default HTML/CSS needs to look reasonable and be responsive 267726219  
343647300 https://github.com/simonw/datasette/issues/16#issuecomment-343647300 https://api.github.com/repos/simonw/datasette/issues/16 MDEyOklzc3VlQ29tbWVudDM0MzY0NzMwMA== simonw 9599 2017-11-11T07:41:19Z 2017-11-11T07:53:09Z OWNER

Still needed:

  • A link to the homepage from some kind of navigation bar in the header
  • link to github.com/simonw/datasette in the footer
  • Slightly better titles (maybe ditch the visited link colours for titles only? should keep those for primary key links)
  • Links to the .json and .jsono versions of every view
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default HTML/CSS needs to look reasonable and be responsive 267726219  
343691342 https://github.com/simonw/datasette/issues/16#issuecomment-343691342 https://api.github.com/repos/simonw/datasette/issues/16 MDEyOklzc3VlQ29tbWVudDM0MzY5MTM0Mg== simonw 9599 2017-11-11T20:19:07Z 2017-11-11T20:19:07Z OWNER

Closing this, opening a fresh ticket for the navigation stuff.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Default HTML/CSS needs to look reasonable and be responsive 267726219  
338852971 https://github.com/simonw/datasette/issues/17#issuecomment-338852971 https://api.github.com/repos/simonw/datasette/issues/17 MDEyOklzc3VlQ29tbWVudDMzODg1Mjk3MQ== simonw 9599 2017-10-24T02:26:47Z 2017-10-24T02:26:47Z OWNER

I'm not going to bother with this.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
In development mode, should still pick up new .db files 267732005  
754188383 https://github.com/simonw/datasette/issues/18#issuecomment-754188383 https://api.github.com/repos/simonw/datasette/issues/18 MDEyOklzc3VlQ29tbWVudDc1NDE4ODM4Mw== simonw 9599 2021-01-04T20:05:48Z 2021-01-04T20:05:48Z OWNER

I'm not using Sanic any more, but this is still very feasible. If I ever do it I'll write a plugin.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
See if I can get a websockets interface working 267739593  
339366612 https://github.com/simonw/datasette/issues/19#issuecomment-339366612 https://api.github.com/repos/simonw/datasette/issues/19 MDEyOklzc3VlQ29tbWVudDMzOTM2NjYxMg== simonw 9599 2017-10-25T15:21:16Z 2017-10-25T15:21:16Z OWNER

I had to manually set the content disposition header:

return await response.file_stream(
    filepath, headers={
        'Content-Disposition': 'attachment; filename="{}"'.format(ilepath)
    }
)

In the next release of Sanic I can just use the filename= argument instead:

https://github.com/channelcat/sanic/commit/07e95dba4f5983afc1e673df14bdd278817288aa

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Efficient url for downloading the raw database file 267741262  
338769538 https://github.com/simonw/datasette/issues/20#issuecomment-338769538 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDMzODc2OTUzOA== simonw 9599 2017-10-23T19:25:55Z 2017-10-23T19:25:55Z OWNER

Maybe this should be handled by views instead?

https://stateless-datasets-wreplxalgu.now.sh/ lists some views

https://stateless-datasets-wreplxalgu.now.sh/?sql=select%20*%20from%20%22Order%20Subtotals%22 is an example showing the content of a view.

What would the URL to views be? I don't think a view can share a name with a table, so the same URL scheme could work for both.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
343581130 https://github.com/simonw/datasette/issues/20#issuecomment-343581130 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0MzU4MTEzMA== simonw 9599 2017-11-10T20:44:38Z 2017-11-10T20:44:38Z OWNER

I'm going to handle this a different way. I'm going to support a local history of your own queries stored in localStorage, but if you want to share a query you have to do it with a URL.

If people really want canned query support, they can do that using custom templates - see #12 - or by adding views to their database before they publish it.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
348420129 https://github.com/simonw/datasette/issues/20#issuecomment-348420129 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0ODQyMDEyOQ== simonw 9599 2017-12-01T07:16:25Z 2017-12-01T07:16:25Z OWNER

I've found some examples of canned queries I want to support that can't be represented as views, so I'm going to reopen this.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
348420955 https://github.com/simonw/datasette/issues/20#issuecomment-348420955 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0ODQyMDk1NQ== simonw 9599 2017-12-01T07:21:08Z 2017-12-01T07:21:08Z OWNER

I'll use the existing metadata.json file:

{
    "databases": {
        "mydb": {
            "queries": {
                 "custom_thingy": {...

The query definition can either be just a string of SQL, or it can be an object with a sql key and optional title and description keys.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
348860623 https://github.com/simonw/datasette/issues/20#issuecomment-348860623 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0ODg2MDYyMw== simonw 9599 2017-12-04T04:56:21Z 2017-12-04T04:56:21Z OWNER

While I'm doing this, I could add per-database and per-table metadata too ala #68

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
349027974 https://github.com/simonw/datasette/issues/20#issuecomment-349027974 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0OTAyNzk3NA== simonw 9599 2017-12-04T17:01:19Z 2017-12-04T17:01:19Z OWNER

This is also a good opportunity to re-factor out a separate query.html template - right now the database.html template is doing two jobs.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
349359498 https://github.com/simonw/datasette/issues/20#issuecomment-349359498 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0OTM1OTQ5OA== simonw 9599 2017-12-05T16:30:06Z 2017-12-05T16:30:06Z OWNER

Named canned queries can now be defined in metadata.json like this:

    {
        "databases": {
            "timezones": {
                "queries": {
                    "timezone_for_point": "select tzid from timezones ..."
                }
            }
        }
    }

These will be shown in a new "Queries" section beneath "Views" on the database page.

https://user-images.githubusercontent.com/9599/33618097-762ae188-d996-11e7-8f85-29f998ef7a18.png">

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
349383276 https://github.com/simonw/datasette/issues/20#issuecomment-349383276 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0OTM4MzI3Ng== simonw 9599 2017-12-05T17:45:20Z 2017-12-05T17:45:20Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
349406761 https://github.com/simonw/datasette/issues/20#issuecomment-349406761 https://api.github.com/repos/simonw/datasette/issues/20 MDEyOklzc3VlQ29tbWVudDM0OTQwNjc2MQ== simonw 9599 2017-12-05T19:03:06Z 2017-12-05T19:03:06Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Config file with support for defining canned queries 267759136  
343581332 https://github.com/simonw/datasette/issues/21#issuecomment-343581332 https://api.github.com/repos/simonw/datasette/issues/21 MDEyOklzc3VlQ29tbWVudDM0MzU4MTMzMg== simonw 9599 2017-11-10T20:45:42Z 2017-11-10T20:45:42Z OWNER

I'm not going to use Sanic's mechanism for this. I'll use arguments passed to my cli instead.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Use Sanic configuration mechanism  267769034  
338854988 https://github.com/simonw/datasette/issues/23#issuecomment-338854988 https://api.github.com/repos/simonw/datasette/issues/23 MDEyOklzc3VlQ29tbWVudDMzODg1NDk4OA== simonw 9599 2017-10-24T02:40:12Z 2017-10-25T00:05:46Z OWNER
/database-name/table-name?name__contains=simon&sort=id+desc

Note that if there's a column called "sort" you can still do sort__exact=blah

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support Django-style filters in querystring arguments 267788884  
338859620 https://github.com/simonw/datasette/issues/23#issuecomment-338859620 https://api.github.com/repos/simonw/datasette/issues/23 MDEyOklzc3VlQ29tbWVudDMzODg1OTYyMA== simonw 9599 2017-10-24T03:11:42Z 2017-10-24T03:11:42Z OWNER

I’m going to implement everything in https://docs.djangoproject.com/en/1.11/ref/models/querysets/#field-lookups with the exception of range and the various date ones.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support Django-style filters in querystring arguments 267788884  
338859709 https://github.com/simonw/datasette/issues/23#issuecomment-338859709 https://api.github.com/repos/simonw/datasette/issues/23 MDEyOklzc3VlQ29tbWVudDMzODg1OTcwOQ== simonw 9599 2017-10-24T03:12:18Z 2017-10-24T03:12:42Z OWNER

I’m going to need to write unit tests for this, is this depends on #9

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support Django-style filters in querystring arguments 267788884  
339138809 https://github.com/simonw/datasette/issues/23#issuecomment-339138809 https://api.github.com/repos/simonw/datasette/issues/23 MDEyOklzc3VlQ29tbWVudDMzOTEzODgwOQ== simonw 9599 2017-10-24T21:32:46Z 2017-10-24T21:32:46Z OWNER

May as well support most of https://sqlite.org/lang_expr.html

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support Django-style filters in querystring arguments 267788884  
339186887 https://github.com/simonw/datasette/issues/23#issuecomment-339186887 https://api.github.com/repos/simonw/datasette/issues/23 MDEyOklzc3VlQ29tbWVudDMzOTE4Njg4Nw== simonw 9599 2017-10-25T01:39:43Z 2017-10-25T04:22:41Z OWNER

Still to do:

  • gt, gte, lt, lte
  • like
  • glob
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support Django-style filters in querystring arguments 267788884  
339210353 https://github.com/simonw/datasette/issues/23#issuecomment-339210353 https://api.github.com/repos/simonw/datasette/issues/23 MDEyOklzc3VlQ29tbWVudDMzOTIxMDM1Mw== simonw 9599 2017-10-25T04:23:02Z 2017-10-25T04:23:02Z OWNER

I'm going to call this one done for the moment. The date filters can go in a stretch goal.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support Django-style filters in querystring arguments 267788884  
338834213 https://github.com/simonw/datasette/issues/24#issuecomment-338834213 https://api.github.com/repos/simonw/datasette/issues/24 MDEyOklzc3VlQ29tbWVudDMzODgzNDIxMw== simonw 9599 2017-10-24T00:23:05Z 2017-10-24T00:23:05Z OWNER

If I can’t setect a primary key, I won’t provide a URL for those records

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Implement full URL design 267828746  
339003850 https://github.com/simonw/datasette/issues/24#issuecomment-339003850 https://api.github.com/repos/simonw/datasette/issues/24 MDEyOklzc3VlQ29tbWVudDMzOTAwMzg1MA== simonw 9599 2017-10-24T14:12:00Z 2017-10-24T14:12:00Z OWNER

As of b46e370ee6126aa2fa85cf789a31da38aed98496 this is done.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Implement full URL design 267828746  
343715915 https://github.com/simonw/datasette/issues/25#issuecomment-343715915 https://api.github.com/repos/simonw/datasette/issues/25 MDEyOklzc3VlQ29tbWVudDM0MzcxNTkxNQ== simonw 9599 2017-11-12T06:08:28Z 2017-11-12T06:08:28Z OWNER
con = sqlite3.connect('existing_db.db')
with open('dump.sql', 'w') as f:
    for line in con.iterdump():
        f.write('%s\n' % line)
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Endpoint that returns SQL ready to be piped into DB 267857622  
344487639 https://github.com/simonw/datasette/issues/25#issuecomment-344487639 https://api.github.com/repos/simonw/datasette/issues/25 MDEyOklzc3VlQ29tbWVudDM0NDQ4NzYzOQ== simonw 9599 2017-11-15T05:11:11Z 2017-11-15T05:11:11Z OWNER

Since you can already download the database directly, I'm not going to bother with this one.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Endpoint that returns SQL ready to be piped into DB 267857622  
343644976 https://github.com/simonw/datasette/issues/26#issuecomment-343644976 https://api.github.com/repos/simonw/datasette/issues/26 MDEyOklzc3VlQ29tbWVudDM0MzY0NDk3Ng== simonw 9599 2017-11-11T06:42:23Z 2017-11-11T06:42:23Z OWNER

Simplest version of this:

  1. Create a temporary directory
  2. Write a Dockerfile into it that pulls an image and pip installs datasette
  3. Add symlinks to the DBs they listed (so we don't have to copy them)
  4. Shell out to "now"
  5. Done!
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Command line tool for uploading one or more DBs to Now 267861210  
343645249 https://github.com/simonw/datasette/issues/26#issuecomment-343645249 https://api.github.com/repos/simonw/datasette/issues/26 MDEyOklzc3VlQ29tbWVudDM0MzY0NTI0OQ== simonw 9599 2017-11-11T06:48:59Z 2017-11-11T06:48:59Z OWNER

Doing this works:

import os
os.link('/tmp/databases/northwind.db', '/tmp/tmp-blah/northwind.db')

That creates a link in tmp-blah - and then when I delete that entire directory like so:

import shutil
shutil.rmtree('/tmp/tmp-blah')

The original database is not deleted, just the link.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Command line tool for uploading one or more DBs to Now 267861210  
343645327 https://github.com/simonw/datasette/issues/26#issuecomment-343645327 https://api.github.com/repos/simonw/datasette/issues/26 MDEyOklzc3VlQ29tbWVudDM0MzY0NTMyNw== simonw 9599 2017-11-11T06:51:16Z 2017-11-11T06:51:16Z OWNER

I can create the temporary directory like so:

import tempfile
t = tempfile.TemporaryDirectory()
t
<TemporaryDirectory '/var/folders/w9/0xm39tk94ng9h52g06z4b54c0000gp/T/tmpkym70wlp'>
t.name
'/var/folders/w9/0xm39tk94ng9h52g06z4b54c0000gp/T/tmpkym70wlp'

And then to delete it all:

t.cleanup()
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Command line tool for uploading one or more DBs to Now 267861210  
344179878 https://github.com/simonw/datasette/issues/27#issuecomment-344179878 https://api.github.com/repos/simonw/datasette/issues/27 MDEyOklzc3VlQ29tbWVudDM0NDE3OTg3OA== simonw 9599 2017-11-14T08:21:22Z 2017-11-14T08:21:22Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Ability to plot a simple graph 267886330  
345652450 https://github.com/simonw/datasette/issues/27#issuecomment-345652450 https://api.github.com/repos/simonw/datasette/issues/27 MDEyOklzc3VlQ29tbWVudDM0NTY1MjQ1MA== rgieseke 198537 2017-11-20T10:19:39Z 2017-11-20T10:19:39Z CONTRIBUTOR

If Data Package metadata gets adopted (#105) the views spec work might also be worth a look:

http://frictionlessdata.io/specs/views/

http://datahub.io/docs/features/views

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Ability to plot a simple graph 267886330  
403910774 https://github.com/simonw/datasette/issues/27#issuecomment-403910774 https://api.github.com/repos/simonw/datasette/issues/27 MDEyOklzc3VlQ29tbWVudDQwMzkxMDc3NA== simonw 9599 2018-07-10T17:52:41Z 2018-07-10T17:52:41Z OWNER

I consider this handled by https://github.com/simonw/datasette-vega

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Ability to plot a simple graph 267886330  
339019873 https://github.com/simonw/datasette/issues/29#issuecomment-339019873 https://api.github.com/repos/simonw/datasette/issues/29 MDEyOklzc3VlQ29tbWVudDMzOTAxOTg3Mw== simonw 9599 2017-10-24T14:58:33Z 2017-10-24T14:58:33Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Handle bytestring records encoding to JSON 268050821  
344352573 https://github.com/simonw/datasette/issues/30#issuecomment-344352573 https://api.github.com/repos/simonw/datasette/issues/30 MDEyOklzc3VlQ29tbWVudDM0NDM1MjU3Mw== simonw 9599 2017-11-14T18:29:01Z 2017-11-14T18:29:01Z OWNER

This is a dupe of #85

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Do something neat with foreign keys 268078453  
392580715 https://github.com/simonw/datasette/issues/31#issuecomment-392580715 https://api.github.com/repos/simonw/datasette/issues/31 MDEyOklzc3VlQ29tbWVudDM5MjU4MDcxNQ== simonw 9599 2018-05-28T18:10:45Z 2018-05-28T18:10:45Z OWNER

Oops, that commit should have referenced #121

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Idea: colour scheme based on sha256 of db 268087542  
343164111 https://github.com/simonw/datasette/issues/32#issuecomment-343164111 https://api.github.com/repos/simonw/datasette/issues/32 MDEyOklzc3VlQ29tbWVudDM0MzE2NDExMQ== simonw 9599 2017-11-09T14:05:56Z 2017-11-09T14:05:56Z OWNER

Implemented in 31b21f5c5e15fc3acab7fabb170c1da71dc3c98c

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Try running SQLite queries in a separate thread 268106803  
392600866 https://github.com/simonw/datasette/issues/34#issuecomment-392600866 https://api.github.com/repos/simonw/datasette/issues/34 MDEyOklzc3VlQ29tbWVudDM5MjYwMDg2Ng== simonw 9599 2018-05-28T20:45:34Z 2018-05-28T20:45:42Z OWNER

This is an accidental duplicate, work is now taking place in #266

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Support CSV export with a .csv extension 268176505  
345262738 https://github.com/simonw/datasette/issues/36#issuecomment-345262738 https://api.github.com/repos/simonw/datasette/issues/36 MDEyOklzc3VlQ29tbWVudDM0NTI2MjczOA== simonw 9599 2017-11-17T14:45:37Z 2017-11-17T14:45:37Z OWNER

Consider for example https://fivethirtyeight.datasettes.com/fivethirtyeight/inconvenient-sequel%2Fratings

https://user-images.githubusercontent.com/9599/32952559-82b81ea8-cb62-11e7-817a-45c8bba7e9a2.png">

The idea here is to be able to support querystring parameters like this:

  • ?timestamp___date=2017-07-17 - return every item where the timestamp falls on that date
  • ?timestamp___year=2017 - return every item where the timestamp falls within 2017
  • ?timestamp___month=1 - return every item where the month component is January
  • ?timestamp___day=10 - return every item where the day-of-the-month component is 10

This is similar to #64 but a fair bit more complicated.

SQLite date functions are documented here: https://sqlite.org/lang_datefunc.html

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
date, year, month and day querystring lookups 268262480  
345448756 https://github.com/simonw/datasette/issues/36#issuecomment-345448756 https://api.github.com/repos/simonw/datasette/issues/36 MDEyOklzc3VlQ29tbWVudDM0NTQ0ODc1Ng== simonw 9599 2017-11-18T15:17:43Z 2017-11-18T15:17:43Z OWNER
{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
date, year, month and day querystring lookups 268262480  
392575160 https://github.com/simonw/datasette/issues/36#issuecomment-392575160 https://api.github.com/repos/simonw/datasette/issues/36 MDEyOklzc3VlQ29tbWVudDM5MjU3NTE2MA== simonw 9599 2018-05-28T17:30:52Z 2018-05-28T17:30:52Z OWNER

I've changed my mind about this.

"Select every record on the 3rd day of the month" doesn't strike me as an actually useful feature.

"Select every record in 2018 / in May 2018 / on 1st May 2018", if you are using the SQLite-preferred datestring format, are already supported using LIKE queries (or the startswith filter):

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
date, year, month and day querystring lookups 268262480  
339382054 https://github.com/simonw/datasette/issues/37#issuecomment-339382054 https://api.github.com/repos/simonw/datasette/issues/37 MDEyOklzc3VlQ29tbWVudDMzOTM4MjA1NA== simonw 9599 2017-10-25T16:05:56Z 2017-10-25T16:05:56Z OWNER

Could this be as simple as using the iterative JSON encoder and adding a yield statement in between each chunk?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Ability to serialize massive JSON without blocking event loop 268453968  
636360861 https://github.com/simonw/datasette/issues/37#issuecomment-636360861 https://api.github.com/repos/simonw/datasette/issues/37 MDEyOklzc3VlQ29tbWVudDYzNjM2MDg2MQ== simonw 9599 2020-05-30T17:29:20Z 2020-05-30T17:29:20Z OWNER

I'm not going to do this: 2.5 years later I have yet to run into anything that makes me think that JSON serialization performance is worth any extra work.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Ability to serialize massive JSON without blocking event loop 268453968  
339388215 https://github.com/simonw/datasette/issues/38#issuecomment-339388215 https://api.github.com/repos/simonw/datasette/issues/38 MDEyOklzc3VlQ29tbWVudDMzOTM4ODIxNQ== simonw 9599 2017-10-25T16:25:45Z 2017-10-25T16:25:45Z OWNER

First experiment: hook up an iterative CSV dump (just because that’s a tiny bit easier to get started with than iterative a JSON). Have it execute a big select statement and then iterate through the result set 100 rows at a time using sqite fetchmany() - also have it async sleep for a second in between each batch of 100.

Can this work without needing python threads?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Experiment with patterns for concurrent long running queries 268462768  
339388771 https://github.com/simonw/datasette/issues/38#issuecomment-339388771 https://api.github.com/repos/simonw/datasette/issues/38 MDEyOklzc3VlQ29tbWVudDMzOTM4ODc3MQ== simonw 9599 2017-10-25T16:27:29Z 2017-10-25T16:27:29Z OWNER

If this does work, I need to figure it what to do about the HTML view. ASsuming I can iteratively produce JSON and CSV, what to do about HTML? One option: render the first 500 rows as HTML, then hand off to an infinite scroll experience that iteratively loads more rows as JSON.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Experiment with patterns for concurrent long running queries 268462768  
339389105 https://github.com/simonw/datasette/issues/38#issuecomment-339389105 https://api.github.com/repos/simonw/datasette/issues/38 MDEyOklzc3VlQ29tbWVudDMzOTM4OTEwNQ== simonw 9599 2017-10-25T16:28:39Z 2017-10-25T16:28:39Z OWNER

The gold standard here is to be able to serve up increasingly large datasets without blocking the event loop and while using a sustainable amount of RAM

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Experiment with patterns for concurrent long running queries 268462768  
339389328 https://github.com/simonw/datasette/issues/38#issuecomment-339389328 https://api.github.com/repos/simonw/datasette/issues/38 MDEyOklzc3VlQ29tbWVudDMzOTM4OTMyOA== simonw 9599 2017-10-25T16:29:23Z 2017-10-25T16:29:23Z OWNER

Ideally we can get some serious gains from the fact that our database file is opened with the immutable option.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Experiment with patterns for concurrent long running queries 268462768  
392601114 https://github.com/simonw/datasette/issues/38#issuecomment-392601114 https://api.github.com/repos/simonw/datasette/issues/38 MDEyOklzc3VlQ29tbWVudDM5MjYwMTExNA== simonw 9599 2018-05-28T20:47:31Z 2018-05-28T20:47:31Z OWNER

I think the way Datasette executes SQL queries in a thread pool introduced in #45 is a good solution for this ticket.

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Experiment with patterns for concurrent long running queries 268462768  
339406634 https://github.com/simonw/datasette/issues/39#issuecomment-339406634 https://api.github.com/repos/simonw/datasette/issues/39 MDEyOklzc3VlQ29tbWVudDMzOTQwNjYzNA== simonw 9599 2017-10-25T17:27:10Z 2017-10-25T17:27:10Z OWNER

It certainly looks like some of the stuff in https://sqlite.org/pragma.html could be used to screw around with things. Example: PRAGMA case_sensitive_like = 1 - would that affect future queries?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Protect against malicious SQL that causes damage even though our DB is immutable 268469569  
339413825 https://github.com/simonw/datasette/issues/39#issuecomment-339413825 https://api.github.com/repos/simonw/datasette/issues/39 MDEyOklzc3VlQ29tbWVudDMzOTQxMzgyNQ== simonw 9599 2017-10-25T17:48:48Z 2017-10-25T17:48:48Z OWNER

Could I use https://sqlparse.readthedocs.io/en/latest/ to parse incoming statements and ensure they are pure SELECTs? Would that prevent people from using a compound SELECT statement to trigger an evil PRAGMA of some sort?

{
    "total_count": 0,
    "+1": 0,
    "-1": 0,
    "laugh": 0,
    "hooray": 0,
    "confused": 0,
    "heart": 0,
    "rocket": 0,
    "eyes": 0
}
Protect against malicious SQL that causes damage even though our DB is immutable 268469569  

Next page

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]);