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
|
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:
|
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):
|
elif isinstance(value, bytes):
|
||||||
return '"' + str(value).replace("b'", "")[:-1] + '"'
|
return '"' + str(value).replace("b'", "")[:-1] + '"'
|
||||||
else:
|
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"}
|
||||||
|
|||||||
@@ -6,40 +6,7 @@ a class bound to a sqlite3 database.
|
|||||||
from typing import Dict, Optional, List, Callable
|
from typing import Dict, Optional, List, Callable
|
||||||
from dataclasses import Field, asdict
|
from dataclasses import Field, asdict
|
||||||
import sqlite3 as sql
|
import sqlite3 as sql
|
||||||
from .commons import _convert_sql_format, _convert_type
|
from .commons import _convert_sql_format, _convert_type, _create_table, type_table
|
||||||
|
|
||||||
|
|
||||||
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});")
|
|
||||||
|
|
||||||
|
|
||||||
def _create_entry(self) -> None:
|
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.
|
:return: The new dataclass.
|
||||||
"""
|
"""
|
||||||
def decorator(dataclass_: type, *args_i, **kwargs_i):
|
def decorator(dataclass_: type, *args_i, **kwargs_i):
|
||||||
type_table: Dict[Optional[type], str] = {None: "NULL", int: "INTEGER", float: "REAL",
|
types_table = type_table.copy()
|
||||||
str: "TEXT", bytes: "BLOB"}
|
|
||||||
if type_overload is not None:
|
if type_overload is not None:
|
||||||
type_table.update(type_overload)
|
types_table.update(type_overload)
|
||||||
with sql.connect(db_path) as con:
|
with sql.connect(db_path) as con:
|
||||||
cur: sql.Cursor = con.cursor()
|
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_, '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_.create_entry = _create_entry
|
||||||
dataclass_.remove_entry = _remove_entry
|
dataclass_.remove_entry = _remove_entry
|
||||||
dataclass_.update_entry = _update_entry
|
dataclass_.update_entry = _update_entry
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import sqlite3 as sql
|
import sqlite3 as sql
|
||||||
from typing import List, Tuple, Any
|
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:
|
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())
|
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:
|
def fetch_equals(class_: type, field: str, value: Any, ) -> Any:
|
||||||
"""
|
"""
|
||||||
Fetch a class_ type variable from its bound db.
|
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 page: Which page to retrieve, default all. (0 means closed).
|
||||||
:param element_count: Element count in each page.
|
:param element_count: Element count in each page.
|
||||||
:return: A tuple of records that fit the given condition
|
:return: A tuple of records that fit the given condition
|
||||||
of given type class_.
|
of given type class_.
|
||||||
"""
|
"""
|
||||||
table_name = class_.__name__.lower()
|
table_name = class_.__name__.lower()
|
||||||
with sql.connect(getattr(class_, 'db_path')) as con:
|
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 class_: Class of the records.
|
||||||
:param range_: Range of the object ids.
|
:param range_: Range of the object ids.
|
||||||
:return: A tuple of class_ type objects whose values
|
: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))
|
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 page: Which page to retrieve, default all. (0 means closed).
|
||||||
:param element_count: Element count in each page.
|
:param element_count: Element count in each page.
|
||||||
:return: All the records of type class_ in
|
:return: All the records of type class_ in
|
||||||
the bound database as a tuple.
|
the bound database as a tuple.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
db_path = getattr(class_, 'db_path')
|
db_path = getattr(class_, 'db_path')
|
||||||
|
|||||||
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:
|
:undoc-members:
|
||||||
:show-inheritance:
|
: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="index" title="Index" href="genindex.html" />
|
||||||
<link rel="search" title="Search" href="search.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>
|
</head>
|
||||||
|
|
||||||
<body class="wy-body-for-nav">
|
<body class="wy-body-for-nav">
|
||||||
@@ -83,12 +83,10 @@
|
|||||||
|
|
||||||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||||
<ul class="current">
|
<ul class="current">
|
||||||
<li class="toctree-l1 current"><a class="reference internal" href="modules.html">datalite</a><ul class="current">
|
<li class="toctree-l1 current"><a class="current reference internal" href="#">datalite package</a><ul>
|
||||||
<li class="toctree-l2 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-l3"><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-l3"><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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -137,8 +135,6 @@
|
|||||||
|
|
||||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||||
|
|
||||||
<li><a href="modules.html">datalite</a> »</li>
|
|
||||||
|
|
||||||
<li>datalite package</li>
|
<li>datalite package</li>
|
||||||
|
|
||||||
|
|
||||||
@@ -196,10 +192,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="field-even">Returns</dt>
|
<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>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p>the bound database as a tuple.</p>
|
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
|
|
||||||
<dl class="py function">
|
<dl class="py function">
|
||||||
@@ -252,10 +248,10 @@ provided they fit the given condition</p>
|
|||||||
</ul>
|
</ul>
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="field-even">Returns</dt>
|
<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>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p>of given type <a href="#id9"><span class="problematic" id="id10">class_</span></a>.</p>
|
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
|
|
||||||
<dl class="py function">
|
<dl class="py function">
|
||||||
@@ -270,10 +266,10 @@ provided they fit the given condition</p>
|
|||||||
</ul>
|
</ul>
|
||||||
</dd>
|
</dd>
|
||||||
<dt class="field-even">Returns</dt>
|
<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>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<p>come from the <a href="#id13"><span class="problematic" id="id14">class_</span></a>’ bound database.</p>
|
|
||||||
</dd></dl>
|
</dd></dl>
|
||||||
|
|
||||||
<dl class="py function">
|
<dl class="py function">
|
||||||
@@ -316,6 +312,33 @@ given value.</p>
|
|||||||
</dl>
|
</dl>
|
||||||
</dd></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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -328,7 +351,7 @@ given value.</p>
|
|||||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
<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>
|
</div>
|
||||||
|
|
||||||
|
|||||||
24
docs/_build/html/genindex.html
vendored
24
docs/_build/html/genindex.html
vendored
@@ -150,24 +150,40 @@
|
|||||||
<h1 id="index">Index</h1>
|
<h1 id="index">Index</h1>
|
||||||
|
|
||||||
<div class="genindex-jumpbox">
|
<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="#F"><strong>F</strong></a>
|
||||||
| <a href="#I"><strong>I</strong></a>
|
| <a href="#I"><strong>I</strong></a>
|
||||||
| <a href="#M"><strong>M</strong></a>
|
| <a href="#M"><strong>M</strong></a>
|
||||||
|
|
||||||
</div>
|
</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>
|
<h2 id="D">D</h2>
|
||||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||||
<td style="width: 33%; vertical-align: top;"><ul>
|
<td style="width: 33%; vertical-align: top;"><ul>
|
||||||
<li><a href="datalite.html#datalite.datalite">datalite() (in module datalite)</a>
|
<li><a href="datalite.html#datalite.datalite">datalite() (in module datalite)</a>
|
||||||
</li>
|
</li>
|
||||||
</ul></td>
|
|
||||||
<td style="width: 33%; vertical-align: top;"><ul>
|
|
||||||
<li>
|
<li>
|
||||||
datalite.fetch
|
datalite.fetch
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="datalite.html#module-datalite.fetch">module</a>
|
<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>
|
</li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
</ul></td>
|
</ul></td>
|
||||||
@@ -209,6 +225,8 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="datalite.html#module-datalite.fetch">datalite.fetch</a>
|
<li><a href="datalite.html#module-datalite.fetch">datalite.fetch</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="datalite.html#module-datalite.migrations">datalite.migrations</a>
|
||||||
</li>
|
</li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
</ul></td>
|
</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-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#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.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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
4
docs/_build/html/objects.inv
vendored
4
docs/_build/html/objects.inv
vendored
@@ -2,4 +2,6 @@
|
|||||||
# Project: Datalite
|
# Project: Datalite
|
||||||
# Version:
|
# Version:
|
||||||
# The remainder of this file is compressed using zlib.
|
# 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>   
|
<td>   
|
||||||
<a href="datalite.html#module-datalite.fetch"><code class="xref">datalite.fetch</code></a></td><td>
|
<a href="datalite.html#module-datalite.fetch"><code class="xref">datalite.fetch</code></a></td><td>
|
||||||
<em></em></td></tr>
|
<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>
|
</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'
|
author = 'Ege Ozkan'
|
||||||
|
|
||||||
# The full version, including alpha/beta/rc tags
|
# The full version, including alpha/beta/rc tags
|
||||||
release = 'v0.5.2'
|
release = 'v0.5.3'
|
||||||
|
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|||||||
@@ -14,4 +14,10 @@ datalite.fetch module
|
|||||||
:undoc-members:
|
:undoc-members:
|
||||||
:show-inheritance:
|
: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(
|
setuptools.setup(
|
||||||
name="datalite", # Replace with your own username
|
name="datalite", # Replace with your own username
|
||||||
version="0.5.2",
|
version="0.5.3",
|
||||||
author="Ege Ozkan",
|
author="Ege Ozkan",
|
||||||
author_email="egeemirozkan24@gmail.com",
|
author_email="egeemirozkan24@gmail.com",
|
||||||
description="A small package that binds dataclasses to an sqlite database",
|
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 math import floor
|
||||||
from os import remove
|
from os import remove
|
||||||
|
|
||||||
|
from datalite.migrations import basic_migrate, _drop_table
|
||||||
|
|
||||||
|
|
||||||
@datalite(db_path='test.db')
|
@datalite(db_path='test.db')
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -28,6 +30,17 @@ class FetchClass:
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return asdict(self) == asdict(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):
|
def getValFromDB(obj_id = 1):
|
||||||
with connect('test.db') as db:
|
with connect('test.db') as db:
|
||||||
@@ -127,5 +140,25 @@ class DatabaseFetchPaginationCalls(unittest.TestCase):
|
|||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
[obj.remove_entry() for obj in self.objs]
|
[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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user