Add basic migrations

This commit is contained in:
Ege Emir Özkan
2020-08-14 11:45:39 +03:00
parent a0bb5704eb
commit 2e14aeaace
19 changed files with 339 additions and 87 deletions

View File

@@ -1,3 +1,3 @@
__all__ = ['commons', 'datalite_decorator', 'fetch', 'datalite']
__all__ = ['commons', 'datalite_decorator', 'fetch', 'migrations', 'datalite']
from .datalite_decorator import datalite

View File

@@ -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:
@@ -33,4 +35,52 @@ def _convert_sql_format(value: Any) -> str:
elif isinstance(value, bytes):
return '"' + str(value).replace("b'", "")[:-1] + '"'
else:
return str(value)
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"}

View File

@@ -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

View File

@@ -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.
@@ -112,7 +100,7 @@ def fetch_if(class_: type, condition: str, page: int = 0, element_count: int = 1
:param page: Which page to retrieve, default all. (0 means closed).
:param element_count: Element count in each page.
:return: A tuple of records that fit the given condition
of given type class_.
of given type class_.
"""
table_name = class_.__name__.lower()
with sql.connect(getattr(class_, 'db_path')) as con:
@@ -146,7 +134,7 @@ def fetch_range(class_: type, range_: range) -> tuple:
:param class_: Class of the records.
:param range_: Range of the object ids.
:return: A tuple of class_ type objects whose values
come from the class_' bound database.
come from the class_' bound database.
"""
return tuple(fetch_from(class_, obj_id) for obj_id in range_ if is_fetchable(class_, obj_id))
@@ -159,7 +147,7 @@ def fetch_all(class_: type, page: int = 0, element_count: int = 10) -> tuple:
:param page: Which page to retrieve, default all. (0 means closed).
:param element_count: Element count in each page.
:return: All the records of type class_ in
the bound database as a tuple.
the bound database as a tuple.
"""
try:
db_path = getattr(class_, 'db_path')

160
datalite/migrations.py Normal file
View 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)

Binary file not shown.

Binary file not shown.

View File

@@ -14,4 +14,10 @@ datalite.fetch module
:undoc-members:
:show-inheritance:
datalite.migrations module
----------------------------
.. automodule:: datalite.migrations
:members:
:undoc-members:
:show-inheritance:

View File

@@ -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 Datalites 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> &raquo;</li>
<li><a href="modules.html">datalite</a> &raquo;</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> &#x2192; 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 Datalites documentation!" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>

View File

@@ -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>

View File

@@ -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>

View File

@@ -2,4 +2,6 @@
# Project: Datalite
# Version:
# The remainder of this file is compressed using zlib.
<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><><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>
<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>ը

View File

@@ -169,6 +169,11 @@
<td>&#160;&#160;&#160;
<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>&#160;&#160;&#160;
<a href="datalite.html#module-datalite.migrations"><code class="xref">datalite.migrations</code></a></td><td>
<em></em></td></tr>
</table>

View File

@@ -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}})

View File

@@ -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 ---------------------------------------------------

View File

@@ -14,4 +14,10 @@ datalite.fetch module
:undoc-members:
:show-inheritance:
datalite.migrations module
----------------------------
.. automodule:: datalite.migrations
:members:
:undoc-members:
:show-inheritance:

View File

@@ -1,7 +0,0 @@
datalite
========
.. toctree::
:maxdepth: 4
datalite

View File

@@ -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",

View File

@@ -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()