Explanation about Row class
[datasette-connectors.git] / DEVELOPERS.md
1 # How to make connectors for other data sources?
2
3 With Datasette-Connectors you can use [Datasette](https://github.com/simonw/datasette) for publishing data in your own format, not only SQLite, to the Internet with a JSON API. For this, you have to make connectors using the interface that is described here.
4
5 ## Tables inspection
6
7 First of all, you need to implement a special method called `inspect` that receives the path of the file as an argument and returns a tuple formed by a dictionary with tables info, a list with views name and a string identifying the connector. Each entry in the dictionary for tables info has the next structure:
8
9     tables['table_name'] = {
10         'name': 'table_name',
11         'columns': ['c1', 'c2'],
12         'primary_keys': [],
13         'count': 100,
14         'label_column': None,
15         'hidden': False,
16         'fts_table': None,
17         'foreign_keys': {'incoming': [], 'outgoing': []}
18
19 This structure is used in [Datasette-PyTables](https://github.com/PyTables/datasette-pytables). In your case, you may need additional entries like primary keys or foreign keys.
20
21 ## Returning results
22
23 Datasette uses SQL for specifying the queries, so your connector has to accept SQL and execute it. The next class and methods are needed:
24
25     class Connection:
26         def __init__(self, path):
27             ...
28
29         def execute(self, sql, params=None, truncate=False, page_size=None, max_returned_rows=None):
30             ...
31
32 The `Connection.execute()` method receives:
33
34 * **sql**: the query
35 * **params**: a dictionary with the params used in the query
36 * **truncate**: a boolean saying if the returned data can be separated in pages or not
37 * **page_size**: the number of rows a page can contain
38 * **max_returned_rows**: the maximum number of rows Datasette expects
39
40 Probably, you'll need to parse the SQL query if your data container has its own style for queries, but other databases could work with the SQL queries without requiring any parsing.
41
42 Note: Sometimes, Datasette make queries to `sqlite_master`; you need to keep it in mind.
43
44 The `Connection.execute()` method has to return a tuple with:
45
46 * a list of rows (Datasette expects something similar to SQLite rows)
47 * a boolean saying if the data is truncated, i.e., if we return all the rows or there are more rows than the maximum indicated in max_returned_rows
48 * a tuple with the description of the columns in the form `(('c1',), ('c2',), ...)`
49
50 ## Rows format
51
52 Datasette receives the results from the queries with SQLite row instances, so you need to return your rows in a similar way.
53
54 For example, if you have the next query:
55
56     SELECT name FROM persons
57
58 you need to return an object that allows to do things like:
59
60     row[0] == 'Susan'
61     row['name'] == 'Susan'
62     [c for c in row] == ['Susan']
63     json.dumps(row)
64
65 Datasette-Connectors provides you a Row class that extends `list` object to get it, but as long as you implement a similar interface, you can develop your own implementation too.