Add basic migrations
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
__all__ = ['commons', 'datalite_decorator', 'fetch', 'datalite']
|
||||
__all__ = ['commons', 'datalite_decorator', 'fetch', 'migrations', 'datalite']
|
||||
|
||||
from .datalite_decorator import datalite
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from typing import Any, Optional, Dict
|
||||
from dataclasses import Field
|
||||
from typing import Any, Optional, Dict, List
|
||||
import sqlite3 as sql
|
||||
|
||||
|
||||
def _convert_type(type_: Optional[type], type_overload: Dict[Optional[type], str]) -> str:
|
||||
@@ -34,3 +36,51 @@ def _convert_sql_format(value: Any) -> str:
|
||||
return '"' + str(value).replace("b'", "")[:-1] + '"'
|
||||
else:
|
||||
return str(value)
|
||||
|
||||
|
||||
def _get_table_cols(cur: sql.Cursor, table_name: str) -> List[str]:
|
||||
"""
|
||||
Get the column data of a table.
|
||||
|
||||
:param cur: Cursor in database.
|
||||
:param table_name: Name of the table.
|
||||
:return: the information about columns.
|
||||
"""
|
||||
cur.execute(f"PRAGMA table_info({table_name});")
|
||||
return [row_info[1] for row_info in cur.fetchall()][1:]
|
||||
|
||||
|
||||
def _get_default(default_object: object, type_overload: Dict[Optional[type], str]) -> str:
|
||||
"""
|
||||
Check if the field's default object is filled,
|
||||
if filled return the string to be put in the,
|
||||
database.
|
||||
:param default_object: The default field of the field.
|
||||
:param type_overload: Type overload table.
|
||||
:return: The string to be put on the table statement,
|
||||
empty string if no string is necessary.
|
||||
"""
|
||||
if type(default_object) in type_overload:
|
||||
return f' DEFAULT {_convert_sql_format(default_object)}'
|
||||
return ""
|
||||
|
||||
|
||||
def _create_table(class_: type, cursor: sql.Cursor, type_overload: Dict[Optional[type], str]) -> None:
|
||||
"""
|
||||
Create the table for a specific dataclass given
|
||||
:param class_: A dataclass.
|
||||
:param cursor: Current cursor instance.
|
||||
:param type_overload: Overload the Python -> SQLDatatype table
|
||||
with a custom table, this is that custom table.
|
||||
:return: None.
|
||||
"""
|
||||
fields: List[Field] = [class_.__dataclass_fields__[key] for
|
||||
key in class_.__dataclass_fields__.keys()]
|
||||
fields.sort(key=lambda field: field.name) # Since dictionaries *may* be unsorted.
|
||||
sql_fields = ', '.join(f"{field.name} {_convert_type(field.type, type_overload)}"
|
||||
f"{_get_default(field.default, type_overload)}" for field in fields)
|
||||
sql_fields = "obj_id INTEGER PRIMARY KEY AUTOINCREMENT, " + sql_fields
|
||||
cursor.execute(f"CREATE TABLE IF NOT EXISTS {class_.__name__.lower()} ({sql_fields});")
|
||||
|
||||
type_table: Dict[Optional[type], str] = {None: "NULL", int: "INTEGER", float: "REAL",
|
||||
str: "TEXT", bytes: "BLOB"}
|
||||
|
||||
@@ -6,40 +6,7 @@ a class bound to a sqlite3 database.
|
||||
from typing import Dict, Optional, List, Callable
|
||||
from dataclasses import Field, asdict
|
||||
import sqlite3 as sql
|
||||
from .commons import _convert_sql_format, _convert_type
|
||||
|
||||
|
||||
def _get_default(default_object: object, type_overload: Dict[Optional[type], str]) -> str:
|
||||
"""
|
||||
Check if the field's default object is filled,
|
||||
if filled return the string to be put in the,
|
||||
database.
|
||||
:param default_object: The default field of the field.
|
||||
:param type_overload: Type overload table.
|
||||
:return: The string to be put on the table statement,
|
||||
empty string if no string is necessary.
|
||||
"""
|
||||
if type(default_object) in type_overload:
|
||||
return f' DEFAULT {_convert_sql_format(default_object)}'
|
||||
return ""
|
||||
|
||||
|
||||
def _create_table(class_: type, cursor: sql.Cursor, type_overload: Dict[Optional[type], str]) -> None:
|
||||
"""
|
||||
Create the table for a specific dataclass given
|
||||
:param class_: A dataclass.
|
||||
:param cursor: Current cursor instance.
|
||||
:param type_overload: Overload the Python -> SQLDatatype table
|
||||
with a custom table, this is that custom table.
|
||||
:return: None.
|
||||
"""
|
||||
fields: List[Field] = [class_.__dataclass_fields__[key] for
|
||||
key in class_.__dataclass_fields__.keys()]
|
||||
fields.sort(key=lambda field: field.name) # Since dictionaries *may* be unsorted.
|
||||
sql_fields = ', '.join(f"{field.name} {_convert_type(field.type, type_overload)}"
|
||||
f"{_get_default(field.default, type_overload)}" for field in fields)
|
||||
sql_fields = "obj_id INTEGER PRIMARY KEY AUTOINCREMENT, " + sql_fields
|
||||
cursor.execute(f"CREATE TABLE IF NOT EXISTS {class_.__name__.lower()} ({sql_fields});")
|
||||
from .commons import _convert_sql_format, _convert_type, _create_table, type_table
|
||||
|
||||
|
||||
def _create_entry(self) -> None:
|
||||
@@ -105,14 +72,14 @@ def datalite(db_path: str, type_overload: Optional[Dict[Optional[type], str]] =
|
||||
:return: The new dataclass.
|
||||
"""
|
||||
def decorator(dataclass_: type, *args_i, **kwargs_i):
|
||||
type_table: Dict[Optional[type], str] = {None: "NULL", int: "INTEGER", float: "REAL",
|
||||
str: "TEXT", bytes: "BLOB"}
|
||||
types_table = type_table.copy()
|
||||
if type_overload is not None:
|
||||
type_table.update(type_overload)
|
||||
types_table.update(type_overload)
|
||||
with sql.connect(db_path) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
_create_table(dataclass_, cur, type_table)
|
||||
_create_table(dataclass_, cur, types_table)
|
||||
setattr(dataclass_, 'db_path', db_path) # We add the path of the database to class itself.
|
||||
setattr(dataclass_, 'types_table', types_table) # We add the type table for migration.
|
||||
dataclass_.create_entry = _create_entry
|
||||
dataclass_.remove_entry = _remove_entry
|
||||
dataclass_.update_entry = _update_entry
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sqlite3 as sql
|
||||
from typing import List, Tuple, Any
|
||||
from .commons import _convert_sql_format
|
||||
from .commons import _convert_sql_format, _get_table_cols
|
||||
|
||||
|
||||
def _insert_pagination(query: str, page: int, element_count: int) -> str:
|
||||
@@ -35,18 +35,6 @@ def is_fetchable(class_: type, obj_id: int) -> bool:
|
||||
return bool(cur.fetchall())
|
||||
|
||||
|
||||
def _get_table_cols(cur: sql.Cursor, table_name: str) -> List[str]:
|
||||
"""
|
||||
Get the column data of a table.
|
||||
|
||||
:param cur: Cursor in database.
|
||||
:param table_name: Name of the table.
|
||||
:return: the information about columns.
|
||||
"""
|
||||
cur.execute(f"PRAGMA table_info({table_name});")
|
||||
return [row_info[1] for row_info in cur.fetchall()][1:]
|
||||
|
||||
|
||||
def fetch_equals(class_: type, field: str, value: Any, ) -> Any:
|
||||
"""
|
||||
Fetch a class_ type variable from its bound db.
|
||||
|
||||
160
datalite/migrations.py
Normal file
160
datalite/migrations.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
Migrations module deals with migrating data when the object
|
||||
definitions change. This functions deal with Schema Migrations.
|
||||
"""
|
||||
from dataclasses import Field
|
||||
from os.path import exists
|
||||
from typing import Dict, Tuple, List
|
||||
import sqlite3 as sql
|
||||
|
||||
from .commons import _create_table, _get_table_cols
|
||||
|
||||
|
||||
def _get_db_table(class_: type) -> Tuple[str, str]:
|
||||
"""
|
||||
Check if the class is a datalite class, the database exists
|
||||
and the table exists. Return database and table names.
|
||||
|
||||
:param class_: A datalite class.
|
||||
:return: A tuple of database and table names.
|
||||
"""
|
||||
database_name: str = getattr(class_, 'db_path', None)
|
||||
if not database_name:
|
||||
raise TypeError(f"{class_.__name__} is not a datalite class.")
|
||||
table_name: str = class_.__name__.lower()
|
||||
if not exists(database_name):
|
||||
raise FileNotFoundError(f"{database_name} does not exist")
|
||||
with sql.connect(database_name) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
cur.execute(f"SELECT count(*) FROM sqlite_master "
|
||||
f"WHERE type='table' AND name='{table_name}';")
|
||||
count: int = int(cur.fetchone()[0])
|
||||
if not count:
|
||||
raise FileExistsError(f"Table, {table_name}, already exists.")
|
||||
return database_name, table_name
|
||||
|
||||
|
||||
def _get_table_column_names(database_name: str, table_name: str) -> Tuple[str]:
|
||||
"""
|
||||
Get the column names of table.
|
||||
|
||||
:param database_name: Name of the database the table
|
||||
resides in.
|
||||
:param table_name: Name of the table.
|
||||
:return: A tuple holding the column names of the table.
|
||||
"""
|
||||
with sql.connect(database_name) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
cols: List[str] = _get_table_cols(cur, table_name)
|
||||
return tuple(cols)
|
||||
|
||||
|
||||
def _copy_records(database_name: str, table_name: str):
|
||||
"""
|
||||
Copy all records from a table.
|
||||
|
||||
:param database_name: Name of the database.
|
||||
:param table_name: Name of the table.
|
||||
:return: A generator holding dataclass asdict representations.
|
||||
"""
|
||||
with sql.connect(database_name) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
cur.execute(f'SELECT * FROM {table_name};')
|
||||
values = cur.fetchall()
|
||||
keys = _get_table_cols(cur, table_name)
|
||||
keys.insert(0, 'obj_id')
|
||||
records = (dict(zip(keys, value)) for value in values)
|
||||
return records
|
||||
|
||||
|
||||
def _drop_table(database_name: str, table_name: str) -> None:
|
||||
"""
|
||||
Drop a table.
|
||||
|
||||
:param database_name: Name of the database.
|
||||
:param table_name: Name of the table to be dropped.
|
||||
:return: None.
|
||||
"""
|
||||
with sql.connect(database_name) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
cur.execute(f'DROP TABLE {table_name};')
|
||||
con.commit()
|
||||
|
||||
|
||||
def _modify_records(data, col_to_del: Tuple[str], col_to_add: Tuple[str],
|
||||
flow: Dict[str, str]) -> Tuple[Dict[str, str]]:
|
||||
"""
|
||||
Modify the asdict records in accordance
|
||||
with schema migration rules provided.
|
||||
|
||||
:param data: Data kept as asdict in tuple.
|
||||
:param col_to_del: Column names to delete.
|
||||
:param col_to_add: Column names to add.
|
||||
:param flow: A dictionary that explain
|
||||
if the data from a deleted column
|
||||
will be transferred to a column
|
||||
to be added.
|
||||
:return: The modified data records.
|
||||
"""
|
||||
records = []
|
||||
for record in data:
|
||||
record_mod = {}
|
||||
for key in record.keys():
|
||||
if key in col_to_del and key in flow:
|
||||
record_mod[flow[key]] = record[key]
|
||||
else:
|
||||
record_mod[key] = record[key]
|
||||
for key_to_add in col_to_add:
|
||||
if key_to_add not in record_mod:
|
||||
record_mod[key_to_add] = None
|
||||
records.append(record_mod)
|
||||
return records
|
||||
|
||||
|
||||
def _migrate_records(class_: type, database_name: str, data,
|
||||
col_to_del: Tuple[str], col_to_add: Tuple[str], flow: Dict[str, str]) -> None:
|
||||
"""
|
||||
Migrate the records into the modified table.
|
||||
|
||||
:param class_: Class of the entries.
|
||||
:param database_name: Name of the database.
|
||||
:param data: Data, asdict tuple.
|
||||
:param col_to_del: Columns to be deleted.
|
||||
:param col_to_add: Columns to be added.
|
||||
:param flow: Flow dictionary stating where
|
||||
column data will be transferred.
|
||||
:return: None.
|
||||
"""
|
||||
with sql.connect(database_name) as con:
|
||||
cur: sql.Cursor = con.cursor()
|
||||
_create_table(class_, cur, getattr(class_, 'types_table'))
|
||||
con.commit()
|
||||
new_records = _modify_records(data, col_to_del, col_to_add, flow)
|
||||
for record in new_records:
|
||||
del record['obj_id']
|
||||
class_(**record).create_entry()
|
||||
|
||||
|
||||
def basic_migrate(class_: type, column_transfer: dict = None) -> None:
|
||||
"""
|
||||
Given a class, compare its previous table,
|
||||
delete the fields that no longer exist,
|
||||
create new columns for new fields. If the
|
||||
column_flow parameter is given, migrate elements
|
||||
from previous column to the new ones.
|
||||
|
||||
:param class_: Datalite class to migrate.
|
||||
:param column_transfer: A dictionary showing which
|
||||
columns will be copied to new ones.
|
||||
:return: None.
|
||||
"""
|
||||
database_name, table_name = _get_db_table(class_)
|
||||
table_column_names: Tuple[str] = _get_table_column_names(database_name, table_name)
|
||||
values = class_.__dataclass_fields__.values()
|
||||
data_fields: Tuple[Field] = tuple(field for field in values)
|
||||
data_field_names: Tuple[str] = tuple(field.name for field in data_fields)
|
||||
columns_to_be_deleted: Tuple[str] = tuple(column for column in table_column_names if column not in data_field_names)
|
||||
columns_to_be_added: Tuple[str] = tuple(column for column in data_field_names if column not in table_column_names)
|
||||
records = _copy_records(database_name, table_name)
|
||||
_drop_table(database_name, table_name)
|
||||
_migrate_records(class_, database_name, records, columns_to_be_deleted, columns_to_be_added, column_transfer)
|
||||
BIN
docs/_build/doctrees/datalite.doctree
vendored
BIN
docs/_build/doctrees/datalite.doctree
vendored
Binary file not shown.
BIN
docs/_build/doctrees/environment.pickle
vendored
BIN
docs/_build/doctrees/environment.pickle
vendored
Binary file not shown.
6
docs/_build/html/_sources/datalite.rst.txt
vendored
6
docs/_build/html/_sources/datalite.rst.txt
vendored
@@ -14,4 +14,10 @@ datalite.fetch module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
datalite.migrations module
|
||||
----------------------------
|
||||
|
||||
.. automodule:: datalite.migrations
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
55
docs/_build/html/datalite.html
vendored
55
docs/_build/html/datalite.html
vendored
@@ -36,7 +36,7 @@
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="datalite" href="modules.html" />
|
||||
<link rel="prev" title="Welcome to Datalite’s documentation!" href="index.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
@@ -83,12 +83,10 @@
|
||||
|
||||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1 current"><a class="reference internal" href="modules.html">datalite</a><ul class="current">
|
||||
<li class="toctree-l2 current"><a class="current reference internal" href="#">datalite package</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#datalite-module">datalite Module</a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="#module-datalite.fetch">datalite.fetch module</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">datalite package</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#datalite-module">datalite Module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#module-datalite.fetch">datalite.fetch module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#module-datalite.migrations">datalite.migrations module</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -137,8 +135,6 @@
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
<li><a href="modules.html">datalite</a> »</li>
|
||||
|
||||
<li>datalite package</li>
|
||||
|
||||
|
||||
@@ -196,10 +192,10 @@
|
||||
</ul>
|
||||
</dd>
|
||||
<dt class="field-even">Returns</dt>
|
||||
<dd class="field-even"><p>All the records of type <a href="#id1"><span class="problematic" id="id2">class_</span></a> in</p>
|
||||
<dd class="field-even"><p>All the records of type <a href="#id1"><span class="problematic" id="id2">class_</span></a> in
|
||||
the bound database as a tuple.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>the bound database as a tuple.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py function">
|
||||
@@ -252,10 +248,10 @@ provided they fit the given condition</p>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt class="field-even">Returns</dt>
|
||||
<dd class="field-even"><p>A tuple of records that fit the given condition</p>
|
||||
<dd class="field-even"><p>A tuple of records that fit the given condition
|
||||
of given type <a href="#id9"><span class="problematic" id="id10">class_</span></a>.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>of given type <a href="#id9"><span class="problematic" id="id10">class_</span></a>.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py function">
|
||||
@@ -270,10 +266,10 @@ provided they fit the given condition</p>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt class="field-even">Returns</dt>
|
||||
<dd class="field-even"><p>A tuple of <a href="#id11"><span class="problematic" id="id12">class_</span></a> type objects whose values</p>
|
||||
<dd class="field-even"><p>A tuple of <a href="#id11"><span class="problematic" id="id12">class_</span></a> type objects whose values
|
||||
come from the <a href="#id13"><span class="problematic" id="id14">class_</span></a>’ bound database.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<p>come from the <a href="#id13"><span class="problematic" id="id14">class_</span></a>’ bound database.</p>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="py function">
|
||||
@@ -316,6 +312,33 @@ given value.</p>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
<div class="section" id="module-datalite.migrations">
|
||||
<span id="datalite-migrations-module"></span><h2>datalite.migrations module<a class="headerlink" href="#module-datalite.migrations" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Migrations module deals with migrating data when the object
|
||||
definitions change. This functions deal with Schema Migrations.</p>
|
||||
<dl class="py function">
|
||||
<dt id="datalite.migrations.basic_migrate">
|
||||
<code class="sig-prename descclassname">datalite.migrations.</code><code class="sig-name descname">basic_migrate</code><span class="sig-paren">(</span><em class="sig-param"><span class="n">class_</span><span class="p">:</span> <span class="n">type</span></em>, <em class="sig-param"><span class="n">column_transfer</span><span class="p">:</span> <span class="n">dict</span> <span class="o">=</span> <span class="default_value">None</span></em><span class="sig-paren">)</span> → None<a class="headerlink" href="#datalite.migrations.basic_migrate" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Given a class, compare its previous table,
|
||||
delete the fields that no longer exist,
|
||||
create new columns for new fields. If the
|
||||
column_flow parameter is given, migrate elements
|
||||
from previous column to the new ones.</p>
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Parameters</dt>
|
||||
<dd class="field-odd"><ul class="simple">
|
||||
<li><p><strong>class</strong> – Datalite class to migrate.</p></li>
|
||||
<li><p><strong>column_transfer</strong> – A dictionary showing which
|
||||
columns will be copied to new ones.</p></li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dt class="field-even">Returns</dt>
|
||||
<dd class="field-even"><p>None.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -328,7 +351,7 @@ given value.</p>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
|
||||
|
||||
<a href="modules.html" class="btn btn-neutral float-left" title="datalite" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
<a href="index.html" class="btn btn-neutral float-left" title="Welcome to Datalite’s documentation!" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
24
docs/_build/html/genindex.html
vendored
24
docs/_build/html/genindex.html
vendored
@@ -150,24 +150,40 @@
|
||||
<h1 id="index">Index</h1>
|
||||
|
||||
<div class="genindex-jumpbox">
|
||||
<a href="#D"><strong>D</strong></a>
|
||||
<a href="#B"><strong>B</strong></a>
|
||||
| <a href="#D"><strong>D</strong></a>
|
||||
| <a href="#F"><strong>F</strong></a>
|
||||
| <a href="#I"><strong>I</strong></a>
|
||||
| <a href="#M"><strong>M</strong></a>
|
||||
|
||||
</div>
|
||||
<h2 id="B">B</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="datalite.html#datalite.migrations.basic_migrate">basic_migrate() (in module datalite.migrations)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
<h2 id="D">D</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="datalite.html#datalite.datalite">datalite() (in module datalite)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
datalite.fetch
|
||||
|
||||
<ul>
|
||||
<li><a href="datalite.html#module-datalite.fetch">module</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li>
|
||||
datalite.migrations
|
||||
|
||||
<ul>
|
||||
<li><a href="datalite.html#module-datalite.migrations">module</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
@@ -209,6 +225,8 @@
|
||||
|
||||
<ul>
|
||||
<li><a href="datalite.html#module-datalite.fetch">datalite.fetch</a>
|
||||
</li>
|
||||
<li><a href="datalite.html#module-datalite.migrations">datalite.migrations</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
|
||||
1
docs/_build/html/index.html
vendored
1
docs/_build/html/index.html
vendored
@@ -162,6 +162,7 @@ sqlite3 database.</p>
|
||||
<li class="toctree-l1"><a class="reference internal" href="datalite.html">datalite package</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="datalite.html#datalite-module">datalite Module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="datalite.html#module-datalite.fetch">datalite.fetch module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="datalite.html#module-datalite.migrations">datalite.migrations module</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
4
docs/_build/html/objects.inv
vendored
4
docs/_build/html/objects.inv
vendored
@@ -2,4 +2,6 @@
|
||||
# Project: Datalite
|
||||
# Version:
|
||||
# The remainder of this file is compressed using zlib.
|
||||
xڝ<EFBFBD><EFBFBD>N<EFBFBD>0<10>w?<3F>!XS<58>ڙ<EFBFBD><01>cu<63>/<2F><><EFBFBD>ncG<63><47><EFBFBD><EFBFBD><EFBFBD>x<12><>C<EFBFBD><43><04><12>~<7E><><EFBFBD><EFBFBD><EFBFBD><1B><>6<EFBFBD><36><EFBFBD><01><><EFBFBD>윎<EFBFBD>;<3B><>Q^ձ<><D5B1>(ԷRR<52>u<EFBFBD>7<EFBFBD>tLp{f<16><><EFBFBD><EFBFBD>w<EFBFBD><77><17>e<EFBFBD>rXƖ<58>o<EFBFBD><6F><EFBFBD>\Ƶ<>*Z<><5A><EFBFBD><EFBFBD>^<5E>ڰ=<0E><><EFBFBD><EFBFBD><10>Y<1B><>8s<38><73><17>_<EFBFBD>"U<><55><EFBFBD><EFBFBD><EFBFBD>a<>'hTzH<7A>:<3A><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>X<EFBFBD><58> z<><7A> <09><>gא<67><D790><EFBFBD>|<7C><><0E><12><><EFBFBD><EFBFBD>@<40>=J%Vz<17><>Y<EFBFBD><59><EFBFBD><EFBFBD><EFBFBD><EFBFBD>c<EFBFBD><63><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40><0E><><EFBFBD><EFBFBD> <09>$<24><><EFBFBD>f_`26<32>
|
||||
xڝ<EFBFBD>=N<EFBFBD>0<10><EFBFBD><EFBFBD>b<10><><EFBFBD>ݚ<EFBFBD>i%
|
||||
<EFBFBD>hbO<0B><>ڎ <1D><>z<EFBFBD><7A>8<EFBFBD><38><EFBFBD> <09><><1A>y<EFBFBD><79><<3C>%F<>*R!<21><0E><><EFBFBD>Y<EFBFBD><59>Y.<2E>h<EFBFBD><68>
|
||||
<EFBFBD>ٷRSm<>';Mp{af9<66><39><EFBFBD>Z<EFBFBD><5A>+<2B>&<26><>갍<><EAB08D>3<EFBFBD>HUo<55><چ<><DA86><EFBFBD>-<2D>ը
|
||||
5
docs/_build/html/py-modindex.html
vendored
5
docs/_build/html/py-modindex.html
vendored
@@ -169,6 +169,11 @@
|
||||
<td>   
|
||||
<a href="datalite.html#module-datalite.fetch"><code class="xref">datalite.fetch</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="datalite.html#module-datalite.migrations"><code class="xref">datalite.migrations</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
2
docs/_build/html/searchindex.js
vendored
2
docs/_build/html/searchindex.js
vendored
@@ -1 +1 @@
|
||||
Search.setIndex({docnames:["datalite","index","modules"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,sphinx:56},filenames:["datalite.rst","index.rst","modules.rst"],objects:{"datalite.fetch":{fetch_all:[0,0,1,""],fetch_equals:[0,0,1,""],fetch_from:[0,0,1,""],fetch_if:[0,0,1,""],fetch_range:[0,0,1,""],fetch_where:[0,0,1,""],is_fetchable:[0,0,1,""]},datalite:{datalite:[0,0,1,""],fetch:[0,1,0,"-"]}},objnames:{"0":["py","function","Python function"],"1":["py","module","Python module"]},objtypes:{"0":"py:function","1":"py:module"},terms:{"class":0,"default":0,"int":0,"new":0,"return":0,The:0,add:0,all:0,ani:0,arg:[],argument:[],bind:[0,1],bool:0,bound:0,callabl:0,can:1,check:0,class_:0,close:0,come:0,common:[],condit:0,content:[],convert:[],count:0,create_entri:0,data:0,databas:[0,1],dataclass:[0,1],datalite_decor:[],db_path:0,decor:[],defin:[],dict:0,dictionari:0,each:0,element:0,element_count:0,fetch:[1,2],fetch_al:0,fetch_equ:0,fetch_from:0,fetch_if:0,fetch_rang:0,fetch_wher:0,fetchabl:0,fetchal:0,field:0,fit:0,from:0,get:[],given:0,ids:0,index:1,insert:[],insert_pagin:[],is_fetch:0,its:0,kwarg:[],librari:1,mean:0,method:0,modifi:[],modul:[1,2],none:0,number:[],obj_id:0,object:0,option:0,overload:0,packag:[1,2],page:[0,1],pagin:[],param:[],paramet:0,path:0,provid:0,python:1,queri:[],rang:0,range_:0,record:0,remove_entri:0,remove_from:[],retriev:0,search:1,simpl:1,sqlite3:[0,1],str:0,submodul:[],taken:0,thei:0,thi:0,tupl:0,type:0,type_overload:0,uniqu:0,update_entri:0,use:1,used:[],valu:0,variabl:0,which:0,whose:0},titles:["datalite package","Welcome to Datalite\u2019s documentation!","datalite"],titleterms:{common:[],content:1,datalit:[0,1,2],datalite_decor:[],document:1,fetch:0,indic:1,modul:0,packag:0,submodul:[],tabl:1,welcom:1}})
|
||||
Search.setIndex({docnames:["datalite","index"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":1,sphinx:56},filenames:["datalite.rst","index.rst"],objects:{"datalite.fetch":{fetch_all:[0,0,1,""],fetch_equals:[0,0,1,""],fetch_from:[0,0,1,""],fetch_if:[0,0,1,""],fetch_range:[0,0,1,""],fetch_where:[0,0,1,""],is_fetchable:[0,0,1,""]},"datalite.migrations":{basic_migrate:[0,0,1,""]},datalite:{datalite:[0,0,1,""],fetch:[0,1,0,"-"],migrations:[0,1,0,"-"]}},objnames:{"0":["py","function","Python function"],"1":["py","module","Python module"]},objtypes:{"0":"py:function","1":"py:module"},terms:{"class":0,"default":0,"function":0,"int":0,"new":0,"return":0,The:0,add:0,all:0,ani:0,arg:[],argument:[],basic_migr:0,bind:[0,1],bool:0,bound:0,callabl:0,can:1,chang:0,check:0,class_:0,close:0,column:0,column_flow:0,column_transf:0,come:0,common:[],compar:0,condit:0,content:[],convert:[],copi:0,count:0,creat:0,create_entri:0,data:0,databas:[0,1],dataclass:[0,1],datalite_decor:[],db_path:0,deal:0,decor:[],defin:[],definit:0,delet:0,dict:0,dictionari:0,each:0,element:0,element_count:0,exist:0,fetch:1,fetch_al:0,fetch_equ:0,fetch_from:0,fetch_if:0,fetch_rang:0,fetch_wher:0,fetchabl:0,fetchal:0,field:0,fit:0,from:0,get:[],given:0,ids:0,index:1,insert:[],insert_pagin:[],is_fetch:0,its:0,kwarg:[],librari:1,longer:0,mean:0,method:0,migrat:1,modifi:[],modul:1,none:0,number:[],obj_id:0,object:0,ones:0,option:0,overload:0,packag:1,page:[0,1],pagin:[],param:[],paramet:0,path:0,previou:0,provid:0,python:1,queri:[],rang:0,range_:0,record:0,remove_entri:0,remove_from:[],retriev:0,schema:0,search:1,show:0,simpl:1,sqlite3:[0,1],str:0,submodul:[],tabl:0,taken:0,thei:0,thi:0,tupl:0,type:0,type_overload:0,uniqu:0,update_entri:0,use:1,used:[],valu:0,variabl:0,when:0,which:0,whose:0},titles:["datalite package","Welcome to Datalite\u2019s documentation!"],titleterms:{common:[],content:1,datalit:[0,1],datalite_decor:[],document:1,fetch:0,indic:1,migrat:0,modul:0,packag:0,submodul:[],tabl:1,welcom:1}})
|
||||
@@ -23,7 +23,7 @@ copyright = '2020, Ege Ozkan'
|
||||
author = 'Ege Ozkan'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = 'v0.5.2'
|
||||
release = 'v0.5.3'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
@@ -14,4 +14,10 @@ datalite.fetch module
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
datalite.migrations module
|
||||
----------------------------
|
||||
|
||||
.. automodule:: datalite.migrations
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
datalite
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
datalite
|
||||
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
||||
|
||||
setuptools.setup(
|
||||
name="datalite", # Replace with your own username
|
||||
version="0.5.2",
|
||||
version="0.5.3",
|
||||
author="Ege Ozkan",
|
||||
author_email="egeemirozkan24@gmail.com",
|
||||
description="A small package that binds dataclasses to an sqlite database",
|
||||
|
||||
@@ -6,6 +6,8 @@ from dataclasses import dataclass, asdict
|
||||
from math import floor
|
||||
from os import remove
|
||||
|
||||
from datalite.migrations import basic_migrate, _drop_table
|
||||
|
||||
|
||||
@datalite(db_path='test.db')
|
||||
@dataclass
|
||||
@@ -28,6 +30,17 @@ class FetchClass:
|
||||
def __eq__(self, other):
|
||||
return asdict(self) == asdict(other)
|
||||
|
||||
@datalite(db_path='test.db')
|
||||
@dataclass
|
||||
class Migrate1:
|
||||
ordinal: int
|
||||
|
||||
|
||||
@datalite(db_path='test.db')
|
||||
@dataclass
|
||||
class Migrate2:
|
||||
cardinal: int
|
||||
|
||||
|
||||
def getValFromDB(obj_id = 1):
|
||||
with connect('test.db') as db:
|
||||
@@ -127,5 +140,25 @@ class DatabaseFetchPaginationCalls(unittest.TestCase):
|
||||
def tearDown(self) -> None:
|
||||
[obj.remove_entry() for obj in self.objs]
|
||||
|
||||
|
||||
class DatabaseMigration(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.objs = [Migrate1(i) for i in range(10)]
|
||||
[obj.create_entry() for obj in self.objs]
|
||||
|
||||
def testBasicMigrate(self):
|
||||
global Migrate1, Migrate2
|
||||
Migrate1 = Migrate2
|
||||
Migrate1.__name__ = 'Migrate1'
|
||||
basic_migrate(Migrate1, {'ordinal': 'cardinal'})
|
||||
t_objs = fetch_all(Migrate1)
|
||||
self.assertEqual([obj.ordinal for obj in self.objs], [obj.cardinal for obj in t_objs])
|
||||
|
||||
def tearDown(self) -> None:
|
||||
t_objs = fetch_all(Migrate1)
|
||||
[obj.remove_entry() for obj in t_objs]
|
||||
_drop_table('test.db', 'migrate1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user