diff --git a/docs/python-api.rst b/docs/python-api.rst index 1dd0ceb..f877b10 100644 --- a/docs/python-api.rst +++ b/docs/python-api.rst @@ -45,6 +45,8 @@ You can also access tables using the ``.table()`` method like so: Using this factory function allows you to set :ref:`python_api_table_configuration`. +.. _python_api_tables: + Listing tables ============== @@ -60,6 +62,23 @@ You can also iterate through the table objects themselves using the ``.tables`` >>> db.tables [] +.. _python_api_views: + +Listing views +============= + +``.table_views()`` shows you a list of views in the database:: + + >>> db.table_views() + ['good_dogs'] + +You can also iterate through view objects using the ``.views`` property:: + + >>> db.views + [] + +View objects work the same as table objects, except any attempts to insert or update data will throw an error. + .. _python_api_rows: Listing rows diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index 3fcb133..2127b63 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -128,10 +128,22 @@ class Database: sql = "select name from sqlite_master where {}".format(" AND ".join(where)) return [r[0] for r in self.conn.execute(sql).fetchall()] + def view_names(self): + return [ + r[0] + for r in self.conn.execute( + "select name from sqlite_master where type = 'view'" + ).fetchall() + ] + @property def tables(self): return [self[name] for name in self.table_names()] + @property + def views(self): + return [self[name] for name in self.view_names()] + def execute_returning_dicts(self, sql, params=None): cursor = self.conn.execute(sql, params or tuple()) keys = [d[0] for d in cursor.description] @@ -404,7 +416,13 @@ class Table: ): self.db = db self.name = name + self.is_view = False self.exists = self.name in self.db.table_names() + if not self.exists: + # It might be a view + self.is_view = self.name in self.db.view_names() + if self.is_view: + self.exists = True self._defaults = dict( pk=pk, foreign_keys=foreign_keys, @@ -420,7 +438,8 @@ class Table: ) def __repr__(self): - return "
".format( + return "<{} {}{}>".format( + "View" if self.is_view else "Table", self.name, " (does not exist yet)" if not self.exists diff --git a/tests/test_introspect.py b/tests/test_introspect.py index d3ab27a..5e5b9ec 100644 --- a/tests/test_introspect.py +++ b/tests/test_introspect.py @@ -6,6 +6,11 @@ def test_table_names(existing_db): assert ["foo"] == existing_db.table_names() +def test_view_names(fresh_db): + fresh_db.create_view("foo_view", "select 1") + assert ["foo_view"] == fresh_db.view_names() + + def test_table_names_fts4(existing_db): existing_db["woo"].insert({"title": "Hello"}).enable_fts( ["title"], fts_version="FTS4" @@ -36,6 +41,16 @@ def test_tables(existing_db): assert "foo" == existing_db.tables[0].name +def test_views(fresh_db): + fresh_db.create_view("foo_view", "select 1") + assert 1 == len(fresh_db.views) + view = fresh_db.views[0] + assert view.is_view + assert view.exists + assert "foo_view" == view.name + assert "" == repr(view) + + def test_count(existing_db): assert 3 == existing_db["foo"].count