Updated developers documentation
[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 ## Starting from scratch
6
7 For making a Datasette connector for your favorite database files, you need to inherit from `datasette_connectors.Connector`. Then, you can specify your connector type in the class property `connector_type` and, very important, you should set `connection_class` property with a class that inherits from `datasette_connectors.Connection` and implements a method for opening your database files.
8
9 For example, in [Datasette-PyTables](https://github.com/PyTables/datasette-pytables) the next class definition is used:
10
11     import tables
12     import datasette_connectors as dc
13
14     class PyTablesConnection(dc.Connection):
15         def __init__(self, path, connector):
16             super().__init__(path, connector)
17             self.h5file = tables.open_file(path)
18
19     class PyTablesConnector(dc.Connector):
20         connector_type = 'pytables'
21         connection_class = PyTablesConnection
22
23 ## Tables inspection
24
25 Datasette needs some data about your database so you have to provide it overwriting some methods in your custom connector. For that, the connector stores and instance of the class set in `connection_class` in the property `conn`, so you can use `self.conn` to access to your database in order to retrieve that data.
26
27 The methods that must be overwritten are:
28
29 * **table_names(self)**: a list of table names
30 * **hidden_table_names(self)**: a list of hidden table names
31 * **detect_spatialite(self)**: a boolean indicating if geometry_columns exists
32 * **view_names(self)**: a list of view names
33 * **table_count(self, table_name)**: an integer with the rows count of the table
34 * **table_info(self, table_name)**: a list of dictionaries with columns description
35 * **foreign_keys(self, table_name)**: a list of dictionaries with foreign keys description
36 * **table_exists(self, table_name)**: a boolean indicating if table exists in the database
37 * **table_definition(self, table_type, table_name)**: a string with a 'CREATE TABLE' sql definition
38 * **indices_definition(self, table_name)**: a list of strings with 'CREATE INDEX' sql definitions
39
40 ## Returning results
41
42 Datasette uses SQL for specifying the queries, so your connector has to accept SQL and execute it. Overwriting `execute` method you can receive the query in SQL format and return some results.
43
44 The `Connector.execute()` method receives:
45
46 * **sql**: the query
47 * **params**: a dictionary with the params used in the query
48 * **truncate**: a boolean saying if the returned data can be separated in pages or not
49 * **custom_time_limit**: an integer with a time limit for the execution of the query in seconds
50 * **page_size**: the number of rows a page can contain
51 * **log_sql_errors**: a boolean saying if errors has to be logged
52
53 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.
54
55 Note: Sometimes, Datasette make queries to `sqlite_master`; you need to keep it in mind.
56
57 The `Connector.execute()` method has to return a tuple with:
58
59 * a list of rows; each row is a dictionary with the field name as key and the field value as value
60 * 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
61 * a tuple with the description of the columns in the form `(('c1',), ('c2',), ...)`