.status_code
====== 2 failed, 1195 passed, 1 skipped, 32 warnings in 191.06s (0:03:11) ======
Error: Process completed with exit code 1.
```
On reading https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS I feel like I should be a bit more thoughtful about how I treat OPTIONS - maybe it should work for every URL on the site, but return a `204 No Content` header?
Comparing a few different sites:
```
~ % curl -X OPTIONS https://www.google.com/ -i
HTTP/2 405
allow: GET, HEAD
date: Wed, 30 Nov 2022 18:18:15 GMT
content-type: text/html; charset=UTF-8
server: gws
content-length: 1592
x-xss-protection: 0
x-frame-options: SAMEORIGIN
alt-svc: h3="":443""; ma=2592000,h3-29="":443""; ma=2592000,h3-Q050="":443""; ma=2592000,h3-Q046="":443""; ma=2592000,h3-Q043="":443""; ma=2592000,quic="":443""; ma=2592000; v=""46,43""
Error 405 (Method Not Allowed)!!1
405. That’s an error.
The request method OPTIONS
is inappropriate for the URL /
. That’s all we know.
~ % curl -X OPTIONS https://www.mozilla.org/ -i
HTTP/2 405
content-type: text/html; charset=utf-8
content-length: 0
server: meinheld/1.0.2
date: Wed, 30 Nov 2022 18:18:38 GMT
allow: GET, HEAD
x-frame-options: DENY
content-security-policy: child-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com www.googletagmanager.com www.google-analytics.com www.youtube-nocookie.com trackertest.org www.surveygizmo.com accounts.firefox.com accounts.firefox.com.cn www.youtube.com; connect-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com www.googletagmanager.com www.google-analytics.com region1.google-analytics.com logs.convertexperiments.com 1003350.metrics.convertexperiments.com 1003343.metrics.convertexperiments.com sentry.prod.mozaws.net o1069899.sentry.io o1069899.ingest.sentry.io https://accounts.firefox.com/ stage.cjms.nonprod.cloudops.mozgcp.net cjms.services.mozilla.com; frame-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com www.googletagmanager.com www.google-analytics.com www.youtube-nocookie.com trackertest.org www.surveygizmo.com accounts.firefox.com accounts.firefox.com.cn www.youtube.com; script-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com 'unsafe-inline' 'unsafe-eval' www.googletagmanager.com www.google-analytics.com tagmanager.google.com www.youtube.com s.ytimg.com cdn-3.convertexperiments.com app.convert.com data.track.convertexperiments.com 1003350.track.convertexperiments.com 1003343.track.convertexperiments.com; img-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com data: mozilla.org www.googletagmanager.com www.google-analytics.com adservice.google.com adservice.google.de adservice.google.dk creativecommons.org cdn-3.convertexperiments.com logs.convertexperiments.com images.ctfassets.net ad.doubleclick.net; style-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com 'unsafe-inline' app.convert.com; default-src 'self' *.mozilla.net *.mozilla.org *.mozilla.com; font-src 'self'
cache-control: max-age=600
expires: Wed, 30 Nov 2022 18:28:38 GMT
x-backend-server: bedrock-prod-web-b95bc569d-grd25.iowa-a
strict-transport-security: max-age=31536000
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
referrer-policy: strict-origin-when-cross-origin
via: 1.1 google, 1.1 6c90b631453c435bd0022caa657b67e8.cloudfront.net (CloudFront)
x-cache: Error from cloudfront
x-amz-cf-pop: SFO5-P2
x-amz-cf-id: A6-9mLztaE2tz840CbV9bXYiBMZRKEamDj6jGGEl1U7sg8egWfsDqg==
~ % curl -X OPTIONS https://example.com -i
HTTP/2 200
allow: OPTIONS, GET, HEAD, POST
cache-control: max-age=604800
content-type: text/html; charset=UTF-8
date: Wed, 30 Nov 2022 18:18:59 GMT
expires: Wed, 07 Dec 2022 18:18:59 GMT
server: EOS (vny/0451)
content-length: 0
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469973742,Make sure CORS works for write APIs,
https://github.com/simonw/datasette/issues/1922#issuecomment-1332504654,https://api.github.com/repos/simonw/datasette/issues/1922,1332504654,IC_kwDOBm6k_c5PbGhO,9599,simonw,2022-11-30T17:27:39Z,2022-11-30T17:27:39Z,OWNER,I'll test this once it's deployed to https://latest.datasette.io/,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469973742,Make sure CORS works for write APIs,
https://github.com/simonw/datasette/issues/1922#issuecomment-1332493004,https://api.github.com/repos/simonw/datasette/issues/1922,1332493004,IC_kwDOBm6k_c5PbDrM,9599,simonw,2022-11-30T17:18:10Z,2022-11-30T17:18:10Z,OWNER,"Here's why:
https://github.com/simonw/datasette/blob/4ddd77e51254bda3bac990ea662bac2e6b29c5e0/datasette/views/base.py#L71-L79
That's code in `BaseView` - but it turns out the code that adds CORS headers is in the `DataView` subclass of that (which the various write API endpoints do not use).
https://github.com/simonw/datasette/blob/4ddd77e51254bda3bac990ea662bac2e6b29c5e0/datasette/views/base.py#L158-L162","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469973742,Make sure CORS works for write APIs,
https://github.com/simonw/datasette/issues/1922#issuecomment-1332492092,https://api.github.com/repos/simonw/datasette/issues/1922,1332492092,IC_kwDOBm6k_c5PbDc8,9599,simonw,2022-11-30T17:17:21Z,2022-11-30T17:17:21Z,OWNER,I tried running `fetch()` with a POST from a separate domain and got a browser error because it did a GET against the `/db/-/create` endpoint and the 405 method not supported response did not include the CORS headers.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469973742,Make sure CORS works for write APIs,
https://github.com/simonw/datasette/issues/1605#issuecomment-1331694246,https://api.github.com/repos/simonw/datasette/issues/1605,1331694246,IC_kwDOBm6k_c5PYAqm,9599,simonw,2022-11-30T06:18:41Z,2022-11-30T06:18:41Z,OWNER,"Those sounds to me like they should be promoted to documented, supported internals.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1108671952,Scripted exports,
https://github.com/simonw/datasette/issues/1918#issuecomment-1331658629,https://api.github.com/repos/simonw/datasette/issues/1918,1331658629,IC_kwDOBm6k_c5PX3-F,9599,simonw,2022-11-30T05:21:51Z,2022-11-30T05:21:51Z,OWNER,"Much better:
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469044738,API explorer should list mutable databases first,
https://github.com/simonw/datasette/issues/1919#issuecomment-1331657404,https://api.github.com/repos/simonw/datasette/issues/1919,1331657404,IC_kwDOBm6k_c5PX3q8,9599,simonw,2022-11-30T05:19:43Z,2022-11-30T05:19:43Z,OWNER,"This is the test: https://github.com/simonw/datasette/blob/8404b21556d133c89eda4bd1bf5335ed9a0785d6/tests/test_api_write.py#L342-L401
I'm suspicious that there's a timing error of some sort but I can't think what it might be.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469062686,Intermittent `test_delete_row` test failure ,
https://github.com/simonw/datasette/issues/1916#issuecomment-1331651721,https://api.github.com/repos/simonw/datasette/issues/1916,1331651721,IC_kwDOBm6k_c5PX2SJ,9599,simonw,2022-11-30T05:10:27Z,2022-11-30T05:10:27Z,OWNER,"They should return 405 method not allowed with an `{""ok"":false, ""error"": ""Method not allowed""}` body.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469015001,GET requests against POST endpoints should not 500 error,
https://github.com/simonw/datasette/issues/1917#issuecomment-1331644751,https://api.github.com/repos/simonw/datasette/issues/1917,1331644751,IC_kwDOBm6k_c5PX0lP,9599,simonw,2022-11-30T04:59:22Z,2022-11-30T04:59:22Z,OWNER,"Yeah it looks like I introduced this bug here:
https://github.com/simonw/datasette/commit/fb7e70d5e72a951efe4b29ad999d8915c032d021","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469043836,Don't allow writable API to edit the `_memory` database,
https://github.com/simonw/datasette/issues/1917#issuecomment-1331644078,https://api.github.com/repos/simonw/datasette/issues/1917,1331644078,IC_kwDOBm6k_c5PX0au,9599,simonw,2022-11-30T04:58:06Z,2022-11-30T04:58:06Z,OWNER,"The problem might actually be here:
https://github.com/simonw/datasette/blob/9f5321ff1eca58c469a45cc406d7eb5ad05accbd/datasette/app.py#L280-L281
`is_mutable` defaults to `True`, so this line should probably be:
```python
self.add_database(Database(self, is_mutable=False, is_memory=True), name=""_memory"")
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1469043836,Don't allow writable API to edit the `_memory` database,
https://github.com/simonw/datasette/issues/1915#issuecomment-1331479606,https://api.github.com/repos/simonw/datasette/issues/1915,1331479606,IC_kwDOBm6k_c5PXMQ2,9599,simonw,2022-11-30T00:09:06Z,2022-11-30T00:09:06Z,OWNER,One last feature: I want to show an indication on the table page that the table has X seconds left to live.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468709531,Interactive demo of Datasette 1.0 write APIs,
https://github.com/simonw/datasette/issues/1915#issuecomment-1331479328,https://api.github.com/repos/simonw/datasette/issues/1915,1331479328,IC_kwDOBm6k_c5PXMMg,9599,simonw,2022-11-30T00:08:41Z,2022-11-30T00:08:41Z,OWNER,Five minute has now passed and https://latest.datasette.io/ephemeral/new_table is gone.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468709531,Interactive demo of Datasette 1.0 write APIs,
https://github.com/simonw/datasette/issues/1915#issuecomment-1331476246,https://api.github.com/repos/simonw/datasette/issues/1915,1331476246,IC_kwDOBm6k_c5PXLcW,9599,simonw,2022-11-30T00:04:35Z,2022-11-30T00:08:24Z,OWNER,"The new https://github.com/simonw/datasette-ephemeral-tables plugin is live now: https://latest.datasette.io/ephemeral - you have to navigate through https://latest.datasette.io/login-as-root first
It work! I created a table using https://latest.datasette.io/-/api#path=%2Fephemeral%2F-%2Fcreate&json=%7B%0A++%22table%22%3A+%22new_table%22%2C%0A++%22columns%22%3A+%5B%0A++++%7B%0A++++++%22name%22%3A+%22id%22%2C%0A++++++%22type%22%3A+%22integer%22%0A++++%7D%2C%0A++++%7B%0A++++++%22name%22%3A+%22name%22%2C%0A++++++%22type%22%3A+%22text%22%0A++++%7D%0A++%5D%2C%0A++%22pk%22%3A+%22id%22%0A%7D&method=POST
The table should vanish in a few minutes.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468709531,Interactive demo of Datasette 1.0 write APIs,
https://github.com/simonw/datasette/issues/1915#issuecomment-1331478611,https://api.github.com/repos/simonw/datasette/issues/1915,1331478611,IC_kwDOBm6k_c5PXMBT,9599,simonw,2022-11-30T00:07:37Z,2022-11-30T00:07:37Z,OWNER,"Then I created an API token at https://latest.datasette.io/-/create-token and ran this:
```
curl -XPOST 'https://latest.datasette.io/ephemeral/new_table/-/insert' \
-H 'Authorization: Bearer xxx' \
-H 'Content-Type: application/json' \
-d '{""row"": {""name"": ""NAME""}}'
```
And it inserted a row into https://latest.datasette.io/ephemeral/new_table","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468709531,Interactive demo of Datasette 1.0 write APIs,
https://github.com/simonw/datasette/issues/1915#issuecomment-1331432223,https://api.github.com/repos/simonw/datasette/issues/1915,1331432223,IC_kwDOBm6k_c5PXAsf,9599,simonw,2022-11-29T23:06:17Z,2022-11-29T23:06:17Z,OWNER,To (slightly) discourage abuse I'm going to make the demo database only visible to the root user - so people can't create tables with rude names and have them show to the public on https://latest.datasette.io/,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468709531,Interactive demo of Datasette 1.0 write APIs,
https://github.com/simonw/datasette/issues/1915#issuecomment-1331331082,https://api.github.com/repos/simonw/datasette/issues/1915,1331331082,IC_kwDOBm6k_c5PWoAK,9599,simonw,2022-11-29T21:24:59Z,2022-11-29T21:34:53Z,OWNER,Maybe a plugin called `datasette-temporary-tables` or `datasette-demo-tables` or `datasette-demo-database`.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468709531,Interactive demo of Datasette 1.0 write APIs,
https://github.com/simonw/datasette/issues/1850#issuecomment-1331238841,https://api.github.com/repos/simonw/datasette/issues/1850,1331238841,IC_kwDOBm6k_c5PWRe5,9599,simonw,2022-11-29T20:11:20Z,2022-11-29T20:11:20Z,OWNER,"Released this in Datasette 1.0a0:
- #1913","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1421529723,Write API in Datasette core,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331238029,https://api.github.com/repos/simonw/datasette/issues/1913,1331238029,IC_kwDOBm6k_c5PWRSN,9599,simonw,2022-11-29T20:10:35Z,2022-11-29T20:10:35Z,OWNER,"Released:
- https://pypi.org/project/datasette/1.0a0/
- https://docs.datasette.io/en/latest/changelog.html#a0-2022-11-29","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331226346,https://api.github.com/repos/simonw/datasette/issues/1913,1331226346,IC_kwDOBm6k_c5PWObq,9599,simonw,2022-11-29T20:00:16Z,2022-11-29T20:00:36Z,OWNER,"Looks like a fix is coming: https://github.com/pypa/twine/issues/940#issuecomment-1331225509
> > Note that `must_decode` was defined in `pkg_info/_compat.py`, and was thus never an API: before 1.9.0, it was only imported and used in `pkginfo/distribution.py'.
>
> Nevertheless, I will push out a 1.9.1 release of `pkginfo` which restores a deprecated compatibility alias.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331225277,https://api.github.com/repos/simonw/datasette/issues/1913,1331225277,IC_kwDOBm6k_c5PWOK9,9599,simonw,2022-11-29T19:59:14Z,2022-11-29T19:59:34Z,OWNER,I deleted the tag and tried creating a new release. Now running here: https://github.com/simonw/datasette/actions/runs/3577554546,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331216652,https://api.github.com/repos/simonw/datasette/issues/1913,1331216652,IC_kwDOBm6k_c5PWMEM,9599,simonw,2022-11-29T19:54:22Z,2022-11-29T19:54:22Z,OWNER,Filed a bug report here: https://bugs.launchpad.net/pkginfo/+bug/1998249,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331208206,https://api.github.com/repos/simonw/datasette/issues/1913,1331208206,IC_kwDOBm6k_c5PWKAO,9599,simonw,2022-11-29T19:51:31Z,2022-11-29T19:51:31Z,OWNER,https://pypi.org/project/pkginfo/#history - 1.9.0 came out 39 minutes ago!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331207334,https://api.github.com/repos/simonw/datasette/issues/1913,1331207334,IC_kwDOBm6k_c5PWJym,9599,simonw,2022-11-29T19:50:37Z,2022-11-29T19:50:37Z,OWNER,"https://pypi.org/project/setuptools/65.6.3/ came out most recently - 23rd November (wheel and twine are older).
No search results at all for that error message. This is very weird, I would have expected it to have been reported by now.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331205613,https://api.github.com/repos/simonw/datasette/issues/1913,1331205613,IC_kwDOBm6k_c5PWJXt,9599,simonw,2022-11-29T19:48:52Z,2022-11-29T19:48:52Z,OWNER,https://github.com/simonw/datasette/blob/07aad511769da9242260c850e8d975cbd8c29552/.github/workflows/publish.yml#L52-L61,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331204360,https://api.github.com/repos/simonw/datasette/issues/1913,1331204360,IC_kwDOBm6k_c5PWJEI,9599,simonw,2022-11-29T19:47:40Z,2022-11-29T19:47:40Z,OWNER,"... but the last step of the deploy failed, when it was meant to push to PyPI!
```
Uploading distributions to https://upload.pypi.org/legacy/
Traceback (most recent call last):
File ""/opt/hostedtoolcache/Python/3.11.0/x64/bin/twine"", line 8, in
sys.exit(main())
^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/__main__.py"", line 33, in main
error = cli.dispatch(sys.argv[1:])
^^^^^^^^^^^^^^^^^^^^^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/cli.py"", line 123, in dispatch
return main(args.args)
^^^^^^^^^^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/commands/upload.py"", line 198, in main
return upload(upload_settings, parsed_args.dists)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/commands/upload.py"", line 123, in upload
packages_to_upload = [
^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/commands/upload.py"", line 124, in
_make_package(filename, signatures, upload_settings) for filename in uploads
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/commands/upload.py"", line 77, in _make_package
package = package_file.PackageFile.from_filename(filename, upload_settings.comment)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/package.py"", line 96, in from_filename
meta = DIST_TYPES[dtype](filename)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/wheel.py"", line 42, in __init__
self.extractMetadata()
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/pkginfo/distribution.py"", line 121, in extractMetadata
self.parse(data)
File ""/opt/hostedtoolcache/Python/3.11.0/x64/lib/python3.11/site-packages/twine/wheel.py"", line 89, in parse
fp = io.StringIO(distribution.must_decode(data))
^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'pkginfo.distribution' has no attribute 'must_decode'. Did you mean: '_must_decode'?
Error: Process completed with exit code 1.
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1913#issuecomment-1331203997,https://api.github.com/repos/simonw/datasette/issues/1913,1331203997,IC_kwDOBm6k_c5PWI-d,9599,simonw,2022-11-29T19:47:13Z,2022-11-29T19:47:13Z,OWNER,"Weird, retrying the tests DID get them to pass. https://github.com/simonw/datasette/actions/runs/3577355358/jobs/6016518244","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468603401,Release Datasette 1.0a0,
https://github.com/simonw/datasette/issues/1877#issuecomment-1331201207,https://api.github.com/repos/simonw/datasette/issues/1877,1331201207,IC_kwDOBm6k_c5PWIS3,9599,simonw,2022-11-29T19:44:07Z,2022-11-29T19:44:07Z,OWNER,"I fixed the duplicate logic issue here: https://github.com/simonw/datasette/commit/ee64130fa8a5ff4a24791916c696e10cf2375102
- #1896
Decided not to address `views/table.py`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1432012302,Refactor and tidy up final write API code,
https://github.com/simonw/datasette/pull/1912#issuecomment-1331196531,https://api.github.com/repos/simonw/datasette/issues/1912,1331196531,IC_kwDOBm6k_c5PWHJz,9599,simonw,2022-11-29T19:39:10Z,2022-11-29T19:39:10Z,OWNER,"Annoyingly it looks like I can't rebase this one, and I don't want to squash-merge and lose the commits, so I'm going to do a regular merge instead.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468592292,Merge 1.0-dev (with initial write API) back into main,
https://github.com/simonw/datasette/issues/1891#issuecomment-1331181922,https://api.github.com/repos/simonw/datasette/issues/1891,1331181922,IC_kwDOBm6k_c5PWDli,9599,simonw,2022-11-29T19:23:41Z,2022-11-29T19:23:41Z,OWNER,https://github.com/simonw/datasette/blob/4d49a5a39739476e1ada43f70a0029abcef07977/docs/changelog.rst#10a0-2022-11-29,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450303205,1.0a0 release notes,
https://github.com/simonw/datasette/issues/1891#issuecomment-1331143292,https://api.github.com/repos/simonw/datasette/issues/1891,1331143292,IC_kwDOBm6k_c5PV6J8,9599,simonw,2022-11-29T18:57:40Z,2022-11-29T18:57:40Z,OWNER,I'm going to keep these short - they'll mostly be links to the documentation for the new features.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450303205,1.0a0 release notes,
https://github.com/simonw/datasette/issues/1891#issuecomment-1331140747,https://api.github.com/repos/simonw/datasette/issues/1891,1331140747,IC_kwDOBm6k_c5PV5iL,9599,simonw,2022-11-29T18:55:42Z,2022-11-29T18:55:42Z,OWNER,"All features for the alpha are complete now. Release notes should be based on these commits:
https://github.com/simonw/datasette/compare/0.63.2...6bda2257868a2cbd70b84b7a86a5bcb47dcc4874","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450303205,1.0a0 release notes,
https://github.com/simonw/datasette/issues/1911#issuecomment-1331135709,https://api.github.com/repos/simonw/datasette/issues/1911,1331135709,IC_kwDOBm6k_c5PV4Td,9599,simonw,2022-11-29T18:50:58Z,2022-11-29T18:50:58Z,OWNER,Updated docs: https://docs.datasette.io/en/1.0-dev/json_api.html#creating-a-table,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468519699,`/db/-/create` should support creating tables with compound primary keys,
https://github.com/simonw/datasette/issues/1911#issuecomment-1331120055,https://api.github.com/repos/simonw/datasette/issues/1911,1331120055,IC_kwDOBm6k_c5PV0e3,9599,simonw,2022-11-29T18:36:01Z,2022-11-29T18:36:01Z,OWNER,"Current API design:
```
POST //-/create
```
```json
{
""table"": ""name_of_new_table"",
""columns"": [
{
""name"": ""id"",
""type"": ""integer""
},
{
""name"": ""title"",
""type"": ""text""
}
],
""pk"": ""id""
}
```
I'm going to add a new `""pks""` key which is a list, and can be used in place of `""pk""`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1468519699,`/db/-/create` should support creating tables with compound primary keys,
https://github.com/simonw/datasette/issues/1863#issuecomment-1331089156,https://api.github.com/repos/simonw/datasette/issues/1863,1331089156,IC_kwDOBm6k_c5PVs8E,9599,simonw,2022-11-29T18:08:53Z,2022-11-29T18:08:53Z,OWNER,"I do think this needs type checking - I just tried and you really can send a string to an integer column and have it work, which feels bad.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1863#issuecomment-1330974099,https://api.github.com/repos/simonw/datasette/issues/1863,1330974099,IC_kwDOBm6k_c5PVQ2T,9599,simonw,2022-11-29T17:03:00Z,2022-11-29T17:11:05Z,OWNER,I've decided that I won't do that validation for the first version of this - I'm going to teach `dclient` to send the correct types instead: https://github.com/simonw/dclient/issues/6#issuecomment-1330963953,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1909#issuecomment-1329624931,https://api.github.com/repos/simonw/datasette/issues/1909,1329624931,IC_kwDOBm6k_c5PQHdj,9599,simonw,2022-11-28T19:19:26Z,2022-11-28T19:19:26Z,OWNER,The list of states here is a good example of somewhere this might be useful: https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=state&_facet_size=max,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1466952626,Option to sort facets alphabetically,
https://github.com/simonw/datasette/issues/1605#issuecomment-1328169472,https://api.github.com/repos/simonw/datasette/issues/1605,1328169472,IC_kwDOBm6k_c5PKkIA,9599,simonw,2022-11-27T04:32:14Z,2022-11-27T04:32:14Z,OWNER,@eyeseast I started work on that plugin: https://github.com/simonw/datasette-export,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1108671952,Scripted exports,
https://github.com/simonw/datasette/issues/1863#issuecomment-1324539030,https://api.github.com/repos/simonw/datasette/issues/1863,1324539030,IC_kwDOBm6k_c5O8tyW,9599,simonw,2022-11-23T04:35:14Z,2022-11-23T04:35:14Z,OWNER,If I do that I should probably update `insert` to do those validation checks as well.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1863#issuecomment-1324531750,https://api.github.com/repos/simonw/datasette/issues/1863,1324531750,IC_kwDOBm6k_c5O8sAm,9599,simonw,2022-11-23T04:20:47Z,2022-11-23T04:20:47Z,OWNER,"... which does imply that I'm going to do an extra layer of validation over what SQLite provides. SQLite will happily allow a text string to be added to a supposedly integer column. I'm not going to allow that - I'll return a validation error instead, unless the string can be safely coerced to the correct type.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1863#issuecomment-1324531085,https://api.github.com/repos/simonw/datasette/issues/1863,1324531085,IC_kwDOBm6k_c5O8r2N,9599,simonw,2022-11-23T04:19:28Z,2022-11-23T04:19:28Z,OWNER,Had a design conversation with myself in https://github.com/simonw/dclient/issues/6 where I decided that the API should allow string values to be sent to integer columns which would be automatically converted *if possible to do so* - as an API usability feature.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1903#issuecomment-1321262142,https://api.github.com/repos/simonw/datasette/issues/1903,1321262142,IC_kwDOBm6k_c5OwNw-,9599,simonw,2022-11-20T22:35:01Z,2022-11-20T22:35:01Z,OWNER,A want to call this `datasette/exceptions.py` inspired by Takahē: https://github.com/andrewgodwin/takahe/blob/f491fdb56e2de9200e14b855b5576009ca99dfa5/core/exceptions.py,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1455928469,Refactor all error classes into a datasette.exceptions module,
https://github.com/simonw/datasette/issues/1905#issuecomment-1320721241,https://api.github.com/repos/simonw/datasette/issues/1905,1320721241,IC_kwDOBm6k_c5OuJtZ,9599,simonw,2022-11-19T01:12:05Z,2022-11-19T01:12:05Z,OWNER,Used it to deploy this: https://fivethirtyeight.datasettes.com/-/versions,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1456012874,`publish heroku` failing due to old Python version,
https://github.com/simonw/datasette/issues/1905#issuecomment-1320689643,https://api.github.com/repos/simonw/datasette/issues/1905,1320689643,IC_kwDOBm6k_c5OuB_r,9599,simonw,2022-11-19T00:17:19Z,2022-11-19T00:41:54Z,OWNER,"The tests don't cover this bit at the moment.
Would be easier to write tests if there was a `--generate-dir` option as seen in https://datasette.io/plugins/datasette-publish-vercel","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1456012874,`publish heroku` failing due to old Python version,
https://github.com/simonw/datasette/issues/1905#issuecomment-1320706596,https://api.github.com/repos/simonw/datasette/issues/1905,1320706596,IC_kwDOBm6k_c5OuGIk,9599,simonw,2022-11-19T00:41:44Z,2022-11-19T00:41:44Z,OWNER,"Tested a deploy:
```
% datasette publish heroku fixtures.db -n datasette-issue-1905
› Warning: heroku update available from 7.63.0 to 7.66.4.
› Warning: heroku update available from 7.63.0 to 7.66.4.
› Warning: heroku update available from 7.63.0 to 7.66.4.
Creating datasette-issue-1905... done
› Warning: heroku update available from 7.63.0 to 7.66.4.
▸ Couldn't detect GNU tar. Builds could fail due to decompression errors
▸ See https://devcenter.heroku.com/articles/platform-api-deploying-slugs#create-slug-archive
▸ Please install it, or specify the '--tar' option
▸ Falling back to node's built-in compressor
-----> Building on the Heroku-22 stack
-----> Determining which buildpack to use for this app
-----> Python app detected
-----> Using Python version specified in runtime.txt
-----> Installing python-3.11.0
-----> Installing pip 22.3.1, setuptools 63.4.3 and wheel 0.37.1
-----> Installing SQLite3
-----> Installing requirements with pip
Collecting datasette
Downloading datasette-0.63.1-py3-none-any.whl (231 kB)
...
-----> Running post-compile hook
-----> Discovering process types
Procfile declares types -> web
-----> Compressing...
Done: 28M
-----> Launching...
Released v3
https://datasette-issue-1905.herokuapp.com/ deployed to Heroku
Starting November 28th, 2022, free Heroku Dynos, free Heroku Postgres, and free Heroku Data for Redis® will no longer be available.
If you have apps using any of these resources, you must upgrade to paid plans by this date to ensure your apps continue to run and to retain your data. For students, we will announce a new program by the end of September. Learn more at https://blog.heroku.com/next-chapter
```
I had to then pay for the dino because I'd run out of free hours.
https://datasette-issue-1905.herokuapp.com/-/versions shows:
```json
{
""python"": {
""version"": ""3.11.0"",
""full"": ""3.11.0 (main, Oct 24 2022, 21:34:02) [GCC 11.2.0]""
},
""datasette"": {
""version"": ""0.63.1""
}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1456012874,`publish heroku` failing due to old Python version,
https://github.com/simonw/datasette/issues/1905#issuecomment-1320678715,https://api.github.com/repos/simonw/datasette/issues/1905,1320678715,IC_kwDOBm6k_c5Ot_U7,9599,simonw,2022-11-19T00:02:28Z,2022-11-19T00:02:28Z,OWNER,This is a strong argument for extracting the Heroku support out to a plugin - it would allow this to be fixed with a plugin release without needing to push a full release of Datasette itself.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1456012874,`publish heroku` failing due to old Python version,
https://github.com/simonw/datasette/issues/1891#issuecomment-1320625260,https://api.github.com/repos/simonw/datasette/issues/1891,1320625260,IC_kwDOBm6k_c5OtyRs,9599,simonw,2022-11-18T23:01:03Z,2022-11-18T23:01:48Z,OWNER,I think this actually needs to include a whole section of the documentation about the road to 1.0 - what to expect (planned breaking changes) etc. I can add that to the https://docs.datasette.io/en/stable/contributing.html page perhaps - or even create a Roadmap page.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450303205,1.0a0 release notes,
https://github.com/simonw/datasette/issues/1896#issuecomment-1320616559,https://api.github.com/repos/simonw/datasette/issues/1896,1320616559,IC_kwDOBm6k_c5OtwJv,9599,simonw,2022-11-18T22:51:14Z,2022-11-18T22:51:14Z,OWNER,New methods are documented here: https://docs.datasette.io/en/1.0-dev/internals.html#resolve-database-request,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452364777,Extract logic for resolving a URL to a database / table / row,
https://github.com/simonw/datasette/issues/1903#issuecomment-1320614541,https://api.github.com/repos/simonw/datasette/issues/1903,1320614541,IC_kwDOBm6k_c5OtvqN,9599,simonw,2022-11-18T22:47:41Z,2022-11-18T22:47:41Z,OWNER,"When I do this it's important to update the documentation for `resolve_database()` and the like:
https://github.com/simonw/datasette/blob/ee64130fa8a5ff4a24791916c696e10cf2375102/docs/internals.rst#L594","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1455928469,Refactor all error classes into a datasette.exceptions module,
https://github.com/simonw/datasette/issues/1896#issuecomment-1320588299,https://api.github.com/repos/simonw/datasette/issues/1896,1320588299,IC_kwDOBm6k_c5OtpQL,9599,simonw,2022-11-18T22:16:59Z,2022-11-18T22:17:06Z,OWNER,"Found myself needing an `await db.view_exists()` method for this, similar to the existing `await db.table_exists()` one.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452364777,Extract logic for resolving a URL to a database / table / row,
https://github.com/simonw/datasette/issues/1896#issuecomment-1317757949,https://api.github.com/repos/simonw/datasette/issues/1896,1317757949,IC_kwDOBm6k_c5Oi2P9,9599,simonw,2022-11-16T22:27:47Z,2022-11-18T21:48:29Z,OWNER,"Open question: should `resolve_table()` know how to identify named canned queries too?
I think not, at least for the moment. Feels a bit too specialist to expose in a documented API.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452364777,Extract logic for resolving a URL to a database / table / row,
https://github.com/simonw/datasette/issues/1863#issuecomment-1320563197,https://api.github.com/repos/simonw/datasette/issues/1863,1320563197,IC_kwDOBm6k_c5OtjH9,9599,simonw,2022-11-18T21:47:35Z,2022-11-18T21:48:07Z,OWNER,"Incomplete implementation of this view:
```python
class RowUpdateView(BaseView):
name = ""row-update""
def __init__(self, datasette):
self.ds = datasette
async def post(self, request):
database_route = tilde_decode(request.url_vars[""database""])
table = tilde_decode(request.url_vars[""table""])
try:
db = self.ds.get_database(route=database_route)
except KeyError:
return _error([""Database not found: {}"".format(database_route)], 404)
database_name = db.name
if not await db.table_exists(table):
return _error([""Table not found: {}"".format(table)], 404)
pk_values = urlsafe_components(request.url_vars[""pks""])
sql, params, pks = await row_sql_params_pks(db, table, pk_values)
results = await db.execute(sql, params, truncate=True)
rows = list(results.rows)
if not rows:
return _error([f""Record not found: {pk_values}""], 404)
# Ensure user has permission to update this row
if not await self.ds.permission_allowed(
request.actor, ""update-row"", resource=(database_name, table)
):
return _error([""Permission denied""], 403)
body = await request.post_body()
try:
data = json.loads(body)
except json.JSONDecodeError as e:
return _error([""Invalid JSON: {}"".format(e)])
if not isinstance(data, dict):
return _error([""JSON must be a dictionary""])
def update_row(conn):
sqlite_utils.Database(conn)[table].update(pk_values, updates)
await db.execute_write_fn(update_row)
result = {""ok"": True}
if data.get(""return""):
result[""row""] = {""row-here"": ""TODO""}
return Response.json(result, status=200)
```
This is before the refactor in:
- #1896","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/pull/1898#issuecomment-1319642535,https://api.github.com/repos/simonw/datasette/issues/1898,1319642535,IC_kwDOBm6k_c5OqCWn,9599,simonw,2022-11-18T07:28:45Z,2022-11-18T07:28:45Z,OWNER,Thanks!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452485922,Use DOMContentLoaded instead of load event for CodeMirror initialization,
https://github.com/simonw/datasette/issues/1899#issuecomment-1319642338,https://api.github.com/repos/simonw/datasette/issues/1899,1319642338,IC_kwDOBm6k_c5OqCTi,9599,simonw,2022-11-18T07:28:28Z,2022-11-18T07:28:28Z,OWNER,Demo: https://latest.datasette.io/fixtures,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452495049,Clicking within the CodeMirror area below the SQL (i.e. when there's only a single line) doesn't cause the editor to get focused ,
https://github.com/simonw/datasette/issues/1901#issuecomment-1319525520,https://api.github.com/repos/simonw/datasette/issues/1901,1319525520,IC_kwDOBm6k_c5OplyQ,9599,simonw,2022-11-18T04:21:14Z,2022-11-18T07:22:37Z,OWNER,This search helps too: [https://ripgrep.datasette.io/-/ripgrep?pattern=%7B%25+block+nav&literal=on&ignore=on&glob=%21datasette%2F**](https://ripgrep.datasette.io/-/ripgrep?pattern=%7B%25+block+nav&literal=on&ignore=on&glob=%21datasette%2F**),"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1900#issuecomment-1319631421,https://api.github.com/repos/simonw/datasette/issues/1900,1319631421,IC_kwDOBm6k_c5Op_o9,9599,simonw,2022-11-18T07:13:00Z,2022-11-18T07:13:00Z,OWNER,"You get:
```
=> [internal] load metadata for docker.io/library/python:3.11.0-slim-bullseye 0.9s
=> [internal] load build context 2.3s
=> => transferring context: 72.38MB 2.3s
=> CACHED [1/6] FROM docker.io/library/python:3.11.0-slim-bullseye@sha256:1cd45c5dad845af18d71745c017325725dc979571c1bbe625b67e6051533716c 0.0s
```
I get:
```
=> [internal] load metadata for docker.io/library/python:3.11.0-slim-bullseye 1.0s
=> [internal] load build context 0.0s
=> => transferring context: 705B 0.0s
=> CACHED [1/6] FROM docker.io/library/python:3.11.0-slim-bullseye@sha256:1cd45c5dad845af18d71745c017325725dc979571c1bbe625b67e6051533716c 0.0s
```
Both the image name and the hash are _exactly_ the same. So why are you getting an error while mine works OK?
For my machine:
```
~ % docker --version
Docker version 20.10.12, build e91ed57
~ % uname -a
Darwin Simons-MacBook-Pro-2.local 22.1.0 Darwin Kernel Version 22.1.0: Sun Oct 9 20:14:54 PDT 2022; root:xnu-8792.41.9~2/RELEASE_X86_64 x86_64
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1900#issuecomment-1319629469,https://api.github.com/repos/simonw/datasette/issues/1900,1319629469,IC_kwDOBm6k_c5Op_Kd,9599,simonw,2022-11-18T07:10:17Z,2022-11-18T07:10:17Z,OWNER,This is so weird! What version of Datasette do you get from `datasette --version` there - and what's your Docker version / operating system version?,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1901#issuecomment-1319627012,https://api.github.com/repos/simonw/datasette/issues/1901,1319627012,IC_kwDOBm6k_c5Op-kE,9599,simonw,2022-11-18T07:07:03Z,2022-11-18T07:07:03Z,OWNER,"Here's the full list of 10 plugin releases for this issue:
* [datasette-search-all 1.1.1](https://github.com/simonw/datasette-search-all/releases/tag/1.1.1)
* [datasette-ripgrep 0.7.1](https://github.com/simonw/datasette-ripgrep/releases/tag/0.7.1)
* [datasette-socrata 0.3.1](https://github.com/simonw/datasette-socrata/releases/tag/0.3.1)
* [datasette-configure-fts 1.1.1](https://github.com/simonw/datasette-configure-fts/releases/tag/1.1.1)
* [datasette-edit-templates 0.2](https://github.com/simonw/datasette-edit-templates/releases/tag/0.2)
* [datasette-copyable 0.3.2](https://github.com/simonw/datasette-copyable/releases/tag/0.3.2)
* [datasette-public 0.2.1](https://github.com/simonw/datasette-public/releases/tag/0.2.1)
* [datasette-import-table 0.3.1](https://github.com/simonw/datasette-import-table/releases/tag/0.3.1)
* [datasette-indieauth 1.2.2](https://github.com/simonw/datasette-indieauth/releases/tag/1.2.2)
* [datasette-edit-schema 0.5.2](https://github.com/simonw/datasette-edit-schema/releases/tag/0.5.2)","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1901#issuecomment-1319493475,https://api.github.com/repos/simonw/datasette/issues/1901,1319493475,IC_kwDOBm6k_c5Opd9j,9599,simonw,2022-11-18T03:19:25Z,2022-11-18T07:03:03Z,OWNER,"Other plugins this looks like it will affect:
- [x] `datasette-ripgrep` https://github.com/simonw/datasette-ripgrep/blob/03446464420130368582022eeb5944993f64ec8f/datasette_ripgrep/templates/ripgrep.html#L37-L42
- [x] `datasette-socrata` https://github.com/simonw/datasette-socrata/blob/32fb256a461bf0e790eca10bdc7dd9d96c20f7c4/datasette_socrata/templates/datasette_socrata_error.html#L5-L10
- [x] `datasette-configure-fts` https://github.com/simonw/datasette-configure-fts/blob/eca742e5d4b9190fc22d68bc0a406c575e6d09a0/datasette_configure_fts/templates/configure_fts_database.html#L9-L14
- [x] `datasette-edit-templates` https://github.com/simonw/datasette-edit-templates/blob/f772aff4a2a4080c949746668a8ec6302dbeb0d9/datasette_edit_templates/templates/edit_template.html#L17-L23
- [x] `datasette-copyable` https://github.com/simonw/datasette-copyable/blob/204d5c912a8d48c49155c67fba7339d4bb26ab9a/datasette_copyable/templates/copyable.html#L36-L43
- [x] `datasette-public` https://github.com/simonw/datasette-public/blob/32b6a0ba53bd5714b6b41eddd8705b213c105efc/datasette_public/templates/public_table_change_privacy.html#L5-L11
- [x] `datasette-import-table` https://github.com/simonw/datasettecloud-datasette/blob/37d0fe525c6649c1aec3d1ee8bc35a684570e87f/templates/import_data.html#L5-L10
- [x] `datasette-edit-schema` (three places)
- [x] `datasette-indieauth` https://github.com/simonw/datasette-indieauth/blob/a08ce67ddad6098b1240adbeff37d040e4df53b1/datasette_indieauth/templates/indieauth.html#L5-L10","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1901#issuecomment-1319623911,https://api.github.com/repos/simonw/datasette/issues/1901,1319623911,IC_kwDOBm6k_c5Op9zn,9599,simonw,2022-11-18T07:02:56Z,2022-11-18T07:02:56Z,OWNER,That's all of them!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1901#issuecomment-1319588163,https://api.github.com/repos/simonw/datasette/issues/1901,1319588163,IC_kwDOBm6k_c5Op1FD,9599,simonw,2022-11-18T06:05:11Z,2022-11-18T06:05:11Z,OWNER,"For `datasette-copyable` I want to show breadcrumbs that take database/instance permissions into account, so I'm removing `{% block nav %}` entirely and replacing it with this:
```html+jinja
{% block crumbs %}
{{ crumbs.nav(request=request, database=database, table=table) }}
{% endblock %}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1899#issuecomment-1319584553,https://api.github.com/repos/simonw/datasette/issues/1899,1319584553,IC_kwDOBm6k_c5Op0Mp,9599,simonw,2022-11-18T06:00:10Z,2022-11-18T06:01:50Z,OWNER,"I can't actually remember where that `min-height: 70px` came from. I just tried without it and it seems fine - especially since any time you add a newline in the editor it increases its height to fit.
I ran this in the DevTools console:
```javascript
document.querySelector('.cm-editor').style.minHeight = 'none';
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452495049,Clicking within the CodeMirror area below the SQL (i.e. when there's only a single line) doesn't cause the editor to get focused ,
https://github.com/simonw/datasette/issues/1900#issuecomment-1319583703,https://api.github.com/repos/simonw/datasette/issues/1900,1319583703,IC_kwDOBm6k_c5Opz_X,9599,simonw,2022-11-18T05:58:31Z,2022-11-18T05:58:31Z,OWNER,Could you provide full steps to reproduce plus a SpatiaLite database file that triggered this for you? I'm not able to recreate the problem.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1900#issuecomment-1319583281,https://api.github.com/repos/simonw/datasette/issues/1900,1319583281,IC_kwDOBm6k_c5Opz4x,9599,simonw,2022-11-18T05:57:44Z,2022-11-18T05:57:44Z,OWNER,"Did you use the `--spatialite` option?
I just tried this:
datasette package nps-spatialite.db
It built the image OK (I didn't see the error you reported), but running the container failed with an error:
```
/tmp % docker run -p 8001:8001 7298e8e6bbfb
Usage: datasette serve [OPTIONS] [FILES]...
Try 'datasette serve --help' for help.
Error: It looks like you're trying to load a SpatiaLite database without first loading the SpatiaLite module.
Read more: https://docs.datasette.io/en/stable/spatialite.html
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1900#issuecomment-1319582239,https://api.github.com/repos/simonw/datasette/issues/1900,1319582239,IC_kwDOBm6k_c5Opzof,9599,simonw,2022-11-18T05:55:38Z,2022-11-18T05:55:38Z,OWNER,"Trying this out locally with this 69MB SpatiaLite file I happened to have lying around (from testing `shapefile-to-sqlite` a while ago): https://static.simonwillison.net/static/2022/nps-spatialite.db
```
% datasette package nps-spatialite.db --spatialite
...
=> [2/6] COPY . /app 0.4s
=> [3/6] WORKDIR /app 0.0s
=> [4/6] RUN apt-get update && apt-get install -y python3-dev gcc libsqlite3-mod-spatialite && rm -rf /var/lib/apt/lists/* 29.6s
=> [5/6] RUN pip install -U datasette 12.0s
=> [6/6] RUN datasette inspect nps-spatialite.db --inspect-file inspect-data.json 2.6s
=> exporting to image 3.0s
=> => exporting layers 3.0s
=> => writing image sha256:4dfef1c373c5c057ef7ac22344f834d522acef24313a1b25d2eba9e500066b8f 0.0s
```
And then:
docker run -p 8001:8001 4dfef1c373c5
This worked fine for me. I ran `datasette package` using Datasette 0.63.1.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1900#issuecomment-1319574972,https://api.github.com/repos/simonw/datasette/issues/1900,1319574972,IC_kwDOBm6k_c5Opx28,9599,simonw,2022-11-18T05:41:28Z,2022-11-18T05:41:28Z,OWNER,Oh this is with `datasette package`? That should work. Will investigate.,"{""total_count"": 1, ""+1"": 1, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1900#issuecomment-1319571220,https://api.github.com/repos/simonw/datasette/issues/1900,1319571220,IC_kwDOBm6k_c5Opw8U,9599,simonw,2022-11-18T05:34:35Z,2022-11-18T05:34:35Z,OWNER,Which Docker image are you using here? It looks like it's missing SpatiaLite from the image.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452572348,datasette package --spatialite throws error during build,
https://github.com/simonw/datasette/issues/1897#issuecomment-1319570586,https://api.github.com/repos/simonw/datasette/issues/1897,1319570586,IC_kwDOBm6k_c5Opwya,9599,simonw,2022-11-18T05:33:20Z,2022-11-18T05:33:20Z,OWNER,"One of the big changes still left to do for Datasette 1.0 is to unify the JSON representation with the context psssed to the templates (via an `?_extra=` mechanism to add extra context needed by the HTML templates), because a goal for 1.0 is for the template context to be a documented API contract such that custom templates won't break with future releases.
As such I expect to do quite a bit of refactoring and cleanup on how the template context works later on.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1901#issuecomment-1319528359,https://api.github.com/repos/simonw/datasette/issues/1901,1319528359,IC_kwDOBm6k_c5Opmen,9599,simonw,2022-11-18T04:27:00Z,2022-11-18T04:27:00Z,OWNER,Also `datasette-indieauth` https://github.com/simonw/datasette-indieauth/blob/a08ce67ddad6098b1240adbeff37d040e4df53b1/datasette_indieauth/templates/indieauth.html#L5-L10,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1901#issuecomment-1319483555,https://api.github.com/repos/simonw/datasette/issues/1901,1319483555,IC_kwDOBm6k_c5Opbij,9599,simonw,2022-11-18T03:02:35Z,2022-11-18T03:02:35Z,OWNER,Looks like this issue could affect a bunch of other plugins too: https://cs.github.com/?scopeName=All+repos&scope=&q=%3Cp+class%3D%22crumbs%22%3E+user%3Asimonw,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1901#issuecomment-1319482791,https://api.github.com/repos/simonw/datasette/issues/1901,1319482791,IC_kwDOBm6k_c5OpbWn,9599,simonw,2022-11-18T03:01:36Z,2022-11-18T03:01:36Z,OWNER,"Good catch. Looks like that bug was introduced by this change: https://github.com/simonw/datasette/commit/1a5e5f2aa951e5bd731067a49819efba68fbe8ef
From:
- https://github.com/simonw/datasette/issues/1831
The search all plugin includes this code which interacts poorly with that refactor:
https://github.com/simonw/datasette-search-all/blob/847b55c368a285e4567627029624d7872ee75cac/datasette_search_all/templates/search_all.html#L31-L36
```html+jinja
{% block nav %}
home
{{ super() }}
{% endblock %}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1453813400,"Some plugins show ""home"" breadcrumbs twice in the top left",
https://github.com/simonw/datasette/issues/1897#issuecomment-1319478811,https://api.github.com/repos/simonw/datasette/issues/1897,1319478811,IC_kwDOBm6k_c5OpaYb,9599,simonw,2022-11-18T02:53:57Z,2022-11-18T02:53:57Z,OWNER,"I decided to just go for the view names, not their columns.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1897#issuecomment-1319477721,https://api.github.com/repos/simonw/datasette/issues/1897,1319477721,IC_kwDOBm6k_c5OpaHZ,9599,simonw,2022-11-18T02:51:40Z,2022-11-18T02:51:40Z,OWNER,Views aren't currently available in the `_internal` schema.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1897#issuecomment-1319435374,https://api.github.com/repos/simonw/datasette/issues/1897,1319435374,IC_kwDOBm6k_c5OpPxu,9599,simonw,2022-11-18T01:33:30Z,2022-11-18T01:33:30Z,OWNER,"Just noticed that this isn't including views, which it should.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1897#issuecomment-1319401843,https://api.github.com/repos/simonw/datasette/issues/1897,1319401843,IC_kwDOBm6k_c5OpHlz,9599,simonw,2022-11-18T00:42:03Z,2022-11-18T00:42:23Z,OWNER,"This function works even if the SQLite JSON functions are not available:
```python
async def _table_columns(datasette, database_name):
internal = datasette.get_database(""_internal"")
result = await internal.execute(
""select table_name, name from columns where database_name = ?"",
[database_name],
)
table_columns = {}
for row in result.rows:
table_columns.setdefault(row[""table_name""], []).append(row[""name""])
return table_columns
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1897#issuecomment-1317840727,https://api.github.com/repos/simonw/datasette/issues/1897,1317840727,IC_kwDOBm6k_c5OjKdX,9599,simonw,2022-11-16T23:57:52Z,2022-11-16T23:57:52Z,OWNER,In terms of permissions: if you have `execute-sql` permission for a database then it's OK for you to see the table columns for that database.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1897#issuecomment-1317839781,https://api.github.com/repos/simonw/datasette/issues/1897,1317839781,IC_kwDOBm6k_c5OjKOl,9599,simonw,2022-11-16T23:56:47Z,2022-11-16T23:56:47Z,OWNER,I'm going to call this `table_columns` in the template context (because `schema` might mean `CREATE TABLE ...`.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/issues/1897#issuecomment-1317838892,https://api.github.com/repos/simonw/datasette/issues/1897,1317838892,IC_kwDOBm6k_c5OjKAs,9599,simonw,2022-11-16T23:55:42Z,2022-11-16T23:55:42Z,OWNER,"Here's where the schema is hard-coded at the moment: https://github.com/simonw/datasette/blob/00e233d7a7f6443cb95fb5227c23580c48551cad/datasette/templates/_codemirror_foot.html#L2-L7
I figured out how to extract that data from the `_internal` table in this comment: https://github.com/simonw/datasette/pull/1893#issuecomment-1317475720
Although that used JSON functions which may (in a real edge-case) not be available in the version of SQLite that Datasette is running on, so probably going to use a regular SQL query and then assemble the JSON separately.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452457263,Serve schema JSON to the SQL editor to enable autocomplete,
https://github.com/simonw/datasette/pull/1893#issuecomment-1317837416,https://api.github.com/repos/simonw/datasette/issues/1893,1317837416,IC_kwDOBm6k_c5OjJpo,9599,simonw,2022-11-16T23:54:02Z,2022-11-16T23:54:02Z,OWNER,"I'm going to tackle #1897 in the next few minutes.
Tests failed due to Prettier check, just pushed a fix so it would ignore `.bundle.js` too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317831555,https://api.github.com/repos/simonw/datasette/issues/1893,1317831555,IC_kwDOBm6k_c5OjIOD,9599,simonw,2022-11-16T23:47:13Z,2022-11-16T23:47:13Z,OWNER,I'll open a follow-up issue to fix the schema.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317831425,https://api.github.com/repos/simonw/datasette/issues/1893,1317831425,IC_kwDOBm6k_c5OjIMB,9599,simonw,2022-11-16T23:47:05Z,2022-11-16T23:47:05Z,OWNER,"OK, let's do it! Thanks so much for this.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317829214,https://api.github.com/repos/simonw/datasette/issues/1893,1317829214,IC_kwDOBm6k_c5OjHpe,9599,simonw,2022-11-16T23:44:36Z,2022-11-16T23:44:36Z,OWNER,Deployed that to https://datasette-pr-1893.vercel.app/fixtures - looks good to me!,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317797044,https://api.github.com/repos/simonw/datasette/issues/1893,1317797044,IC_kwDOBm6k_c5Oi_y0,9599,simonw,2022-11-16T23:08:34Z,2022-11-16T23:08:34Z,OWNER,"> I can push up a commit that uses the static fixtures schema for testing, but given that the query used to generate it is authed we would still need some work to make that work on live data, right?
Yeah, push that up. I'm happy to wire in the query right after we land this.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/issues/1896#issuecomment-1317757112,https://api.github.com/repos/simonw/datasette/issues/1896,1317757112,IC_kwDOBm6k_c5Oi2C4,9599,simonw,2022-11-16T22:26:52Z,2022-11-16T22:33:13Z,OWNER,"Some ideas from walking the dog:
Challenge: standard URL routing of request to database/table/row
Standardize on the named components of the URL patterns - `database`, `table`, `pks`
Async function that takes the request and the Datasette instance and returns a Resolved instance with:
```
.level - database or table or row (better name?)
.database - the name of the database
.db - the database object
.table - the name of the table (or view)
.is_view perhaps?
.pk_values if it's a row
```
Should this attempt to resolve names queries too?
```
.where_sql - the where fragment you use
.where_params - accompanying dictionary
await datasette.resolve_request(request)
```
Or even better three methods:
```python
datasette.resolve_database(request)
datasette.resolve_table(request)
datasette.resolve_row(request)
```
These can be typed correctly
Methods raise `NotFound` if not found","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1452364777,Extract logic for resolving a URL to a database / table / row,
https://github.com/simonw/datasette/issues/1863#issuecomment-1317755263,https://api.github.com/repos/simonw/datasette/issues/1863,1317755263,IC_kwDOBm6k_c5Oi1l_,9599,simonw,2022-11-16T22:24:59Z,2022-11-16T22:24:59Z,OWNER,"In trying to write this I realize that there's a lot of duplicated code with delete row, specifically around resolving the incoming URL into a row (or a database or a table).
Since this is so common, I think it's worth extracting the logic out first.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/pull/1893#issuecomment-1317746206,https://api.github.com/repos/simonw/datasette/issues/1893,1317746206,IC_kwDOBm6k_c5OizYe,9599,simonw,2022-11-16T22:17:24Z,2022-11-16T22:17:24Z,OWNER,Deployed 0a649e8f78c23e8db6869442eeb0dfe36a5443da: https://datasette-pr-1893.vercel.app/fixtures,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317744563,https://api.github.com/repos/simonw/datasette/issues/1893,1317744563,IC_kwDOBm6k_c5Oiy-z,9599,simonw,2022-11-16T22:16:03Z,2022-11-16T22:16:03Z,OWNER,"Honestly I'm not too bothered if table names with weird characters don't work correctly here - I care about those in the Datasette `fixtures.db` database because Datasette aims to support ANY valid SQLite database, so I need stuff in the test suite that includes weird edge cases like this. But I would hope very few people actually create tables with spaces in their names, so it's not a huge concern to me if autocompletion doesn't work properly for those.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317475720,https://api.github.com/repos/simonw/datasette/issues/1893,1317475720,IC_kwDOBm6k_c5OhxWI,9599,simonw,2022-11-16T18:25:16Z,2022-11-16T18:25:16Z,OWNER,"Here's a query that returns the exact JSON we need to pass to the schema:
https://latest.datasette.io/_internal?sql=with+inner+as+%28%0D%0A++select%0D%0A++++table_name%2C%0D%0A++++json_group_array%28name%29+as+table_columns%0D%0A++from%0D%0A++++columns%0D%0A++where%0D%0A++++database_name+%3D+%3Adatabase%0D%0A++group+by%0D%0A++++table_name%0D%0A%29%0D%0Aselect%0D%0A++json_group_object%28table_name%2C+table_columns%29%0D%0Afrom%0D%0A++inner&database=fixtures
```sql
with inner as (
select
table_name,
json_group_array(name) as table_columns
from
columns
where
database_name = :database
group by
table_name
)
select
json_group_object(table_name, table_columns)
from
inner
```
Returns (after pretty-printing):
```json
{
""123_starts_with_digits"": [
""content""
],
""Table With Space In Name"": [
""content"",
""pk""
],
""attraction_characteristic"": [
""name"",
""pk""
],
""binary_data"": [
""data""
],
""complex_foreign_keys"": [
""f1"",
""f2"",
""f3"",
""pk""
],
""compound_primary_key"": [
""content"",
""pk1"",
""pk2""
],
""compound_three_primary_keys"": [
""content"",
""pk1"",
""pk2"",
""pk3""
],
""custom_foreign_key_label"": [
""foreign_key_with_custom_label"",
""pk""
],
""facet_cities"": [
""id"",
""name""
],
""facetable"": [
""_city_id"",
""_neighborhood"",
""complex_array"",
""created"",
""distinct_some_null"",
""n"",
""on_earth"",
""pk"",
""planet_int"",
""state"",
""tags""
],
""foreign_key_references"": [
""foreign_key_compound_pk1"",
""foreign_key_compound_pk2"",
""foreign_key_with_blank_label"",
""foreign_key_with_label"",
""foreign_key_with_no_label"",
""pk""
],
""infinity"": [
""value""
],
""no_primary_key"": [
""a"",
""b"",
""c"",
""content""
],
""primary_key_multiple_columns"": [
""content"",
""content2"",
""id""
],
""primary_key_multiple_columns_explicit_label"": [
""content"",
""content2"",
""id""
],
""roadside_attraction_characteristics"": [
""attraction_id"",
""characteristic_id""
],
""roadside_attractions"": [
""address"",
""latitude"",
""longitude"",
""name"",
""pk"",
""url""
],
""searchable"": [
""name with . and spaces"",
""pk"",
""text1"",
""text2""
],
""searchable_fts"": [
""__langid"",
""docid"",
""name with . and spaces"",
""searchable_fts"",
""text1"",
""text2""
],
""searchable_fts_docsize"": [
""docid"",
""size""
],
""searchable_fts_segdir"": [
""end_block"",
""idx"",
""leaves_end_block"",
""level"",
""root"",
""start_block""
],
""searchable_fts_segments"": [
""block"",
""blockid""
],
""searchable_fts_stat"": [
""id"",
""value""
],
""searchable_tags"": [
""searchable_id"",
""tag""
],
""select"": [
""and"",
""group"",
""having"",
""json""
],
""simple_primary_key"": [
""content"",
""id""
],
""sortable"": [
""content"",
""pk1"",
""pk2"",
""sortable"",
""sortable_with_nulls"",
""sortable_with_nulls_2"",
""text""
],
""table/with/slashes.csv"": [
""content"",
""pk""
],
""tags"": [
""tag""
],
""units"": [
""distance"",
""frequency"",
""pk""
]
}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317465874,https://api.github.com/repos/simonw/datasette/issues/1893,1317465874,IC_kwDOBm6k_c5Ohu8S,9599,simonw,2022-11-16T18:21:17Z,2022-11-16T18:21:54Z,OWNER,"I was worrying about the server-side overhead of collecting together all of the tables and column names for databases that might have hundreds of tables... but then I remember that I built the `_internal` table precisely for this kind of thing - so gathering all of that data should still only be a single SQL query against an in-memory database.
https://latest.datasette.io/login-as-root and then visit this page for an example query: https://latest.datasette.io/_internal?sql=select%0D%0A++database_name%2C%0D%0A++table_name%2C%0D%0A++json_group_array%28name%29%0D%0Afrom%0D%0A++columns%0D%0Awhere%0D%0A++database_name+%21%3D+%27_internal%27%0D%0Agroup+by%0D%0A++database_name%2C%0D%0A++table_name
```sql
select
database_name,
table_name,
json_group_array(name)
from
columns
where
database_name != '_internal'
group by
database_name,
table_name
```
database_name | table_name | json_group_array(name)
-- | -- | --
extra_database | searchable | [""pk"",""text1"",""text2""]
extra_database | searchable_fts | [""__langid"",""content"",""docid"",""searchable_fts"",""text1"",""text2""]
extra_database | searchable_fts_content | [""c0text1"",""c1text2"",""c2content"",""docid""]
extra_database | searchable_fts_segdir | [""end_block"",""idx"",""leaves_end_block"",""level"",""root"",""start_block""]
extra_database | searchable_fts_segments | [""block"",""blockid""]
fixtures | 123_starts_with_digits | [""content""]
fixtures | Table With Space In Name | [""content"",""pk""]
fixtures | attraction_characteristic | [""name"",""pk""]
fixtures | binary_data | [""data""]
fixtures | complex_foreign_keys | [""f1"",""f2"",""f3"",""pk""]
fixtures | compound_primary_key | [""content"",""pk1"",""pk2""]
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317456909,https://api.github.com/repos/simonw/datasette/issues/1893,1317456909,IC_kwDOBm6k_c5OhswN,9599,simonw,2022-11-16T18:17:39Z,2022-11-16T18:17:39Z,OWNER,"Tiny feature request (since you're in this code already) - I keep hitting Command+Enter on my macOS keyboard to submit the query, but the correct shortcut is Shift+Enter. Would be great if both worked!","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317452541,https://api.github.com/repos/simonw/datasette/issues/1893,1317452541,IC_kwDOBm6k_c5Ohrr9,9599,simonw,2022-11-16T18:15:52Z,2022-11-16T18:15:52Z,OWNER,"Deployed latest copy with:
```
datasette publish vercel fixtures.db \
--project datasette-pr-1893 \
--about 'PR 1893' \
--about_url https://github.com/simonw/datasette/pull/1893 \
--scope datasette \
--branch eccb1c6c781d69d8ec3c542ef65c78a4a0927a7c
```
https://datasette-pr-1893.vercel.app/fixtures
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1317449610,https://api.github.com/repos/simonw/datasette/issues/1893,1317449610,IC_kwDOBm6k_c5Ohq-K,9599,simonw,2022-11-16T18:14:28Z,2022-11-16T18:14:28Z,OWNER,"> I'm thinking of also adding `count` to the list since that's a common thing people would want to autocomplete. I notice BQ console highlights `count` in the same manner as other keywords like `select` as well.
Huh, yeah we should definitely have `count` - surprised it's not on the list on https://www.sqlite.org/lang_keywords.html which is why we didn't get it from the GPT-3 generated schema.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/sqlite-utils/issues/512#issuecomment-1316530539,https://api.github.com/repos/simonw/sqlite-utils/issues/512,1316530539,IC_kwDOCGYnMM5OeKlr,9599,simonw,2022-11-16T07:49:50Z,2022-11-16T07:49:50Z,OWNER,Tests passed.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450952393,mypy failures in CI,
https://github.com/simonw/sqlite-utils/issues/512#issuecomment-1316447182,https://api.github.com/repos/simonw/sqlite-utils/issues/512,1316447182,IC_kwDOCGYnMM5Od2PO,9599,simonw,2022-11-16T06:32:31Z,2022-11-16T06:32:31Z,OWNER,"Test failed again: https://github.com/simonw/sqlite-utils/actions/runs/3476950474/jobs/5812663096
`E: Failed to fetch http://azure.archive.ubuntu.com/ubuntu/pool/universe/s/spatialite/libsqlite3-mod-spatialite_4.3.0a-6build1_amd64.deb Unable to connect to azure.archive.ubuntu.com:http:`
That looks like an intermittent error. I'll try running it again in the morning.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450952393,mypy failures in CI,
https://github.com/simonw/sqlite-utils/issues/512#issuecomment-1316437748,https://api.github.com/repos/simonw/sqlite-utils/issues/512,1316437748,IC_kwDOCGYnMM5Odz70,9599,simonw,2022-11-16T06:24:31Z,2022-11-16T06:24:31Z,OWNER,"```
sqlite-utils % pipx run no_implicit_optional .
Calculating full-repo metadata...
Executing codemod...
11.43s 98% complete, 0.24s estimated for 5 files to go...
```
Then:
```
Finished codemodding 239 files!
- Transformed 239 files successfully.
- Skipped 0 files.
- Failed to codemod 0 files.
- 0 warnings were generated.
```
Here's the diff:
```diff
diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py
index a06f4b7..e819d17 100644
--- a/sqlite_utils/db.py
+++ b/sqlite_utils/db.py
@@ -297,12 +297,12 @@ class Database:
def __init__(
self,
- filename_or_conn: Union[str, pathlib.Path, sqlite3.Connection] = None,
+ filename_or_conn: Optional[Union[str, pathlib.Path, sqlite3.Connection]] = None,
memory: bool = False,
- memory_name: str = None,
+ memory_name: Optional[str] = None,
recreate: bool = False,
recursive_triggers: bool = True,
- tracer: Callable = None,
+ tracer: Optional[Callable] = None,
use_counts_table: bool = False,
):
assert (filename_or_conn is not None and (not memory and not memory_name)) or (
@@ -341,7 +341,7 @@ class Database:
self.conn.close()
@contextlib.contextmanager
- def tracer(self, tracer: Callable = None):
+ def tracer(self, tracer: Optional[Callable] = None):
""""""
Context manager to temporarily set a tracer function - all executed SQL queries will
be passed to this.
@@ -378,7 +378,7 @@ class Database:
def register_function(
self,
- fn: Callable = None,
+ fn: Optional[Callable] = None,
deterministic: bool = False,
replace: bool = False,
name: Optional[str] = None,
@@ -879,7 +879,7 @@ class Database:
pk: Optional[Any] = None,
foreign_keys: Optional[ForeignKeysType] = None,
column_order: Optional[List[str]] = None,
- not_null: Iterable[str] = None,
+ not_null: Optional[Iterable[str]] = None,
defaults: Optional[Dict[str, Any]] = None,
hash_id: Optional[str] = None,
hash_id_columns: Optional[Iterable[str]] = None,
@@ -1129,7 +1129,7 @@ class Database:
sql += "" [{}]"".format(name)
self.execute(sql)
- def init_spatialite(self, path: str = None) -> bool:
+ def init_spatialite(self, path: Optional[str] = None) -> bool:
""""""
The ``init_spatialite`` method will load and initialize the SpatiaLite extension.
The ``path`` argument should be an absolute path to the compiled extension, which
@@ -1182,7 +1182,7 @@ class Queryable:
def count_where(
self,
- where: str = None,
+ where: Optional[str] = None,
where_args: Optional[Union[Iterable, dict]] = None,
) -> int:
""""""
@@ -1213,12 +1213,12 @@ class Queryable:
def rows_where(
self,
- where: str = None,
+ where: Optional[str] = None,
where_args: Optional[Union[Iterable, dict]] = None,
- order_by: str = None,
+ order_by: Optional[str] = None,
select: str = ""*"",
- limit: int = None,
- offset: int = None,
+ limit: Optional[int] = None,
+ offset: Optional[int] = None,
) -> Generator[dict, None, None]:
""""""
Iterate over every row in this table or view that matches the specified where clause.
@@ -1251,11 +1251,11 @@ class Queryable:
def pks_and_rows_where(
self,
- where: str = None,
+ where: Optional[str] = None,
where_args: Optional[Union[Iterable, dict]] = None,
- order_by: str = None,
- limit: int = None,
- offset: int = None,
+ order_by: Optional[str] = None,
+ limit: Optional[int] = None,
+ offset: Optional[int] = None,
) -> Generator[Tuple[Any, Dict], None, None]:
""""""
Like ``.rows_where()`` but returns ``(pk, row)`` pairs - ``pk`` can be a single value or tuple.
@@ -1345,7 +1345,7 @@ class Table(Queryable):
pk: Optional[Any] = None,
foreign_keys: Optional[ForeignKeysType] = None,
column_order: Optional[List[str]] = None,
- not_null: Iterable[str] = None,
+ not_null: Optional[Iterable[str]] = None,
defaults: Optional[Dict[str, Any]] = None,
batch_size: int = 100,
hash_id: Optional[str] = None,
@@ -1545,7 +1545,7 @@ class Table(Queryable):
pk: Optional[Any] = None,
foreign_keys: Optional[ForeignKeysType] = None,
column_order: Optional[List[str]] = None,
- not_null: Iterable[str] = None,
+ not_null: Optional[Iterable[str]] = None,
defaults: Optional[Dict[str, Any]] = None,
hash_id: Optional[str] = None,
hash_id_columns: Optional[Iterable[str]] = None,
@@ -2464,7 +2464,7 @@ class Table(Queryable):
columns: Optional[Iterable[str]] = None,
limit: Optional[int] = None,
offset: Optional[int] = None,
- where: str = None,
+ where: Optional[str] = None,
where_args: Optional[Union[Iterable, dict]] = None,
quote: bool = False,
) -> Generator[dict, None, None]:
@@ -2527,7 +2527,7 @@ class Table(Queryable):
def delete_where(
self,
- where: str = None,
+ where: Optional[str] = None,
where_args: Optional[Union[Iterable, dict]] = None,
analyze: bool = False,
) -> ""Table"":
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450952393,mypy failures in CI,
https://github.com/simonw/datasette/pull/1893#issuecomment-1316412234,https://api.github.com/repos/simonw/datasette/issues/1893,1316412234,IC_kwDOBm6k_c5OdttK,9599,simonw,2022-11-16T06:00:39Z,2022-11-16T06:01:36Z,OWNER,"Should note though that this is a classic example of GPT-3 making stuff up in places.
> current: Returns the current date, time, or timestamp
`select current` throws an error for me: https://latest.datasette.io/_memory?sql=select+current
`select current_date, current_time, current_timestamp` works though: https://latest.datasette.io/_memory?sql=select+current_date%2C+current_time%2C+current_timestamp
So let's drop `current` from the list. I'm OK with it though, I think it's likely good enough for the first attempt at this.
We should drop `temp` and `temporary` too.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316401895,https://api.github.com/repos/simonw/datasette/issues/1893,1316401895,IC_kwDOBm6k_c5OdrLn,9599,simonw,2022-11-16T05:50:40Z,2022-11-16T05:50:40Z,OWNER,"So I think our dialect (at least to start with) should be:
```
keywords:
""and as asc between by case cast current current_date current_time current_timestamp desc distinct each else escape except exists explain filter first for from full generated group having if in index inner intersect into isnull join last left like limit not null or order outer over pragma primary query raise range regexp right rollback row select set table temp temporary then to union unique using values view virtual when where"",
// https://www.sqlite.org/datatype3.html
types: ""null integer real text blob"",
builtin:
""""
```
I left `builtin` blank here because I don't think we need any of those things at all.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316400688,https://api.github.com/repos/simonw/datasette/issues/1893,1316400688,IC_kwDOBm6k_c5Odq4w,9599,simonw,2022-11-16T05:49:31Z,2022-11-16T05:49:31Z,OWNER,"OK, I decided to play around with GPT-3 for this. https://beta.openai.com/playground
I prompted:
```
For each keyword say it it can be used in a SQLite select statement: and as asc between by case cast
cross current current_date current_time current_timestamp desc detach distinct each else end escape
except exclude exclusive exists explain fail filter first following for foreign from full generated glob
group groups having if ignore immediate in index indexed initially inner insert instead intersect into
is isnull join key last left like limit match materialized natural no not nothing notnull null nulls of
offset on or order others outer over partition plan pragma preceding primary query raise range recursive
references regexp reindex release rename replace restrict returning right rollback row rows savepoint
select set table temp temporary then ties to transaction trigger unbounded union unique update using
vacuum values view virtual when where window with without
- and: yes
- as: yes
- asc: yes
- between: yes
- by: yes
- case: yes
- cast: yes
- current: yes
- current_date: yes
- current_time: yes
- current_timestamp: yes
- desc: yes
- detach: no
- distinct: yes
- each: yes
- else: yes
- end: yes
- escape: yes
- except: no
- exclude: yes
- exclusive: no
```
Having given it these examples I ran it a few times:
```
- exists: yes
- explain: no
- fail: no
- filter: yes
- first: yes
- following: no
- for: yes
- foreign: no
- from: yes
- full: no
- generated: no
- glob: yes
- group: yes
- groups: no
- having: yes
- if: yes
- ignore: no
- immediate: no
- in: yes
- index: yes
- indexed: no
- initially: no
- inner: no
- insert: no
- instead: no
- intersect: no
- into: yes
- is: yes
- isnull: yes
- join: yes
- key: no
- last: yes
- left: yes
- like: yes
- limit: yes
- match: no
- materialized: no
-natural: yes
- no: no
- not: no
- nothing: no
- notnull: yes
- null: yes
- nulls: yes
- of: no
- offset: yes
- on: yes
- or: yes
- order: yes
- others: yes
- outer: no
- over: no
- partition: no
- pragma: no
- preceding: no
- primary: no
- query: no
- raise: no
- range: no
- recursive: no
- references: no
- regexp: yes
- reindex: no
- release: no
- rename: no
- replace: no
- restrict: no
- returning: yes
- right: yes
- rollback: no
- row: yes
- rows: no
- savepoint: no
- select: yes
- set: no
- table: yes
- temp: yes
- temporary: yes
- then: yes
- ties: no
- to: yes
- transaction: no
- trigger: no
- unbounded: no
- union: yes
- unique: yes
- update: no
- using: yes
- vacuum: no
- values: yes
- view: yes
- virtual: no
- when: yes
- where: yes
- window: no
- with: yes
- without: no
```
I added this:
```
A space separated list of all of the keywords for which the answer was yes:
```
It replied:
```
and as asc between by case cast current current_date current_time current_timestamp desc distinct each else escape except exists explain filter first for from full generated group having if in index inner intersect into isnull join last left like limit not null or order outer over pragma primary query raise range regexp right rollback row select set table temp temporary then to union unique using values view virtual when where
```
Then I tried doing this:
```
Produce a short summary of how each of those are used:
- and:
```
This kind of worked too! It ended up defining every keyword, not just the ones for which the ""is it used in select"" was ""yes"", but it gave me this list:
> - and: Used to combine multiple conditions in a WHERE clause
> - as: Rename a column or table in the output of a query
> - asc: Sort results in ascending order
> - between: Used in a WHERE clause to specify a range
> - by: Specifies how to sort the results of a query
> - case: Used to create different outputs based on conditions
> - cast: Converts a value from one data type to another
> - current: Returns the current date, time, or timestamp
> - current_date: Returns the current date
> - current_time: Returns the current time
> - current_timestamp: Returns the current timestamp
> - desc: Sort results in descending order
> - distinct: Selects only unique values
> - each: Used with aggregate functions
> - else: Used with the CASE statement
> - escape: Used to escape special characters in a string
> - except: Returns all rows from the first query that are not in the second query
> - exists: Used with subqueries
> - explain: Analyzes a query and returns information about it
> - fail: Used with the ON CONFLICT clause
> - filter: Used to specify conditions for aggregate functions
> - first: Used to specify conditions for aggregate functions
> - following: Used with window functions
> - for: Used to specify conditions for aggregate functions
> - foreign: Creates a foreign key
> - from: Specifies the table(s) to select data from
> - full: Used with outer joins
> - generated: Used with generated columns
> - glob: Used to match a string with a pattern
> - group: Groups together rows that have the same values
> - groups: Used with aggregate functions
> - having: Specifies conditions for groups
> - if: Used with the ON CONFLICT clause
> - ignore: Used with the ON CONFLICT clause
> - immediate: Used with the ON CONFLICT clause
> - in: Used to check if a value is in a list
> - index: Creates an index on a column or columns
> - indexed: Used with the ON CONFLICT clause
> - initially: Used with the ON CONFLICT clause
> - inner: Creates an inner join
> - insert: Inserts new data into a table
> - instead: Used with triggers
> - intersect: Returns all rows that are in both queries
> - into: Specifies the table to insert data into
> - is: Used to compare values
> - isnull: Checks if a value is null
> - join: Joins two or more tables together
> - key: Creates a primary key or unique key
> - last: Used to specify conditions for aggregate functions
> - left: Creates a left join
> - like: Checks if a value matches a pattern
> - limit: Specifies the maximum number of rows to return
> - not: Negates a condition
> - null: Specifies a null value
> - nulls: Used with the ORDER BY clause
> - of: Used with window functions
> - offset: Specifies the number of rows to skip
> - on: Specifies conditions for a join
> - or: Checks if either condition is true
> - order: Specifies how to sort the results of a query
> - others: Used with the CASE statement
> - outer: Creates an outer join
> - over: Used with window functions
> - partition: Used with window functions
> - pragma: Used to query or change various internal settings
> - preceding: Used with window functions
> - primary: Creates a primary key
> - query: Used with the ON CONFLICT clause
> - raise: Used with the ON CONFLICT clause
> - range: Used with window functions
> - recursive: Used with common table expressions
> - references: Creates a foreign key
> - regexp: Checks if a value matches a regular expression
> - reindex: Recreates an index
> - release: Used with savepoints
> - rename: Changes the name of a table or column
> - replace: Inserts new data into a table or updates existing data
> - restrict: Used with savepoints
> - returning: Returns data from inserted or updated rows
> - right: Creates a right join
> - rollback: Rolls back a transaction
> - row: Used with aggregate functions
> - select: Selects data from a table
> - set: Changes the value of a column
> - table: Specifies the table(s) to select data from
> - temp: Creates a temporary table
> - temporary: Creates a temporary table
> - then: Used with the CASE statement
> - ties: Used with the ORDER BY clause
> - to: Used with common table expressions
> - transaction: Starts a transaction
> - trigger: Creates a trigger
> - unbounded: Used with window functions
> - union: Returns all rows from both queries
> - unique: Creates a unique constraint
> - update: Updates existing data in a table
> - using: Specifies the table(s) to join
> - vacuum: Optimizes a database
> - values: Specifies the values to insert into a table
> - view: Creates a view
> - virtual: Creates a virtual table
> - when: Used with the CASE statement
> - where: Specifies conditions for a query
> - window: Used with window functions
> - with: Specifies common table expressions used in a query
> - without: Used with indexes","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316340865,https://api.github.com/repos/simonw/datasette/issues/1893,1316340865,IC_kwDOBm6k_c5OdcSB,9599,simonw,2022-11-16T04:49:30Z,2022-11-16T04:49:43Z,OWNER,"> The main issue is that we don't pass the relevant table data down to QueryView.
If you can come up with a static example JSON data structure example that does the right thing, I'm happy to refactor QueryView to make that available to the template - or even have a separate `fetch()` that grabs just the data needed for the autocomplete as a separate hit when the page loads (whichever has better performance implications). I'm working a fair amount in the view classes at the moment so adding this to that work would make sense.
","{""total_count"": 1, ""+1"": 1, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316294156,https://api.github.com/repos/simonw/datasette/issues/1893,1316294156,IC_kwDOBm6k_c5OdQ4M,9599,simonw,2022-11-16T04:00:12Z,2022-11-16T04:00:12Z,OWNER,Have you ever seen CodeMirror correctly auto-completing columns? I'm not entirely sure I believe that the feature works anywhere else.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316293353,https://api.github.com/repos/simonw/datasette/issues/1893,1316293353,IC_kwDOBm6k_c5OdQrp,9599,simonw,2022-11-16T03:59:03Z,2022-11-16T03:59:03Z,OWNER,"Deployed a fresh copy:
```
datasette publish vercel fixtures.db \
--branch b7b2942b13f9ea09cfa9f8c73e2869b9bd2349ae \
--project datasette-pr-1893 \
--about 'PR 1893' \
--about_url https://github.com/simonw/datasette/pull/1893 \
--scope datasette
```
https://datasette-pr-1893.vercel.app/fixtures","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/issues/1890#issuecomment-1316262169,https://api.github.com/repos/simonw/datasette/issues/1890,1316262169,IC_kwDOBm6k_c5OdJEZ,9599,simonw,2022-11-16T03:22:40Z,2022-11-16T03:22:40Z,OWNER,"Actually this works as it should in desktop Safari:
![autocomplete-safari](https://user-images.githubusercontent.com/9599/202075764-fbc4b4c8-c92f-4f69-81fd-84002de5aea7.gif)
I'm going to just put up with the weird behaviour in Mobile Safari.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/pull/1893#issuecomment-1316253186,https://api.github.com/repos/simonw/datasette/issues/1893,1316253186,IC_kwDOBm6k_c5OdG4C,9599,simonw,2022-11-16T03:16:36Z,2022-11-16T03:16:36Z,OWNER,Yeah I haven't written this down anywhere but Datasette definitely has an undocumented preference for lower-case SQL.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/issues/1890#issuecomment-1316242752,https://api.github.com/repos/simonw/datasette/issues/1890,1316242752,IC_kwDOBm6k_c5OdEVA,9599,simonw,2022-11-16T03:10:52Z,2022-11-16T03:12:47Z,OWNER,"https://bugs.webkit.org/show_bug.cgi?id=201768 - "" Datalist option's label not used"" - marked as RESOLVED FIXED on March 31st 2020.
The commit: https://trac.webkit.org/changeset/259330/webkit
And here's the test mirrored on GitHub: https://cs.github.com/qtwebkit/webkit-mirror/blob/cc3fcd0b4bad1f7cf77c26e34aa01d16618d6d5e/LayoutTests/fast/forms/datalist/datalist-option-labels.html?q=datalist-option-labels.html","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1316240839,https://api.github.com/repos/simonw/datasette/issues/1890,1316240839,IC_kwDOBm6k_c5OdD3H,9599,simonw,2022-11-16T03:09:11Z,2022-11-16T03:09:11Z,OWNER,"Here's a polyfill for ``: https://github.com/mfranzke/datalist-polyfill
It shouldn't be necessary now that Safari has shipped support (apparently added in https://developer.apple.com/documentation/safari-release-notes/safari-12_1-release-notes#3130314 Safari 12.1 in March 2019).
But it does look like Safari doesn't support differing `label` and `value` attributes, though documentation about this is hard to come by.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/pull/1893#issuecomment-1316236448,https://api.github.com/repos/simonw/datasette/issues/1893,1316236448,IC_kwDOBm6k_c5OdCyg,9599,simonw,2022-11-16T03:04:57Z,2022-11-16T03:04:57Z,OWNER,If you rebase from `main` you should get the fix for that test failure.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/issues/1890#issuecomment-1316233532,https://api.github.com/repos/simonw/datasette/issues/1890,1316233532,IC_kwDOBm6k_c5OdCE8,9599,simonw,2022-11-16T03:00:58Z,2022-11-16T03:00:58Z,OWNER,"Oops, introduced a test failure:
```
def test_table_html_foreign_key_facets(app_client):
response = app_client.get(
""/fixtures/foreign_key_references?_facet=foreign_key_with_blank_label""
)
assert response.status == 200
> assert (
''
""- 1 ""
) in response.text
E assert '- 1 ' in '\n\n\n fixtures: foreign_key_references: 2 rows \n \n\n\n\n\n'
E + where '\n\n\n fixtures: foreign_key_references: 2 rows \n \n\n\n\n\n' = .text
```
Need to fix this test:
https://github.com/simonw/datasette/blob/eac028d3f77aa5473a5fcf59240635a1bca80f7d/tests/test_table_html.py#L616-L624","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/pull/1893#issuecomment-1316232588,https://api.github.com/repos/simonw/datasette/issues/1893,1316232588,IC_kwDOBm6k_c5OdB2M,9599,simonw,2022-11-16T03:00:04Z,2022-11-16T03:00:04Z,OWNER,"Oops, the tests are failing because of a test failure I introduced here:
- #1890","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316231560,https://api.github.com/repos/simonw/datasette/issues/1893,1316231560,IC_kwDOBm6k_c5OdBmI,9599,simonw,2022-11-16T02:59:00Z,2022-11-16T02:59:00Z,OWNER,"The resize handle doesn't appear on Mobile Safari on iPhone - I don't think that particularly matters though.
The textarea does get a weird border around it when focused on iPhone though.
Focused:
![BF34E8FB-E35C-4CAB-9BFB-8EEF7E29B16C_1_201_a](https://user-images.githubusercontent.com/9599/202072748-c85bab94-a039-4ed6-8185-3cac25c78ed3.jpeg)
Not focused:
![31A5CF38-D540-4A1A-8A7D-E29453D150F4_1_201_a](https://user-images.githubusercontent.com/9599/202072744-d9f0ea62-13b7-46ff-afe1-6d88d7fb8b53.jpeg)
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316227073,https://api.github.com/repos/simonw/datasette/issues/1893,1316227073,IC_kwDOBm6k_c5OdAgB,9599,simonw,2022-11-16T02:54:22Z,2022-11-16T02:54:32Z,OWNER,"If you can get a version of this working with table and column autocompletion just using a static JavaScript object in the source code with the right tables and columns, I'm happy to take on the work of turning that static object into something that Datasette includes in the page itself with all of the correct values.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316141764,https://api.github.com/repos/simonw/datasette/issues/1893,1316141764,IC_kwDOBm6k_c5OcrrE,9599,simonw,2022-11-16T01:26:59Z,2022-11-16T01:26:59Z,OWNER,"Resizing works great for me - and the page automatically sizes the editor to fit an existing query, e.g. on https://datasette-pr-1893.vercel.app/fixtures?sql=select+id%2C+content%2C+content2%0D%0A++from+primary_key_multiple_columns_explicit_label%0D%0A++order+by+id%0D%0A++limit+101","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316137982,https://api.github.com/repos/simonw/datasette/issues/1893,1316137982,IC_kwDOBm6k_c5Ocqv-,9599,simonw,2022-11-16T01:23:47Z,2022-11-16T01:23:47Z,OWNER,"Autocomplete here looks promising (I've wanted that to work for years!), but it does currently show a whole bunch of suggestions which aren't part of the SQLite SQL dialect:
![autocomplete](https://user-images.githubusercontent.com/9599/202060211-51ec9f45-bc52-459a-a729-27fc2faadff9.gif)
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/pull/1893#issuecomment-1316135244,https://api.github.com/repos/simonw/datasette/issues/1893,1316135244,IC_kwDOBm6k_c5OcqFM,9599,simonw,2022-11-16T01:21:41Z,2022-11-16T01:21:41Z,OWNER,"I just deployed a demo instance like this (using the commit hash from this PR):
```bash
datasette publish vercel fixtures.db \
--branch 544f7025900b78f63c34b9985522271ba5fd9c0f \
--project datasette-pr-1893 \
--scope datasette \
--about 'PR 1893' \
--about_url https://github.com/simonw/datasette/pull/1893
```
Here's the result: https://datasette-pr-1893.vercel.app/fixtures","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450363982,"Upgrade to CodeMirror 6, add SQL autocomplete",
https://github.com/simonw/datasette/issues/1863#issuecomment-1315812212,https://api.github.com/repos/simonw/datasette/issues/1863,1315812212,IC_kwDOBm6k_c5ObbN0,9599,simonw,2022-11-15T20:12:02Z,2022-11-15T20:12:02Z,OWNER,"If the update succeeds it will return `{""ok"": true}`.
For consistency with `/db/table/-/insert` you can pass `""return"": true` and it will return a `""row""` key with the now-updated full row.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1863#issuecomment-1315809867,https://api.github.com/repos/simonw/datasette/issues/1863,1315809867,IC_kwDOBm6k_c5ObapL,9599,simonw,2022-11-15T20:09:44Z,2022-11-15T20:09:44Z,OWNER,"I'm also not going to implement `""alter"": true` yet (which would add any missing columns based on the update) - I'll hold that off for a later feature.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1863#issuecomment-1315809260,https://api.github.com/repos/simonw/datasette/issues/1863,1315809260,IC_kwDOBm6k_c5Obafs,9599,simonw,2022-11-15T20:09:11Z,2022-11-15T20:09:11Z,OWNER,"I'm going to use the error format I've been experimenting with here:
- #1875
```json
{
""type"": ""https://example.net/validation-error"",
""title"": ""Your request is not valid."",
""errors"": [
{
""detail"": ""must be a positive integer"",
""pointer"": ""#/age""
},
{
""detail"": ""must be 'green', 'red' or 'blue'"",
""pointer"": ""#/profile/color""
}
]
}
```
I'm not quite ready to commit to a `type` URL though, so I'll leave that to be solved later should I fully embrace that RFC.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1863#issuecomment-1315808062,https://api.github.com/repos/simonw/datasette/issues/1863,1315808062,IC_kwDOBm6k_c5ObaM-,9599,simonw,2022-11-15T20:08:04Z,2022-11-15T20:08:04Z,OWNER,"The initial design I'm going to implement will look like this:
```
POST /db/table/1/-/update
Authorization: Bearer xxx
Content-Type: application/json
```
```json
{
""update"": {
""name"": ""New name""
}
}
```
Any fields that are not yet columns will return an error.
Should it enforce types, in as much as an integer column should have a JSON integer passed to it, or should it allow strings containing valid integers?
I'm going to allow strings, mainly as a workaround for the fact that JavaScript integers have a maximum size.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425029242,Update a single record in an existing table,
https://github.com/simonw/datasette/issues/1892#issuecomment-1315805498,https://api.github.com/repos/simonw/datasette/issues/1892,1315805498,IC_kwDOBm6k_c5ObZk6,9599,simonw,2022-11-15T20:05:30Z,2022-11-15T20:05:30Z,OWNER,"One slight concern: https://latest.datasette.io/ will increasingly reflect a version that isn't the most recent production release.
I might setup https://stable.datasette.io/ as a demo instance of the most recent non-alpha release to compensate for that.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450312343,Merge 1.0-dev branch back to main,
https://github.com/simonw/datasette/issues/1892#issuecomment-1315804535,https://api.github.com/repos/simonw/datasette/issues/1892,1315804535,IC_kwDOBm6k_c5ObZV3,9599,simonw,2022-11-15T20:04:38Z,2022-11-15T20:04:38Z,OWNER,"I'll do this after the 1.0a0 release:
- #1708","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1450312343,Merge 1.0-dev branch back to main,
https://github.com/simonw/datasette/issues/1708#issuecomment-1095675839,https://api.github.com/repos/simonw/datasette/issues/1708,1095675839,IC_kwDOBm6k_c5BTq-_,9599,simonw,2022-04-11T23:06:30Z,2022-11-15T19:57:53Z,OWNER,"# Datasette 1.0 alpha 1
This alpha release is a preview of Datasette 1.0.
Datasette 1.0 marks a significant milestone in the project: it is the point from which various aspects of Datasette can be considered ""stable"", in that code developed against them should expect not to be broken by future releases in the 1.x series.
This will hold true until the next major version release, Datasette 2.0 - which we hope to hold off releasing for as long as possible.
The following Datasette components should be be considered stable after 1.0:
- The plugin API. Plugins developed against 1.0 should continue to work unmodified throughout the 1.x series.
- The JSON API. Code written that interacts with Datasette's default JSON web API should continue to work.
- The template context. If you build custom templates against Datasette your custom pages should continue to work.
Note that none of these components will cease to introduce new features. New plugin hooks, new JSON APIs and new template context variables can be introduced without breaking existing code.
Since this alpha release previews features that will be frozen for 1.0, please test this thoroughly against your existing Datasette projects.
You can install the alpha using:
pip install datasette==1.0a0
## JSON API changes
The most significant changes introduced in this new alpha concern Datasette's JSON API.
The default JSON returned by the `/database/table.json` endpoint has changed. It now returns an object with two keys: `rows` - which contains a list of objects representing the rows in the table or query, and `more` containing a `boolean` that shows if there are more rows or if this object contains them all.
```json
{
""rows"": [{
""id"": 1,
""name"": ""Name 1""
}, {
""id"": 2,
""name"": ""Name 2""
}],
""more"": false
}
```
[ Initially I thought about going with `next_url`, which would be `null` if you have reached the last page of records. Maybe that would be better? But since `next_url` cannot be provided on query pages, should this be part of the default format at all? ]
## Use ?_extra= to retrieve extra fields
The default format can be expanded using one or more `?_extra=` parameters. This takes names of extra keys you would like to include. These can be comma-separated or `?_extra=` can be applied multiple times.
For example:
/database/table.json?_extra=total
This adds a `""total"": 124` field to the returned JSON.
[ Question: if you do `?_facet=foo` then do you still need to do `?_extra=facets` - I think not? ]","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1200649124,Datasette 1.0 alpha upcoming release notes,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314891228,https://api.github.com/repos/simonw/datasette/issues/1890,1314891228,IC_kwDOBm6k_c5OX6Xc,9599,simonw,2022-11-15T07:23:01Z,2022-11-15T07:23:01Z,OWNER,"Annoying: Mobile Safari doesn't seem to support separate labels and values. I should probably disable this feature on that browser, at least for foreign key facets (for the moment).","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314856513,https://api.github.com/repos/simonw/datasette/issues/1890,1314856513,IC_kwDOBm6k_c5OXx5B,9599,simonw,2022-11-15T06:56:29Z,2022-11-15T06:56:29Z,OWNER,"Looks like I can fix that like so:
```html
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314850524,https://api.github.com/repos/simonw/datasette/issues/1890,1314850524,IC_kwDOBm6k_c5OXwbc,9599,simonw,2022-11-15T06:48:37Z,2022-11-15T06:48:37Z,OWNER,"Spotted a bug with this on https://latest.datasette.io/fixtures/facetable?_facet=_city_id - the `_city_id` column is a foreign key, so you need to type `1` or `2` - but the autocomplete list shows the full text names for the cities.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314849867,https://api.github.com/repos/simonw/datasette/issues/1890,1314849867,IC_kwDOBm6k_c5OXwRL,9599,simonw,2022-11-15T06:47:51Z,2022-11-15T06:47:51Z,OWNER,Demo now live here: https://congress-legislators.datasettes.com/legislators/legislator_terms?_facet=party - select `party` and start typing.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314848432,https://api.github.com/repos/simonw/datasette/issues/1890,1314848432,IC_kwDOBm6k_c5OXv6w,9599,simonw,2022-11-15T06:46:08Z,2022-11-15T06:46:08Z,OWNER,Wrote a TIL about ``: https://til.simonwillison.net/html/datalist,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1862#issuecomment-1314845667,https://api.github.com/repos/simonw/datasette/issues/1862,1314845667,IC_kwDOBm6k_c5OXvPj,9599,simonw,2022-11-15T06:42:09Z,2022-11-15T06:42:32Z,OWNER,"I implemented this as part of `/db/-/create`.
https://docs.datasette.io/en/1.0-dev/json_api.html#creating-a-table-from-example-data","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1425011030,"Create a new table from one or more records, `sqlite-utils` style",
https://github.com/simonw/datasette/issues/1890#issuecomment-1314835740,https://api.github.com/repos/simonw/datasette/issues/1890,1314835740,IC_kwDOBm6k_c5OXs0c,9599,simonw,2022-11-15T06:30:26Z,2022-11-15T06:30:26Z,OWNER,That prototype actually works really well! I'm going to add that to `table.js`.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314833881,https://api.github.com/repos/simonw/datasette/issues/1890,1314833881,IC_kwDOBm6k_c5OXsXZ,9599,simonw,2022-11-15T06:27:21Z,2022-11-15T06:27:21Z,OWNER,"Here's a prototype:
```javascript
function createDataLists() {
var facetResults = document.querySelectorAll("".facet-results [data-column]"");
Array.from(facetResults).forEach(function (facetResult) {
// Use link text from all links in the facet result
var linkTexts = Array.from(
facetResult.querySelectorAll(""li:not(.facet-truncated) a"")
).map(function (link) {
return link.textContent;
});
// Create a datalist element
var datalist = document.createElement(""datalist"");
datalist.id = ""datalist-"" + facetResult.dataset.column;
// Create an option element for each link text
linkTexts.forEach(function (linkText) {
var option = document.createElement(""option"");
option.value = linkText;
datalist.appendChild(option);
});
// Add the datalist to the facet result
facetResult.appendChild(datalist);
});
}
createDataLists();
// When any select with name=_filter_column changes, update the datalist
document.body.addEventListener(""change"", function (event) {
if (event.target.name === ""_filter_column"") {
event.target
.closest("".filter-row"")
.querySelector("".filter-value"")
.setAttribute(""list"", ""datalist-"" + event.target.value);
}
});
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314829751,https://api.github.com/repos/simonw/datasette/issues/1890,1314829751,IC_kwDOBm6k_c5OXrW3,9599,simonw,2022-11-15T06:20:50Z,2022-11-15T06:20:50Z,OWNER,"This finds the right links on the page:
document.querySelectorAll('.facet-results [data-column] li:not(.facet-truncated) a')","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314825019,https://api.github.com/repos/simonw/datasette/issues/1890,1314825019,IC_kwDOBm6k_c5OXqM7,9599,simonw,2022-11-15T06:13:36Z,2022-11-15T06:13:36Z,OWNER,"This could start out as a purely JavaScript enhancement for pages that already figured out the available values through faceting, like you suggested.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314823752,https://api.github.com/repos/simonw/datasette/issues/1890,1314823752,IC_kwDOBm6k_c5OXp5I,9599,simonw,2022-11-15T06:11:49Z,2022-11-15T06:11:49Z,OWNER,"I tried this out on https://congress-legislators.datasettes.com/legislators/legislator_terms for the `party` column - here's the demo:
![datalist](https://user-images.githubusercontent.com/9599/201839812-db887ce0-c4b9-432c-8620-5ac73f222a63.gif)
I made this work by dropping the following HTML into the page in the browser DevTools:
```html
```
And then adding `list=""party""` to the input element in the filter form.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1890#issuecomment-1314821337,https://api.github.com/repos/simonw/datasette/issues/1890,1314821337,IC_kwDOBm6k_c5OXpTZ,9599,simonw,2022-11-15T06:08:19Z,2022-11-15T06:08:19Z,OWNER,"Oh interesting... this doesn't even need to be attached to the visible faceting feature, necessarily: Datasette could try to detect when a column has a limited number of options (which the faceting code handles already) and could turn those into an auto-complete interface.
There's actually a native HTML element for this these days: the `` https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1448143294,Autocomplete text entry for filter values that correspond to facets,
https://github.com/simonw/datasette/issues/1882#issuecomment-1314813205,https://api.github.com/repos/simonw/datasette/issues/1882,1314813205,IC_kwDOBm6k_c5OXnUV,9599,simonw,2022-11-15T06:00:41Z,2022-11-15T06:00:41Z,OWNER,"Documentation:
- https://docs.datasette.io/en/1.0-dev/json_api.html#creating-a-table
- https://docs.datasette.io/en/1.0-dev/json_api.html#creating-a-table-from-example-data
Wrote a TIL about how I wrote some of those tests with Copilot: https://til.simonwillison.net/gpt3/writing-test-with-copilot","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1875#issuecomment-1314620086,https://api.github.com/repos/simonw/datasette/issues/1875,1314620086,IC_kwDOBm6k_c5OW4K2,9599,simonw,2022-11-15T01:09:56Z,2022-11-15T01:09:56Z,OWNER,"Rough initial prototype:
```diff
diff --git a/datasette/views/table.py b/datasette/views/table.py
index 8b987221..518ac578 100644
--- a/datasette/views/table.py
+++ b/datasette/views/table.py
@@ -1103,19 +1103,30 @@ class TableInsertView(BaseView):
except json.JSONDecodeError as e:
return _errors([""Invalid JSON: {}"".format(e)])
if not isinstance(data, dict):
- return _errors([""JSON must be a dictionary""])
+ return _errors([{""detail"": ""JSON must be a dictionary"", ""pointer"": ""#/""}])
keys = data.keys()
# keys must contain ""row"" or ""rows""
if ""row"" not in keys and ""rows"" not in keys:
return _errors(['JSON must have one or other of ""row"" or ""rows""'])
rows = []
+ was_single_row = False
if ""row"" in keys:
if ""rows"" in keys:
- return _errors(['Cannot use ""row"" and ""rows"" at the same time'])
+ return _errors(
+ [
+ {
+ ""detail"": 'Cannot use ""row"" and ""rows"" at the same time',
+ ""pointer"": ""#/row"",
+ }
+ ]
+ )
+ was_single_row = True
row = data[""row""]
if not isinstance(row, dict):
- return _errors(['""row"" must be a dictionary'])
+ return _errors(
+ [{""detail"": '""row"" must be a dictionary', ""pointer"": ""#/row""}]
+ )
rows = [row]
data[""return""] = True
else:
@@ -1152,9 +1163,12 @@ class TableInsertView(BaseView):
invalid_columns = set(row.keys()) - columns
if invalid_columns:
errors.append(
- ""Row {} has invalid columns: {}"".format(
- i, "", "".join(sorted(invalid_columns))
- )
+ {
+ ""detail"": ""Invalid columns: {}"".format(
+ "", "".join(sorted(invalid_columns))
+ ),
+ ""pointer"": ""#/blah/"",
+ }
)
if errors:
return _errors(errors)
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1875#issuecomment-1314615592,https://api.github.com/repos/simonw/datasette/issues/1875,1314615592,IC_kwDOBm6k_c5OW3Eo,9599,simonw,2022-11-15T01:04:28Z,2022-11-15T01:04:28Z,OWNER,"Worth noting this bit in RFC 7807:
> The fictional problem type here defines the ""errors"" extension, an
> array that describes the details of each validation error. Each
> member is an object containing ""detail"" to describe the issue, and
> ""pointer"" to locate the problem within the request's content using a
> JSON Pointer [JSON-POINTER].
So the list of `""errors""` with JSON Pointer isn't technically part of the spec, it's an imaginary extension.
It fits what I need to do though, so I'm inclined to stick with it anyway.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1875#issuecomment-1314545407,https://api.github.com/repos/simonw/datasette/issues/1875,1314545407,IC_kwDOBm6k_c5OWl7_,9599,simonw,2022-11-14T23:30:34Z,2022-11-14T23:30:34Z,OWNER,TIL: https://til.simonwillison.net/json/json-pointer,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1875#issuecomment-1314491884,https://api.github.com/repos/simonw/datasette/issues/1875,1314491884,IC_kwDOBm6k_c5OWY3s,9599,simonw,2022-11-14T22:26:11Z,2022-11-14T22:26:54Z,OWNER,"Spec looks pretty simple:
> A JSON Pointer is a Unicode string (see [RFC4627], Section 3)
> containing a sequence of zero or more reference tokens, each prefixed
> by a `/` (%x2F) character.
>
> Because the characters `~` (%x7E) and `/` (%x2F) have special
> meanings in JSON Pointer, `~` needs to be encoded as `~0` and `/`
> needs to be encoded as `~1` when these characters appear in a
> reference token.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1875#issuecomment-1314491150,https://api.github.com/repos/simonw/datasette/issues/1875,1314491150,IC_kwDOBm6k_c5OWYsO,9599,simonw,2022-11-14T22:25:20Z,2022-11-14T22:25:20Z,OWNER,"That's using JSON Pointer: https://www.rfc-editor.org/rfc/rfc6901
There's a Python library for that here https://github.com/stefankoegl/python-json-pointer/blob/master/jsonpointer.py - which looks simple and clean and well maintained and documented, but it only handles the ""what is at this pointer within this JSON object"" case - I need to generate the correct JSON pointer to explain where my error is.
So I think I'll end up hand-rolling this.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1875#issuecomment-1314488010,https://api.github.com/repos/simonw/datasette/issues/1875,1314488010,IC_kwDOBm6k_c5OWX7K,9599,simonw,2022-11-14T22:21:43Z,2022-11-14T22:21:43Z,OWNER,"Here's the most relevant example from the RFC spec:
```
POST /details HTTP/1.1
Host: account.example.com
Accept: application/json
```
```json
{
""age"": 42.3,
""profile"": {
""color"": ""yellow""
}
}
```
```
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en
```
```json
{
""type"": ""https://example.net/validation-error"",
""title"": ""Your request is not valid."",
""errors"": [
{
""detail"": ""must be a positive integer"",
""pointer"": ""#/age""
},
{
""detail"": ""must be 'green', 'red' or 'blue'"",
""pointer"": ""#/profile/color""
}
]
}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1884#issuecomment-1314054300,https://api.github.com/repos/simonw/datasette/issues/1884,1314054300,IC_kwDOBm6k_c5OUuCc,9599,simonw,2022-11-14T16:40:06Z,2022-11-14T16:40:06Z,OWNER,"I wonder if there are any reasons that inspect SHOULD try to count virtual tables? Like are there any likely uses for a cirial table where the count is both interesting and likely to be accessed often enough that it's worth caching?
I have an issue open to add a setting to disable table counts entirely:
- #1818
Maybe that should be expanded to automatically disable row counts for virtual tables entirely? Which would mean no count would be shown for them in the UI.
If you desperately wanted a count you would then have to run a count(*) query against them explicitly.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1439009231,Exclude virtual tables from datasette inspect,
https://github.com/simonw/datasette/issues/1850#issuecomment-1313156167,https://api.github.com/repos/simonw/datasette/issues/1850,1313156167,IC_kwDOBm6k_c5ORSxH,9599,simonw,2022-11-14T06:23:39Z,2022-11-14T06:23:39Z,OWNER,The API explorer is now live here: https://latest-1-0-dev.datasette.io/-/api,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1421529723,Write API in Datasette core,
https://github.com/simonw/datasette/issues/1850#issuecomment-1313155712,https://api.github.com/repos/simonw/datasette/issues/1850,1313155712,IC_kwDOBm6k_c5ORSqA,9599,simonw,2022-11-14T06:22:57Z,2022-11-14T06:22:57Z,OWNER,"I think the ability to create tokens should be protected by a `create-tokens` permission, not just a global setting.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1421529723,Write API in Datasette core,
https://github.com/simonw/datasette/issues/1888#issuecomment-1313139657,https://api.github.com/repos/simonw/datasette/issues/1888,1313139657,IC_kwDOBm6k_c5OROvJ,9599,simonw,2022-11-14T06:04:48Z,2022-11-14T06:04:48Z,OWNER,Demo: https://latest-1-0-dev.datasette.io/-/api,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1447439985,API explorer should take immutability into account,
https://github.com/simonw/datasette/issues/1866#issuecomment-1313128913,https://api.github.com/repos/simonw/datasette/issues/1866,1313128913,IC_kwDOBm6k_c5ORMHR,9599,simonw,2022-11-14T05:48:22Z,2022-11-14T05:48:22Z,OWNER,"I changed my mind about the `""return_rows"": true` option - I'm going to rename it to `""return"": true`.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1426001541,API for bulk inserting records into a table,
https://github.com/simonw/datasette/issues/1874#issuecomment-1313127054,https://api.github.com/repos/simonw/datasette/issues/1874,1313127054,IC_kwDOBm6k_c5ORLqO,9599,simonw,2022-11-14T05:45:00Z,2022-11-14T05:45:00Z,OWNER,"Demo: https://latest-1-0-dev.datasette.io/-/api#path=%2Ffixtures%2Ffacetable%2F-%2Fdrop&json=&method=POST
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1429030341,API to drop a table,
https://github.com/simonw/datasette/issues/1871#issuecomment-1313125870,https://api.github.com/repos/simonw/datasette/issues/1871,1313125870,IC_kwDOBm6k_c5ORLXu,9599,simonw,2022-11-14T05:42:50Z,2022-11-14T05:42:50Z,OWNER,Demo: https://latest-1-0-dev.datasette.io/-/api#path=%2Ffixtures%2Ffacetable%2F-%2Fdrop&json=%7B%22confirm%22%3A+true%7D&method=POST,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1874#issuecomment-1313125123,https://api.github.com/repos/simonw/datasette/issues/1874,1313125123,IC_kwDOBm6k_c5ORLMD,9599,simonw,2022-11-14T05:41:20Z,2022-11-14T05:42:23Z,OWNER,"I also changed the confirmation JSON returned by this endpoint to add the `database` and `table` like so:
```json
{
""ok"": true,
""database"": ""data"",
""table"": ""docs"",
""row_count"": 1,
""message"": ""Pass \""confirm\"": true to confirm""
}
```
Updated docs: https://docs.datasette.io/en/1.0-dev/json_api.html#dropping-tables","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1429030341,API to drop a table,
https://github.com/simonw/datasette/issues/1874#issuecomment-1313119558,https://api.github.com/repos/simonw/datasette/issues/1874,1313119558,IC_kwDOBm6k_c5ORJ1G,9599,simonw,2022-11-14T05:30:27Z,2022-11-14T05:30:27Z,OWNER,Found a bug: you get a 500 error if you try this against an immutable database.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1429030341,API to drop a table,
https://github.com/simonw/datasette/issues/1850#issuecomment-1313115059,https://api.github.com/repos/simonw/datasette/issues/1850,1313115059,IC_kwDOBm6k_c5ORIuz,9599,simonw,2022-11-14T05:21:30Z,2022-11-14T05:21:30Z,OWNER,New documentation for these features currently lives here: https://docs.datasette.io/en/1.0-dev/json_api.html#the-json-write-api,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1421529723,Write API in Datasette core,
https://github.com/simonw/datasette/issues/1875#issuecomment-1313114283,https://api.github.com/repos/simonw/datasette/issues/1875,1313114283,IC_kwDOBm6k_c5ORIir,9599,simonw,2022-11-14T05:20:00Z,2022-11-14T05:20:00Z,OWNER,"I started a conversation about JSON error standards on Mastodon here: https://fedi.simonwillison.net/web/@simon/109338725610487457
Quite a few people pointed to this RFC independently.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1430797211,Figure out design for JSON errors (consider RFC 7807),
https://github.com/simonw/datasette/issues/1887#issuecomment-1313113642,https://api.github.com/repos/simonw/datasette/issues/1887,1313113642,IC_kwDOBm6k_c5ORIYq,9599,simonw,2022-11-14T05:18:51Z,2022-11-14T05:18:51Z,OWNER,Updated docs: https://docs.datasette.io/en/1.0-dev/json_api.html#dropping-tables,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1447388809,Add a confirm step to the drop table API,
https://github.com/simonw/datasette/issues/1887#issuecomment-1313097713,https://api.github.com/repos/simonw/datasette/issues/1887,1313097713,IC_kwDOBm6k_c5OREfx,9599,simonw,2022-11-14T05:00:54Z,2022-11-14T05:00:54Z,OWNER,"I'm going to add a `""confirm"": true` option to the API. Without that, it returns a note about how many rows will be deleted.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1447388809,Add a confirm step to the drop table API,
https://github.com/simonw/datasette/issues/1871#issuecomment-1313097057,https://api.github.com/repos/simonw/datasette/issues/1871,1313097057,IC_kwDOBm6k_c5OREVh,9599,simonw,2022-11-14T04:59:28Z,2022-11-14T04:59:28Z,OWNER,In playing with the API explorer just now I realized it's way too easy to accidentally drop a table using it.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1871#issuecomment-1313072900,https://api.github.com/repos/simonw/datasette/issues/1871,1313072900,IC_kwDOBm6k_c5OQ-cE,9599,simonw,2022-11-14T04:15:50Z,2022-11-14T04:15:50Z,OWNER,"For the example links - I'm going to have these at the bottom of the page so you don't have to scroll past them.
Ideally these would take the user's permissions into account. This could make the page expensive to load, but I'm going to risk it for the moment.
Something like this then:
> - data
> - /data/-/create - create table
> - /data/table1/-/insert - insert into table1
> - /data/table1/-/drop - drop table1
I won't bother with per-row demo links (for update and delete) because there could be thousands of them for each table.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1871#issuecomment-1313062699,https://api.github.com/repos/simonw/datasette/issues/1871,1313062699,IC_kwDOBm6k_c5OQ78r,9599,simonw,2022-11-14T04:03:29Z,2022-11-14T04:12:41Z,OWNER,"Two things left before I close this issue:
- [x] I want to preserve the state of the forms in the URL - probably after a `#`
- [ ] Instead of hard-coding the current examples, I want to provide a list of links which populate the forms","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1886#issuecomment-1313052863,https://api.github.com/repos/simonw/datasette/issues/1886,1313052863,IC_kwDOBm6k_c5OQ5i_,9599,simonw,2022-11-14T03:40:50Z,2022-11-14T03:40:50Z,OWNER,"Tim Sherratt on Twitter: https://twitter.com/wragge/status/1591930345469153282
> Where do I start? The [#GLAMWorkbench](https://twitter.com/hashtag/GLAMWorkbench?src=hashtag_click) now includes a number of examples where GLAM data is harvested, processed, and then made available for exploration via Datasette.
>
> https://glam-workbench.net/
>
> For example the GLAM Name Index Search brings together 10+ million entries from 240 indexes and provides an aggregated search using the Datasette search-all plugin:
>
> https://glam-workbench.net/name-search/
>
> Most recently I converted PDFs of the Tasmanian Postal Directories to a big Datasette instance: https://updates.timsherratt.org/2022/09/15/from-pdfs-to.html the process is documented and reusable.","{""total_count"": 1, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 1, ""rocket"": 0, ""eyes"": 0}",1447050738,"Call for birthday presents: if you're using Datasette, let us know how you're using it here",
https://github.com/simonw/datasette/issues/1871#issuecomment-1312822353,https://api.github.com/repos/simonw/datasette/issues/1871,1312822353,IC_kwDOBm6k_c5OQBRR,9599,simonw,2022-11-13T21:07:40Z,2022-11-13T21:07:40Z,OWNER,I'm going to need extra code to toggle POST closed when GET opens and vice-versa.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1871#issuecomment-1312821031,https://api.github.com/repos/simonw/datasette/issues/1871,1312821031,IC_kwDOBm6k_c5OQA8n,9599,simonw,2022-11-13T21:02:06Z,2022-11-13T21:03:11Z,OWNER,"Actually no, I'm going to add a class of `details-menu` to the other details elements that SHOULD be closed. That way custom templates using `` won't close in a surprising way.","{""total_count"": 1, ""+1"": 1, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1871#issuecomment-1312816451,https://api.github.com/repos/simonw/datasette/issues/1871,1312816451,IC_kwDOBm6k_c5OP_1D,9599,simonw,2022-11-13T20:39:26Z,2022-11-13T20:39:34Z,OWNER,I'm going to add a special `no-auto-close` class to these and teach that code not to close them.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1871#issuecomment-1312816292,https://api.github.com/repos/simonw/datasette/issues/1871,1312816292,IC_kwDOBm6k_c5OP_yk,9599,simonw,2022-11-13T20:38:42Z,2022-11-13T20:38:42Z,OWNER,"The current API explorer uses details/summary elements for the GET and POST dialogs.
I only want one of these to be open at a time, to reflect that you can make either a GET or a POST.
I just noticed that clicking anywhere else on the page closes both elements, which isn't what I want to happen. Turns out that's because of this code I added as part of Datasette's menu implementation!
https://github.com/simonw/datasette/blob/9f54f00a50a4d950cfd69a0ff3526ae82c858826/datasette/templates/_close_open_menus.html#L2-L15","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1427293909,API explorer tool,
https://github.com/simonw/datasette/issues/1882#issuecomment-1312582512,https://api.github.com/repos/simonw/datasette/issues/1882,1312582512,IC_kwDOBm6k_c5OPGtw,9599,simonw,2022-11-12T22:11:18Z,2022-11-12T22:11:18Z,OWNER,"I like this:
```json
{
""ok"": true,
""database"": ""data"",
""table"": ""agai2n"",
""table_url"": ""http://127.0.0.1:8001/data/agai2n"",
""schema"": ""CREATE TABLE [agai2n] (\n [hello] INTEGER\n)""
}
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1882#issuecomment-1312581121,https://api.github.com/repos/simonw/datasette/issues/1882,1312581121,IC_kwDOBm6k_c5OPGYB,9599,simonw,2022-11-12T22:01:32Z,2022-11-12T22:01:32Z,OWNER,I'm going to change it to `table` in the output AND the input.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1882#issuecomment-1312581008,https://api.github.com/repos/simonw/datasette/issues/1882,1312581008,IC_kwDOBm6k_c5OPGWQ,9599,simonw,2022-11-12T22:00:52Z,2022-11-12T22:00:52Z,OWNER,"Tried out my prototype in the API explorer:
The `""name""` on the output is bothering me a bit - should it be `table_name` or `table` instead?
Problem is I really like `name` for the input, so should it be consistent to have the same name on the output here, or should I aim for consistency with other endpoints that might return `table` rather than the ambiguous `name` elsewhere?","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1882#issuecomment-1312580348,https://api.github.com/repos/simonw/datasette/issues/1882,1312580348,IC_kwDOBm6k_c5OPGL8,9599,simonw,2022-11-12T21:55:54Z,2022-11-12T21:56:45Z,OWNER,"What should this API return?
I think the name of the table (`name`), the URL to that table (`table_url` - for consistency with how faceting API works already) and the schema of the table (`schema`).","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1882#issuecomment-1312575048,https://api.github.com/repos/simonw/datasette/issues/1882,1312575048,IC_kwDOBm6k_c5OPE5I,9599,simonw,2022-11-12T21:22:58Z,2022-11-12T21:22:58Z,OWNER,Need to validate the table name. SQLite supports almost any table name - but they can't contain a newline character and cannot start with `sqlite_` - according to https://stackoverflow.com/questions/3694276/what-are-valid-table-names-in-sqlite/43049720#43049720,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1882#issuecomment-1312556044,https://api.github.com/repos/simonw/datasette/issues/1882,1312556044,IC_kwDOBm6k_c5OPAQM,9599,simonw,2022-11-12T19:29:11Z,2022-11-12T19:29:11Z,OWNER,"Thought of an edge-case: with `sqlite-utils` one feature I really like is that I can pipe data into it without caring if the table already exists or not:
cat data.json | sqlite-utils insert my.db mytable -
How could this new API support that?
I thought about adding a `""create"": true` option to `/db/table/-/insert` which creates the table if it doesn't already exist, but if I do that I'll need to start adding other options to that endpoint - to set the primary key, add foreign keys and suchlike - which would be ignored except for the cases where the table was being created from scratch.
This doesn't feel right to me - I want to keep those options here, on `/db/-/create`.
One idea I had was to implement it such that you can call `/db/-/create` multiple times for the same table, but only if you are using the `""rows""` option. If so, and if the rows can be safely inserted, it would let you do that.
But instead, I'm going to outsource this to the CLI tool I plan to write that feeds data into this API. I'm already planning to use that tool for CSV inserts (so the API doesn't need to accept CSV directly). I think it's a good place for other usability enhancements like ""insert this, creating the table if it does not exist"" as well.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435294468,`/db/-/create` API for creating tables,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311314981,https://api.github.com/repos/simonw/datasette/issues/1883,1311314981,IC_kwDOBm6k_c5OKRQl,9599,simonw,2022-11-11T07:15:48Z,2022-11-11T07:15:48Z,OWNER,I released that fix in Datasette 0.63.1: https://docs.datasette.io/en/stable/changelog.html#v0-63-1,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311299535,https://api.github.com/repos/simonw/datasette/issues/1883,1311299535,IC_kwDOBm6k_c5OKNfP,9599,simonw,2022-11-11T06:54:58Z,2022-11-11T06:54:58Z,OWNER,"This time deployed with:
```
cd demos/apache-proxy
fly deploy --build-arg DATASETTE_REF=8d9a957c6329d26cc1e417b5d6911640d74765eb
```
To ensure the exact commit with the fix.
And that fixed it!
```
% curl -i 'https://datasette-apache-proxy-demo.datasette.io/prefix/fixtures/binary_data?_filter_column=rowid&_filter_op=exact&_filter_value=1&_sort=rowid'
HTTP/2 302
date: Fri, 11 Nov 2022 06:54:45 GMT
server: Fly/b1863e2e7 (2022-11-09)
location: /prefix/fixtures/binary_data?_sort=rowid&rowid__exact=1
link: ; rel=preload
content-type: text/plain
x-proxied-by: Apache2 Debian
via: 2 fly.io
fly-request-id: 01GHJQGBSXBR7E53TY0EKMQ9PA-sjc
```
","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311292463,https://api.github.com/repos/simonw/datasette/issues/1883,1311292463,IC_kwDOBm6k_c5OKLwv,9599,simonw,2022-11-11T06:44:24Z,2022-11-11T06:44:24Z,OWNER,"Modifying that test to the following does indeed cause a failure:
```python
def test_base_url_affects_filter_redirects(app_client_base_url_prefix):
response = app_client_base_url_prefix.get(
""/fixtures/binary_data?_filter_column=rowid&_filter_op=exact&_filter_value=1&_sort=rowid""
)
assert response.status == 302
assert (
response.headers[""location""]
== ""/prefix/fixtures/binary_data?_sort=rowid&rowid__exact=1""
)
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311291632,https://api.github.com/repos/simonw/datasette/issues/1883,1311291632,IC_kwDOBm6k_c5OKLjw,9599,simonw,2022-11-11T06:43:00Z,2022-11-11T06:43:00Z,OWNER,"https://datasette-apache-proxy-demo.datasette.io/prefix/-/asgi-scope is useful:
It confirms that `/prefix/` is nowhere to be seen in the incoming request data:
```
'path': '/-/asgi-scope',
'query_string': b'',
'raw_path': b'/-/asgi-scope',
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311290115,https://api.github.com/repos/simonw/datasette/issues/1883,1311290115,IC_kwDOBm6k_c5OKLMD,9599,simonw,2022-11-11T06:40:14Z,2022-11-11T06:41:56Z,OWNER,"I modified that config file to have this line instead:
```
ProxyPass /prefix/ http://127.0.0.1:8001/ nocanon
```
And then deployed it by running:
flyctl deploy --build-arg DATASETTE_REF=main
This does NOT seem to have fixed the bug:
```
~ % curl -i 'https://datasette-apache-proxy-demo.datasette.io/prefix/fixtures/binary_data?_filter_column=rowid&_filter_op=exact&_filter_value=1&_sort=rowid'
HTTP/2 302
date: Fri, 11 Nov 2022 06:40:01 GMT
server: Fly/b1863e2e7 (2022-11-09)
location: /fixtures/binary_data?_sort=rowid&rowid__exact=1
link: ; rel=preload
content-type: text/plain
x-proxied-by: Apache2 Debian
via: 2 fly.io
fly-request-id: 01GHJPNCF51CJ626EWZEHK2CH9-sjc
```
https://datasette-apache-proxy-demo.datasette.io/prefix/-/versions seems to confirm that this is the latest deployed version (0.63), so it looks like the deploy worked.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311286593,https://api.github.com/repos/simonw/datasette/issues/1883,1311286593,IC_kwDOBm6k_c5OKKVB,9599,simonw,2022-11-11T06:34:09Z,2022-11-11T06:34:09Z,OWNER,"https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxypass includes this note:
> Normally, mod_proxy will canonicalise ProxyPassed URLs. But this may be incompatible with some backends, particularly those that make use of *PATH_INFO*. The optional *nocanon* keyword suppresses this and passes the URL path ""raw"" to the backend.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311284537,https://api.github.com/repos/simonw/datasette/issues/1883,1311284537,IC_kwDOBm6k_c5OKJ05,9599,simonw,2022-11-11T06:30:38Z,2022-11-11T06:30:38Z,OWNER,"Is there a chance that it's Apache that's messing with that `location:` header here, not Datasette?
https://github.com/simonw/datasette/blob/bbaab3b38ec2ce5944239ffbe2dd53328df40fff/demos/apache-proxy/000-default.conf#L7-L13","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311283301,https://api.github.com/repos/simonw/datasette/issues/1883,1311283301,IC_kwDOBm6k_c5OKJhl,9599,simonw,2022-11-11T06:28:38Z,2022-11-11T06:29:33Z,OWNER,"`path_with_added_args(request, redirect_params)` should be preserving the current path from the request.
https://github.com/simonw/datasette/blob/bbaab3b38ec2ce5944239ffbe2dd53328df40fff/datasette/utils/__init__.py#L273-L286","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311282970,https://api.github.com/repos/simonw/datasette/issues/1883,1311282970,IC_kwDOBm6k_c5OKJca,9599,simonw,2022-11-11T06:28:05Z,2022-11-11T06:28:05Z,OWNER,Relevant code: https://github.com/simonw/datasette/blob/bbaab3b38ec2ce5944239ffbe2dd53328df40fff/datasette/views/table.py#L227-L249,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311280709,https://api.github.com/repos/simonw/datasette/issues/1883,1311280709,IC_kwDOBm6k_c5OKI5F,9599,simonw,2022-11-11T06:25:27Z,2022-11-11T06:25:27Z,OWNER,"I tried adding this test but it passed! I expected it to fail:
```python
def test_base_url_affects_filter_redirects(app_client_base_url_prefix):
response = app_client_base_url_prefix.get(
""/prefix/fixtures/binary_data?_filter_column=rowid&_filter_op=exact&_filter_value=1&_sort=rowid""
)
assert response.status == 302
assert (
response.headers[""location""]
== ""/prefix/fixtures/binary_data?_sort=rowid&rowid__exact=1""
)
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",1435917503,Errors when using table filters behind a proxy,
https://github.com/simonw/datasette/issues/1883#issuecomment-1311278678,https://api.github.com/repos/simonw/datasette/issues/1883,1311278678,IC_kwDOBm6k_c5OKIZW,9599,simonw,2022-11-11T06:22:37Z,2022-11-11T06:22:37Z,OWNER,"If you view source on that page the HTML looks correct:
```html