datasette icon indicating copy to clipboard operation
datasette copied to clipboard

Documentation and unit tests for urls.row() urls.row_blob() methods

Open simonw opened this issue 4 years ago • 7 comments

https://github.com/simonw/datasette/blob/5db7ae3ce165ded57c7fb1cfbdb3258b1cf06c10/datasette/app.py#L1307-L1313

simonw avatar Oct 25 '20 00:10 simonw

The row_path part of these really isn't very user friendly, since you need to properly URL-encode the values. The safest way to do so is by calling this obscure, undocumented utility function: https://github.com/simonw/datasette/blob/f0bd2d05f5f7832df4879822afb99d2096c00d48/datasette/utils/init.py#L84-L98

This feels like it should be improved before I turn it into a documented API.

(Note that this API deals with a row that is a potentially-nested dictionary - not with a sqlite3.Row object.)

simonw avatar Oct 31 '20 22:10 simonw

Also related: row is now available to render_cell() hook as-of this issue:

  • https://github.com/simonw/datasette/issues/1300

simonw avatar Jul 10 '22 16:07 simonw

I'm considering changing these functions to accept the row object itself:

 def row(self, database, table, row): 
     ...
  
 def row_blob(self, database, table, row, column): 
     ... 

Just one catch: in order to generate the correct row_path we need to know the primary keys for that table. We can look those up based on having access to database and table, but doing so requires an await ... operation - and these functions are not async.

simonw avatar Jul 10 '22 16:07 simonw

None of the potential solutions for that problem are particularly appealing:

  • Make these URL generation methods async - should the other ones be async too for consistency?
  • Have some kind of mechanism that calculates and caches the pks for each table somewhere which can then be accessed here without an async call
  • Require pks is passed to these methods, having been looked up elsewhere. This is a bit gross but may end up being the best solution

simonw avatar Jul 10 '22 16:07 simonw

If I do require pks to be passed here, maybe I could make those available to the render_cell() plugin hook to at least make this a bit more pleasant for plugin authors to use?

Current hook: https://docs.datasette.io/en/latest/plugin_hooks.html#render-cell-row-value-column-table-database-datasette

https://github.com/simonw/datasette/blob/035dc5e7b95142d4a700819a8cc4ff64aefe4efe/datasette/hookspecs.py#L62-L64

The hook is called in two places in the codebase - when rendering a table (pks variable is already in scope here):

https://github.com/simonw/datasette/blob/6373bb341457e5becfd5b67792ac2c8b9ed7c384/datasette/views/table.py#L897-L904

And when rendering an arbitrary query:

https://github.com/simonw/datasette/blob/6373bb341457e5becfd5b67792ac2c8b9ed7c384/datasette/views/database.py#L377-L384

Note that in that second one table is None (which is also called out in the documentation) - pks would be None here too.

simonw avatar Jul 10 '22 16:07 simonw

I think the best way to do this is to change the method signatures to:

 def row(self, database, table, row, pks): 
     ...
  
 def row_blob(self, database, table, row, pks, column): 
     ... 

simonw avatar Jul 10 '22 16:07 simonw

But do I need to pass the use_rowid boolean here as well, as used by def path_from_row_pks(row, pks, use_rowid, quote=True)? Or can I derive that from the fact that pks is an empty tuple?

simonw avatar Jul 10 '22 16:07 simonw