{ "info": { "author": "Sanish Maharjan", "author_email": "sanishmaharjan@lftechnology.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6" ], "description": "# Google Cloud Spanner-ORM:\nSpanner ORM is a simple and small ORM. It easy to learn and intuitive to use.\n\n[![PYPI][pypi-image]][pypi-url]\n[![Version][version-image]][pypi-url]\n\n[pypi-image]: https://img.shields.io/pypi/v/spannerorm.svg?style=flat-square\n[pypi-url]: https://pypi.org/project/spannerorm/\n[version-image]: https://img.shields.io/pypi/pyversions/spannerorm.svg?style=flat-square \n```\nproduct:\n name: Google Cloud Spanner ORM\n short_name: spannerorm\n url: https://github.com/leapfrogtechnology/spanner-orm.git\n description:\n spannerdb ORM is a highly scalable, efficient Google Cloud Spanner ORM.\n```\n\n## Features\n- A small, simple ORM\n- Support Cloud Spanner Database\n- python 2.7 - 3.6\n- Connection pooling\n- Support DB Transaction\n- Support DB Migration\n\n## Table of contents\n\n* [Installation](#installation)\n* [Connection](#connection)\n* [BaseModel and DataType](#basemodel-and-datatype)\n * [DataType](#datatype)\n * [DataType Field arguments](#datatype-field-arguments)\n * [Relation](#relation)\n * [Meta](#meta)\n * [Model Decorator](#model-decorator)\n* [Query Records](#query-records)\n * [count(criteria, transaction)](#countcriteria-transaction)\n * [find(criteria, transaction)](#findcriteria-transaction)\n * [find_by_pk(pk, criteria, transaction)](#find_by_pkpk-criteria-transaction)\n * [find_all(criteria, transaction)](#find_allcriteria-transaction)\n * [Criteria](#criteria)\n * [criteria.condition(conditions, operator)](#criteriaconditionconditions-operator)\n * [criteria.add_condition(condition, operator)](#criteriaadd_conditioncondition-operator)\n * [Criteria condition](#criteria-condition)\n * [Criteria Condition Operators](#criteria-condition-operators)\n * [criteria.limit](#criterialimit)\n * [criteria.offset](#criteriaoffset)\n * [criteria.set_order_by(order_by_props, order)](#criteriaset_order_byorder_by_props-order)\n * [criteria.oin_with(relation, join_type)](#criteriaoin_withrelation-join_type)\n* [Block Records INSERT | UPDATE](#block-records-insert--update)\n * [insert_block(raw_data_list, transaction)](#insert_blockraw_data_list-transaction)\n * [update_block(cls, raw_data_list, transaction)](#update_blockcls-raw_data_list-transaction)\n* [Save Record (ADD / UPDATE)](#save-record-add--update)\n * [save(model_obj, transaction)](#savemodel_obj-transaction)\n * [save_all(model_obj_list, transaction)](#save_allmodel_obj_list-transaction)\n * [update_by_pk(pk, data, transaction)](#update_by_pkpk-data-transaction)\n* [Delete Records](#delete-records)\n * [delete_one(criteria, transaction)](#delete_onecriteria-transaction)\n * [delete_by_pk(pk, transaction)](#delete_by_pkpk-transaction)\n* [Running with Transaction](#running-with-transaction)\n* [Model object functions](#model-object-functions)\n * [set_props(raw_data)](#set_propsraw_data)\n * [equals(obj)](#equalsobj)\n * [is_new_record()](#is_new_record)\n * [get_pk_value()](#get_pk_value)\n * [get_errors()](#get_errors)\n * [validate()](#validate)\n * [validate_property(prop)](#validate_propertyprop)\n* [Model class functions](#model-class-functions)\n * [get_meta_data()](#get_meta_data)\n * [primary_key_property()](#primary_key_property)\n * [has_property(property_name)](#has_propertyproperty_name)\n* [SpannerDb](#spannerdb)\n * [SpannerDb.execute_query(query_string, params, transaction)](#spannerdbexecute_queryquery_string-params-transaction)\n * [SpannerDb.execute_ddl_query(ddl_query_string)](#spannerdbexecute_ddl_queryddl_query_string)\n * [SpannerDb.insert_data(table_name, columns, data)](#spannerdbinsert_datatable_name-columns-data)\n * [SpannerDb.update_data(table_name, columns, data)](#spannerdbupdate_datatable_name-columns-data)\n * [SpannerDb.save_data(table_name, columns, data)](#spannerdbsave_datatable_name-columns-data)\n * [SpannerDb.delete_data(table_name, id_list)](#spannerdbdelete_datatable_name-id_list)\n* [Database Migration](#database-migration)\n * [setup Db Migration](#setup-db-migration)\n * [Db Migration commands](#db-migration-commands)\n\n\n\n\n## Installation\n- Install pip (If not install in your system)\n```bash\nsudo apt-get install python-pip\n```\n- Install client library\n```bash\n pip install --upgrade google-cloud-spanner\n```\n- Installing with Git\n```bash\n git clone https://github.com/leapfrogtechnology/spanner-orm.git\n cd spanner-orm\n python setup.py install\n```\n- Download `Service account json`\n - Go to the `GCP Console` > `Service accounts`\n - Download key from service account list by clicking at `action` > `create key`\n\n## Connection\nThe spannerorm Connection object represents a connection to a database. The Connection class is instantiated with all \nthe information needed to open a connection to a database, and then can be used.\n\n```python\nfrom spannerorm import Connection\n\ninstance_id = 'develop'\ndatabase_id = 'auth'\nservice_account_json = '/home/leapfrog/personal-data/python-work/opensource/spanner-orm/service_account.json'\npool_size = 10\ntime_out = 5\nping_interval = 300\n\nConnection.config(instance_id=instance_id,\n database_id=database_id,\n service_account_json=service_account_json,\n pool_size=pool_size,\n time_out=time_out,\n ping_interval=ping_interval)\n```\n\n| Parameter | DataType | Required / Optional | Description |\n| --------------------------- | ----------- | ------------------- | -------------------------------------------------- |\n| instance_id | String | Required | Cloud Spanner Instance Id |\n| database_id | String | Required | Cloud Spanner Database |\n| service_account_json | String | Required | Service account json's file full path |\n| pool_size | Integer | Optional | Max number of database pool connection |\n| time_out | Integer | Optional | In seconds, to wait for a returned session |\n| ping_interval | Integer | Optional | Interval at which to ping sessions |\n\n## BaseModel and DataType\nBaseModel classes, DataType instances, BaseModel instances, Relation instances all map to database concepts:\n\n| Class \\ Instance | Corresponds to\u2026 |\n| ---------------------------------- | ------------------------------------------- |\n| BaseModel | Database table |\n| DataType instance | Column on a table |\n| BaseModel instance | Row in a database table |\n| Relation instance | Database relational |\n\n### DataType\nThe `DataType` class is used to describe the mapping of Model attributes to database columns. Each field type has a \ncorresponding SQL storage class (i.e. varchar, int), and conversion between DataType and underlying storage is handled \ntransparently.\n\n| DataType | Corresponding Spanner Data Type |\n| ----------- | ------------------------------- |\n| StringField | STRING |\n| IntegerField | INT64 |\n| FloatField | FLOAT64 |\n| BoolField | BOOL |\n| BytesField | BYTES |\n| TimeStampField | TIMESTAMP |\n| DateField | DATE |\n| EnumField | STRING |\n\n### DataType Field arguments\n- StringField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | False | bool | is allow null value, Default True |\n| default | False | str | Default value |\n| max_length | False | int | Max allow string length |\n| reg_exr | False | str | Regex expression |\n\neg: \n```python\nfrom spannerorm import StringField\n\n_email = StringField(db_column='email', null=False, reg_exr='^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$')\n```\n\n\n- IntegerField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | int | Default value |\n| min_value | Optional | int | Max allow value |\n| max_value | Optional | int | Min allow value |\n\n- FloatField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | float | Default value |\n| min_value | Optional | float | Max allow string length |\n| max_value | Optional | float | Regex expression |\n| decimal_places | Optional | int | Regex expression |\n\n\n- BoolField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | bool | Default value |\n\n- BytesField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | str | Default value |\n\n- TimeStampField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | float | Default value |\n\n- DateField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | -------------- | -------------------------------------------------------- |\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | datetime.date | Default value |\n\n- EnumField arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| db_column | Required | str | Corresponding db column |\n| null | Optional | bool | is allow null value, Default True |\n| default | Optional | str | Default value |\n| enum_list | Require | list | Enum values |\n\n- Simple Example\n```python\nfrom time import time\nfrom uuid import uuid4\nfrom spannerorm import BaseModel, IntegerField, StringField, BoolField, TimeStampField, DateField\n\n\nclass Sample(BaseModel):\n # Db Fields\n _id = StringField(db_column='id', null=False)\n _name = StringField(db_column='name', null=False, reg_exr='^[A-Z][ a-z]+')\n _modified_at = TimeStampField(db_column='modified_at', null=True, default=time())\n\n\n @property\n @StringField.get\n def id(self):\n return self._id\n\n @id.setter\n @StringField.set\n def id(self, id):\n self._id = id\n\n @property\n @StringField.get\n def name(self):\n return self._name\n\n @name.setter\n @StringField.set\n def name(self, name):\n self._name = name\n\n @property\n @TimeStampField.get\n def modified_at(self):\n return self._modified_at\n\n @modified_at.setter\n @TimeStampField.set\n def modified_at(self, created):\n self._modified_at = created\n\n class Meta:\n db_table = 'sample'\n primary_key = 'id'\n\n @classmethod\n def generate_pk(cls):\n return uuid4()\n\n```\n\n### Relation\nRelation class is a special field type that allows one model to reference another.\n\n| RelationType | Description |\n| ------------------- | ----------------------------------------------------------- |\n| OneToOne | OneToOne relation with reference model |\n| ManyToOne | ManyToOne relation with reference model |\n| OneToMany | OneToMany relation with reference model |\n| ManyToMany | ManyToMany relation with reference model |\n\n- RelationType arguments\n\n| Arguments | Require / Optional | Type | Description |\n| ------------------- | ------------------ | ------------ | -----------------------------------------------------------|\n| join_on | Required | str | Corresponding db column |\n| relation_name | Required | bool | is allow null value, Default True |\n| refer_to | Required | str | Default value |\n\n\n- Simple Example : \n - User Model\n\n ```python\n import hashlib\n import role\n from time import time\n from uuid import uuid4\n from spannerorm import BaseModel, StringField, BoolField, TimeStampField, ManyToOne\n\n\n class User(BaseModel):\n # Db column Fields\n _id = StringField(db_column='id', null=False)\n _name = StringField(db_column='name', null=False)\n _role_id = StringField(db_column='role_id', null=False)\n _created_at = TimeStampField(db_column='created_at', null=False, default=time())\n\n # Relational Fields\n _role = ManyToOne(join_on='role_id', relation_name='role', refer_to='id')\n\n @property\n @StringField.get\n def id(self):\n return self._id\n\n @id.setter\n @StringField.set\n def id(self, id):\n self._id = id\n\n @property\n @StringField.get\n def name(self):\n return self._name\n\n @name.setter\n @StringField.set\n def name(self, name):\n self._name = name\n\n @property\n @StringField.get\n def role_id(self):\n return self._role_id\n\n @role_id.setter\n @StringField.set\n def role_id(self, role_id):\n self._role_id = role_id\n\n @property\n @TimeStampField.get\n def created_at(self):\n return self._created_at\n\n @created_at.setter\n @TimeStampField.set\n def created_at(self, created_at):\n self._created_at = created_at\n\n @property\n @ManyToOne.get\n def role(self):\n return self._role\n\n @role.setter\n @ManyToOne.set\n def role(self, data):\n self._role = data\n\n class Meta:\n db_table = 'users'\n primary_key = 'id'\n\n @classmethod\n def relations(cls):\n return {\n 'role': role.Role\n }\n\n @classmethod\n def generate_pk(cls):\n return uuid4()\n\n ```\n\n**_Note_**: Model `Field` & `Relation Field` name should be `_[prop_name]` form & should have property with `getter` & `setter`\n\n### Meta\nModel-specific configuration is placed in a special class called `Meta`. Meta Class created inside model class.\n```python\n # This Meta class placed inside mode class\n class Meta:\n db_table = 'users'\n primary_key = 'id'\n\n @classmethod\n def relations(cls):\n return {\n 'role': role.Role\n }\n\n @classmethod\n def generate_pk(cls):\n return uuid4()\n```\n- db_table: database table map to model\n- primary_key: primary key of db table\n- relations(cls): function return relations that reference to another model.\n- generate_pk(cls): function that generate & return primary key value\n\n### Model Decorator\n- spannerorm decorators\n\n| Decorator | Description |\n| ----------------------- | ----------------------------------------------------------------------- |\n| @StringField.get | StringField getter, should use with `property` decorator |\n| @StringField.set | StringField setter, should use with `setter` decorator |\n| @IntegerField.get | IntegerField getter, should use with `property` decorator |\n| @IntegerField.set | IntegerField setter, should use with `setter` decorator |\n| @FloatField.get | FloatField getter, should use with `property` decorator |\n| @FloatField.set | FloatField setter, should use with `setter` decorator |\n| @BoolField.get | BoolField getter, should use with `property` decorator |\n| @BoolField.set | BoolField setter, should use with `setter` decorator |\n| @BytesField.get | BytesField getter, should use with `property` decorator |\n| @BytesField.set | BytesField setter, should use with `setter` decorator |\n| @TimeStampField.get | TimeStampField getter, should use with `property` decorator |\n| @TimeStampField.set | TimeStampField setter, should use with `setter` decorator |\n| @DateField.get | DateField getter, should use with `property` decorator |\n| @DateField.set | DateField setter, should use with `setter` decorator |\n| @EnumField.get | EnumField getter, should use with `property` decorator |\n| @EnumField.set | EnumField setter, should use with `setter` decorator |\n| | |\n| @OneToOne.get | OneToOne relation getter, should use with `property` decorator |\n| @OneToOne.set | OneToOne relation setter, should use with `setter` decorator |\n| @OneToMany.get | OneToMany relation getter, should use with `property` decorator |\n| @OneToMany.set | OneToMany relation setter, should use with `setter` decorator |\n| @ManyToOne.get | ManyToOne relation getter, should use with `property` decorator |\n| @ManyToOne.set | ManyToOne relation setter, should use with `setter` decorator |\n| @ManyToMany.get | ManyToMany relation getter, should use with `property` decorator |\n| @ManyToMany.set | ManyToMany relation setter, should use with `setter` decorator |\n\n## Query Records\nModel query records public methods\n\n### count(criteria, transaction)\nCount record filter by criteria\n```markdown\n- params:\n - criteria:\n - Filter criteria\n - Type: Criteria\n - Default Value: None\n - Optional\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return:\n - Count of record\n - Type: bool\n```\n\neg: With out join\n```python\ncriteria = Criteria()\ncriteria.condition([(User.role_id, '=', '1'), (User.organization_id, '=', '4707145032222247178')])\nuser = User.count(criteria)\n```\n\n### find(criteria, transaction)\nFetch single record data filter by criteria\n```markdown\n- params:\n - criteria:\n - Filter criteria\n - Type: Criteria\n - Default Value: None\n - Optional\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return:\n - If exist return Model object else None\n - Type: Model object | None\n```\n\neg: With out join\n```python\ncriteria = Criteria()\ncriteria.condition([(User.role_id, '=', '1'), (User.organization_id, '=', '4707145032222247178')])\nuser = User.find(criteria)\n```\n\neg: With join\n```python\ncriteria = Criteria()\ncriteria.join_with(User.role)\nuser = User.find()\nuser_role = user.role\n```\n### find_by_pk(pk, criteria, transaction)\nFetch record by primary key filter by criteria\n```markdown\n- params:\n - pk:\n - Primary Key value\n - Type: str | int (depending on primary key data type)\n - Required\n - criteria:\n - Filter criteria\n - Type: Criteria\n - Default Value: None\n - Optional\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return:\n - If exist return Model object else None\n - Type: Model object | None\n```\n\neg:\n```python\ncriteria = Criteria()\ncriteria.add_condition((User.is_deleted, '=', False))\nuser = User.find_by_pk('-300113230644022007', criteria)\n```\n\n### find_all(criteria, transaction)\nFetch records filter by criteria\n```markdown\n- params:\n - criteria:\n - Filter criteria\n - Type: Criteria\n - Default Value: None\n - Optional\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return:\n - list of model\n -Type: list\n```\n\neg: With out join\n```python\ncriteria = Criteria()\ncriteria.condition([(User.email, 'LIKE', '%@lftechnology.com')])\ncriteria.add_condition((User.role_id, 'IN', ['1', '2']))\ncriteria.add_condition((User.organization_id, 'NOT IN', ['4707145032222247178']))\ncriteria.set_order_by(User.email, 'ASC')\ncriteria.limit = 2\n\nusers = User.find_all(criteria)\n```\n\neg: With ManyToOne Join\n```python\ncriteria = Criteria()\ncriteria.join_with(User.role)\ncriteria.join_with(User.organization)\ncriteria.condition([(User.email, 'LIKE', '%@lftechnology.com')])\ncriteria.set_order_by(User.email, 'ASC')\ncriteria.limit = 2\n\nusers = User.find_all(criteria)\n\nfor user in users:\n print(user.role)\n```\n\neg: With OneToMany Join\n```python\ncriteria = Criteria()\ncriteria.join_with(Role.users)\ncriteria.add_condition((User.email, '=', 'mjsanish+admin@gmail.com'))\ncriteria.set_order_by(User.email, order='DESC')\nrole = Role.find(criteria)\nusers = role.users\n\nfor user in users:\n print(user)\n```\n\n#### Criteria\n`Criteria` object represents a query filter criteria, such as conditions, ordering by, limit/offset. \n\n##### criteria.condition(conditions, operator)\nSet criteria condition that filter result set\n```markdown\n- params:\n - conditions:\n - List of conditions\n - Type: list\n - Required\n - operator:\n - Sql operator\n - Type: str\n - Default: AND\n - Allow values: [AND | OR]\n - Optional\n```\n\neg: `WHERE users.email LIKe '%@lftechnology.com'`\n```python\ncriteria = Criteria()\ncriteria.condition([(User.email, 'LIKE', '%@lftechnology.com')])\n```\n\neg: `WHERE users.email LIKe '%@lftechnology.com' OR users.role_id IN ('1', '2')`\n```python\ncriteria = Criteria()\ncriteria.condition([(User.email, 'LIKE', '%@lftechnology.com'), (User.role_id, 'IN', ['1', '2'])], 'OR')\n```\n\neg: `WHERE user.name LIKE '%lf%' AND (users.active=true OR users.is_deleted=false)`\n```python\ncriteria = Criteria()\ncriteria.condition([((User.name, 'LIKE', '%lf%'), 'AND', ((User.active, '=', True), 'OR', (User.is_deleted, '=', False)))])\n```\n\neg: `WHERE (((users.name LIKE '%lf%') AND (users.active=true OR users.is_deleted=false)) OR (users.user_name='mjsanish' AND users.password='pass')) OR (users.role_id IN (1, 3))`\n```python\ncriteria = Criteria()\ncriteria.condition([(((User.name, 'LIKE', '%lf%'), 'AND', ((User.active, '=', True), 'OR', (User.is_deleted, '=', False))), 'OR', ((User.user_name, '=', 'mjsanish'), 'AND', (User.password, '=', 'pass'))), (User.role_id, 'IN', [1, 3])], 'OR')\n\n```\n\n##### criteria.add_condition(condition, operator)\nAdd criteria condition that filter result set\n```markdown\n- params:\n - condition:\n - Filter condition\n - Type: tuple\n - Required\n - operator:\n - Condition operator\n - Type: str\n - Default: AND\n - Allow values: [AND | OR]\n - Optional\n```\n\neg: `WHERE users.email LIKe '%@lftechnology.com'`\n```python\ncriteria = Criteria()\ncriteria.add_condition([(User.email, 'LIKE', '%@lftechnology.com')])\n```\n\neg: `WHERE users.email LIKe '%@lftechnology.com' OR users.role_id IN ('1', '2')`\n```python\ncriteria = Criteria()\ncriteria.condition([(User.email, 'LIKE', '%@lftechnology.com')])\ncriteria.add_condition((User.role_id, 'IN', ['1', '2']), 'OR')\n```\n\neg:\neg: `WHERE user.name LIKE '%lf%' AND (users.active=false OR users.is_deleted=true)`\n```python\ncriteria = Criteria()\ncriteria.add_condition((User.name, 'LIKE', '%lf%'))\ncriteria.add_condition(((User.active, '=', False), 'OR', (User.is_deleted, '=', True)))\n```\n\n###### Criteria condition\nCriteria `condition` object provide filter cirteria.\n```markdown\n- Type: tuple(3)\n- suntax: (model.property, [= | > | < | >= | <= | <> | IN | NOT IN], value)\n (condition, [AND | OR], condition)\n\n```\n\n###### Criteria Condition Operators\n| Operator | Description | Example |\n| --------- | ---------------------------------------| -------------------------------------------------------------------- |\n| = | Equal | (User.name, '=', 'sanish') |\n| > | Greater Than | (User.points, '>', 100) |\n| < | Less Than | (User.points, '<', 2000) |\n| >= | Greater Than Or Equal | (User.points, '>=', 100) |\n| <= | Less Than Or Equal | (User.points, '<=', 1000) |\n| <> | Not Equal | (User.name, '<>', 'sanish') |\n| LIKE | Search for a pattern | (User.name, 'LIKE', '%sa%') |\n| IN | Search for `In` Multiple values | (User.role_id, 'IN', ['1', '2']) |\n| NOT IN | Search for `Not In` Multiple values | (Task.status, 'NOT IN', ['pending', 'under review']) |\n| AND | Join two condition with `AND` operator | ((User.name, 'LIKE', '%sa%') , 'AND', (User.is_deleted, '=', False)) |\n| OR | Join two condition with `OR` operator | ((User.name, 'LIKE', '%sa%') , 'OR', (User.is_deleted, '=', False)) |\n| IS | Is null | (User.name, 'IS', 'NULL') |\n| IS NOT | Is not null | (User.name, 'IS NOT', 'NULL') |\n\n\n##### criteria.limit\nSetter limit criteria\n```markdown\n- Type: int\n```\n\n##### criteria.offset\nSetter offset criteria\n```markdown\n- Type: int\n```\n\neg: `WHERE users.name LIKE '%lf%' LIMIT 5 OFFSET 10`\n```python\ncriteria = Criteria()\ncriteria.add_condition((User.name, 'LIKE', '%lf%'))\ncriteria.limit = 5\ncriteria.offset = 10\n```\n\n##### criteria.set_order_by(order_by_props, order)\nSet order by criteria\n```markdown\n- params:\n - order_by_props:\n - Order by property or list of order by properties\n - Type: property | list\n - Require\n - order:\n - Define order on asc or desc\n - Type: str\n - Default: ASC\n - Optional\n - Allow values: [ASC | DESC]\n```\n\neg: `WHERE users.name LIKE '%lf%' ORDER BY users.name DESC`\n```python\ncriteria = Criteria()\ncriteria.add_condition((User.name, 'LIKE', '%lf%'))\ncriteria.set_order_by(User.name, 'DESC')\n```\n\neg: `ORDER BY users.name, user.email ASC`\n```python\ncriteria = Criteria()\ncriteria.set_order_by([User.name, User.email])\n```\n\n##### criteria.oin_with(relation, join_type, join_condition)\n- Add join with criteria. For joining should define relation in model\n`````markdown\n- params:\n - relation:\n - Model relation property\n - Type: property\n - Require\n - join_type:\n - Define join type\n - Type: str\n - Default value: 'LEFT'\n - Optional\n - Allow values: [LEFT, RIGHT, FULL]\n - join_condition:\n - Define join condition\n - Type: tuple\n - Optional\n`````\n\neg: `LEFT JOIN users on roles.id=users.role_id WHERE roles.name='admin' AND users.email='mjsanish+admin@gmail.com'` \n```python\ncriteria = Criteria()\ncriteria.join_with(Role.users, join_condition=(User.is_deleted, '=', False))\ncriteria.add_condition((Role.name, '=', 'admin'))\ncriteria.add_condition((User.email, '=', 'mjsanish+admin@gmail.com'))\n```\n\n## Block Records INSERT | UPDATE\nModel Block function allow insert/update lots of data quickly. \n\n### insert_block(raw_data_list, transaction)\nInsert block of data\n```markdown\n- params:\n - raw_data_list:\n - List of data\n - Type: list of dict \n - Require\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n```\n\neg:\n```python\n data_list = [{\n 'email': 'mjsanish+1@gmail.com',\n 'name': 'sanish1',\n \"is_deleted\": False,\n 'organization_id' : '4707145032222247178',\n 'role_id': '1',\n 'created_by': '-1202895510759970011',\n }, {\n 'email': 'mjsanish+2@gmail.com',\n 'name': 'sanish2',\n \"is_deleted\": False,\n 'organization_id' : '4707145032222247178',\n 'role_id': '1',\n 'created_by': '-1202895510759970011',\n }]\n\n users = User.insert_block(data_list)\n```\n\n\n### update_block(cls, raw_data_list, transaction)\nUpdate block of data\n```markdown\n- params:\n - raw_data_list:\n - List of data\n - Type: list of dict \n - Require\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n```\n\neg:\n```python\n data_list = [{\n 'id': '271fc766-6de7-44c7-bd1c-b04954cd401f',\n 'email': 'mjsanish+100@gmail.com',\n 'name': 'sanish100'\n }, {\n 'id': '20b2e97f-4c77-460b-9324-bb7530d6b8f7',\n 'role_id': '2'\n }]\n\n users = User.update_block(data_list)\n```\n\n\n## Save Record (ADD / UPDATE)\nModel function provide ability to save model object.\n\n### save(model_obj, transaction)\nAdd/Update model data to database\n```markdown\n- params:\n - model_obj:\n - Model object\n - Type: Model\n - Require \n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return:\n - Saved or updated model\n - Type: Model\n```\n\neg:\n```python\nuser = User()\nuser.name = 'some one'\nuser.email = 'someone@gmail.com'\nuser.organization_id = '4707145032222247178'\nuser.role_id = '1'\n\nuser = User.save(user)\n```\n\n### save_all(model_obj_list, transaction)\nAdd / Update list of model to database\n```markdown\n- params:\n - model_obj_list:\n - list of model objects\n - Type: list\n - Require\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return: \n - list of model\n```\n\neg:\n```python\nuser = User.find_by_pk('d3fefb2a-ef30-4c39-a560-81b459f5024e')\nuser.name = 'some one'\nuser.email = 'someone@gmail.com'\nuser.organization_id = '4707145032222247178'\nuser.role_id = '1'\n\nusers = []\nusers.append(user)\nuser = User.save_all(users)\n```\n\n### update_by_pk(pk, data, transaction)\nUpdate by primary key of model to database\n\n```markdown\n- params:\n - pk:\n - primary key value\n - Type: int | str (base on primary key type)\n - Require\n - data:\n - Data to update\n - Type: dict\n - Require\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return: \n - model\n```\n\n## Delete Records\nModel delete function allow delete records from database\n\n### delete_one(criteria, transaction)\nDelete single record that match with criteria\n\n```markdown\n- params:\n - criteria:\n - Filter criteria\n - Type: Criteria\n - Default Value: None\n - Optional\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return: True on success else throw exception\n```\n\n### delete_by_pk(pk, transaction)\nDelete record by primary key\n```markdown\n- params:\n - pk:\n - Primary key value\n - Type: int | str (base on primary key type)\n - Require\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return: True on success else throw exception\n```\n\n### delete_all(criteria, transaction)\nDelete all records that match with criteria\n```markdown\n- params:\n - criteria:\n - Filter criteria\n - Type: Criteria\n - Default Value: None\n - Optional\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return: True on success else throw exception\n```\n\n## Running with Transaction\nSpanner-ORM provide @transactional decoration to support transaction\neg:\n```python\n@transactional\ndef with_transaction(transaction):\n \"\"\"\n :type transaction: Transaction\n :param transaction: provide automatically by @transactional \n \"\"\"\n role = Role()\n role.name = 'guest'\n role.save(role, transaction)\n\n user = User()\n user.name = 'person 10'\n user.email = 'transaction@gmail.com'\n user.role_id = role.id\n\n user = User.save(user, transaction)\n```\n\n## Model object functions\nSpanner-ORM provide some basic model instance functions\n### set_props(raw_data):\nSet model properties\n```markdown\n- params: \n - raw_data\n - Model properties values in prop-value pairs\n - Type : dict\n - Required\n```\n\neg:\n```python\nuser = User()\nuser.set_props({\n 'name' : 'Sanish',\n 'address' : 'Nepal'\n})\n\n```\n\n### equals(obj)\nCompare two model object is equals or not\n```markdown\n- params: \n obj:\n - Model object that need to compare\n - Type : Model\n - Required\n- return: \n - True if both are same Model instance with equals values else return return False\n - Type: bool\n```\n\n### is_new_record()\nCheck is new record model instance or existing record model instance\n```markdown\n- return:\n - Is new record or not\n - Type: bool\n```\n\n### get_pk_value()\n```markdown\n- return:\n - primary key value\n - Type: int | str base on primary key data type\n```\n\n### get_errors()\n```markdown\n- return:\n - Model instance validation errors\n - Type: dict\n```\neg:\n```python\n user = User()\n user.email = 'someone@gmail.com'\n user.role_id = '1'\n\n if not user.validate():\n errors = user.get_errors()\n```\n### validate()\n```markdown\n- return:\n - Check model instance data valid or not\n - Type: bool\n```\neg:\n```python\n user = User()\n user.email = 'someone@gmail.com'\n user.role_id = '1'\n\n if not user.validate():\n errors = user.get_errors()\n```\n### validate_property(prop)\n```markdown\n- return:\n - Check model instance property data is valid or not\n - Type: dict \n {'is_valid':bool, 'error_msg':str}\n```\n\n## Model class functions\nSpanner-ORM provide some basic Model class functions\n\n### get_meta_data()\n```markdown\n- return:\n - Return model mata data information\n - Type: dict\n\n```\n\n### primary_key_property()\n```markdown\n- return:\n - Primary key name\n - Type: str\n```\n### has_property(property_name)\n```markdown\n- return:\n - Check model has property by name, if exist return True else False\n - Type: bool\n```\n\n## SpannerDb\n`SpannerDb` class provide some direct methods to run native and direct db operations. \n\n### SpannerDb.execute_query(query_string, params, transaction)\nExecute query string\n```markdown\n- params:\n - query_string:\n - Sql select query string\n - Type: str\n - Required\n - params:\n - Sql params\n - Type: dict\n - transaction\n - DB transaction\n - Type: Transaction\n - Default value: None\n - Optional\n- return:\n - Result set\n - Type dict\n```\neg:\n```python\nquery_string = 'SELECT * FROM users WHERE name=@name'\nresults = SpannerDb.execute_query(query_string, {'name': 'sanish'})\n```\n\n### SpannerDb.execute_ddl_query(ddl_query_string)\nExecute DDL query string\n```markdown\n- params:\n - query_string:\n - DDL query string\n - Type: str\n - Required\n```\n\neg:\n```python\nquery_string = '''\n CREATE TABLE sample (\n id STRING(64) NOT NULL,\n address STRING(MAX),\n is_active BOOL NOT NULL,\n join_date DATE,\n modified_at TIMESTAMP,\n name STRING(100) NOT NULL,\n points INT64 NOT NULL,\n ) PRIMARY KEY (id)\n '''\nSpannerDb.execute_ddl_query(query_string)\n```\n\n### SpannerDb.insert_data(table_name, columns, data)\nInsert given table data\n```markdown\n- params:\n - table_name:\n - Db table name\n - Type: str\n - Required\n - columns:\n - list of columns in which date inserting\n - Type: list\n - Required\n - eg. ['id', 'name']\n - data:\n - List of data\n - Type: list\n - Required\n - eg. [(value11, value12), (value21, value22)]\n```\n### SpannerDb.update_data(table_name, columns, data)\nUpdate given table data\n```markdown\n- params:\n - table_name:\n - Db table name\n - Type: str\n - Required\n - columns:\n - list of columns in which date updating\n - Type: list\n - Required\n - eg. ['id', 'name']\n - data:\n - List of data\n - Type: list\n - Required\n - eg. [(value11, value12), (value21, value22)]\n```\n\n### SpannerDb.save_data(table_name, columns, data)\nSave given table data\n```markdown\n- params:\n - table_name:\n - Db table name\n - Type: str\n - Required\n - columns:\n - list of columns in which date saving\n - Type: list\n - Required\n - eg. ['id', 'name']\n - data:\n - List of data\n - Type: list\n - Required\n - eg. [(value11, value12), (value21, value22)]\n```\n\n### SpannerDb.delete_data(table_name, id_list):\nDelete given ids data row\n```markdown\n- params:\n - table_name:\n - Db table name\n - Type: str\n - Required\n - id_list:\n - id tuple list \n - Type: type\n - eg. [('1',), ('2',)]\n```\n\n## Database Migration\n`DbMigration` class responsible to run Db migration\n\n### setup Db Migration\n- create migration.py file\n- Add following code\n```python\nfrom spannerorm import DbMigration\n\n\nclass Migration(DbMigration):\n instance_id = ''\n database_id = ''\n service_account_json = ''\n\n\nif __name__ == '__main__':\n Migration.run()\n```\n- Config db connection\n```markdown\n instance_id: Spanner database instance id\n database_id: Database name\n service_account_json: service account json location full path\n```\n\n### Db Migration commands:\nAvailable Migration Commands:\n```markdown\n help : list available migration commands\n create [migration_name] : Create new migration file\n up : Run all new migrations\n down : Revert back last migration\n down : Revert back last given number of migrations\n```\neg: python migration.py create user_table \n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/leapfrogtechnology/spanner-orm", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "spannerorm", "package_url": "https://pypi.org/project/spannerorm/", "platform": "", "project_url": "https://pypi.org/project/spannerorm/", "project_urls": { "Homepage": "https://github.com/leapfrogtechnology/spanner-orm" }, "release_url": "https://pypi.org/project/spannerorm/0.1.17/", "requires_dist": [ "flask", "google-cloud-spanner" ], "requires_python": "", "summary": "ORM for cloud spanner", "version": "0.1.17" }, "last_serial": 4261050, "releases": { "0.1.11": [ { "comment_text": "", "digests": { "md5": "45a3b867f7df73f0fb360a5d114ba2b1", "sha256": "0867510681ea1989e7fcf9e1e98c76bddc650793f1a81a1d16e2b482b9781a51" }, "downloads": -1, "filename": "spannerorm-0.1.11-py2-none-any.whl", "has_sig": false, "md5_digest": "45a3b867f7df73f0fb360a5d114ba2b1", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33220, "upload_time": "2018-09-10T11:16:07", "url": "https://files.pythonhosted.org/packages/8f/28/9d340fec8b728cf30eb00a32db973bbec6bde77292e2debf6c9123ee8006/spannerorm-0.1.11-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e7e9dac693a048d9259f740b884aecb5", "sha256": "1075ca5ed44509ae735fec24d50d7eb825d9fb286b765f19d32fa7e967857dd1" }, "downloads": -1, "filename": "spannerorm-0.1.11.tar.gz", "has_sig": false, "md5_digest": "e7e9dac693a048d9259f740b884aecb5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43500, "upload_time": "2018-09-10T11:16:11", "url": "https://files.pythonhosted.org/packages/9d/e2/48d158d3f24e8218f9749909665fdd6e810d7f88cffc0633914fea0d8644/spannerorm-0.1.11.tar.gz" } ], "0.1.12": [ { "comment_text": "", "digests": { "md5": "4800d0c5281a0b59c66fc60d884d9d80", "sha256": "9822d34a52376a32e8bd97d1225f89f55e632fdb79d4c7bd02a97f8982d1e2a5" }, "downloads": -1, "filename": "spannerorm-0.1.12-py2-none-any.whl", "has_sig": false, "md5_digest": "4800d0c5281a0b59c66fc60d884d9d80", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33219, "upload_time": "2018-09-10T11:30:41", "url": "https://files.pythonhosted.org/packages/de/57/082f13791cb2490c6484fca084ccb07e9bbc6e7b4e28df57c1d092536e4e/spannerorm-0.1.12-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "21f09f13180eb94dbab320ae01f2e0f9", "sha256": "53efa93a0149fb5e10df6d411c44fc3de04b631eae75a3d2d1431779d3e3b7d6" }, "downloads": -1, "filename": "spannerorm-0.1.12.tar.gz", "has_sig": false, "md5_digest": "21f09f13180eb94dbab320ae01f2e0f9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43500, "upload_time": "2018-09-10T11:30:45", "url": "https://files.pythonhosted.org/packages/56/ce/841f57171aa3ce3c64db229ea4ff8af889260683f8e35a46aa25987be839/spannerorm-0.1.12.tar.gz" } ], "0.1.16": [ { "comment_text": "", "digests": { "md5": "c32a366fa3d570e19365e6b40ec1eb5f", "sha256": "ae747789ae29a93c804cdbe04370ddf7399644b501519af64180e6f77c641d1d" }, "downloads": -1, "filename": "spannerorm-0.1.16-py2-none-any.whl", "has_sig": false, "md5_digest": "c32a366fa3d570e19365e6b40ec1eb5f", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33235, "upload_time": "2018-09-11T12:11:37", "url": "https://files.pythonhosted.org/packages/4a/da/152253f0cf4c185edd4eea7d6df5a47f0417958156d839dc6c931ae8a2f4/spannerorm-0.1.16-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "2de747791574c49fc9cc1b671fa9c37f", "sha256": "6da23c3139e7b48120e8b0fd8dfc4030fd6e1a76e15857f27e0853ae69e590d1" }, "downloads": -1, "filename": "spannerorm-0.1.16.tar.gz", "has_sig": false, "md5_digest": "2de747791574c49fc9cc1b671fa9c37f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43514, "upload_time": "2018-09-11T12:11:38", "url": "https://files.pythonhosted.org/packages/15/20/eecbedb3084720b031bd2c4319b85e18a98d8c1a1a33559e004b90f68306/spannerorm-0.1.16.tar.gz" } ], "0.1.17": [ { "comment_text": "", "digests": { "md5": "f2a5d00453d98261fd281096a589d3cd", "sha256": "51ca27baa83dd93b444e5cde4325343f2d53c176c6366e23ba934230c61f6de5" }, "downloads": -1, "filename": "spannerorm-0.1.17-py2-none-any.whl", "has_sig": false, "md5_digest": "f2a5d00453d98261fd281096a589d3cd", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33235, "upload_time": "2018-09-11T12:18:04", "url": "https://files.pythonhosted.org/packages/ad/d4/73739f2e8be54fe15ba2d379db272bd21267859917f852a522ae3b3772af/spannerorm-0.1.17-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "13df9c1fd27cc081f193d35b9aa85779", "sha256": "b2e8eb1ef11ee9ddca8e85b81eb8554f5747ea99a8aa44c3618384a648225107" }, "downloads": -1, "filename": "spannerorm-0.1.17.tar.gz", "has_sig": false, "md5_digest": "13df9c1fd27cc081f193d35b9aa85779", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43517, "upload_time": "2018-09-11T12:18:08", "url": "https://files.pythonhosted.org/packages/c9/1c/77bd81aa144388a6ae24ca109f2d84c383e2e4b13860461784d51f7cf6ad/spannerorm-0.1.17.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "8c82cfc23bb4e45431475f9a52aacb4e", "sha256": "a942ae007c7a9546666c2291cce6c4994c7c67e668b701c52885587dd647e4f8" }, "downloads": -1, "filename": "spannerorm-0.1.3-py2-none-any.whl", "has_sig": false, "md5_digest": "8c82cfc23bb4e45431475f9a52aacb4e", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 32758, "upload_time": "2018-07-31T14:55:05", "url": "https://files.pythonhosted.org/packages/54/8f/ea081877a56750b1599be7a92c84ee75888ea560fbd2556de3eeeff219b8/spannerorm-0.1.3-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a0645c69d1e9a82de8e538ab4cc35454", "sha256": "b65eaa54e4f1c5ecac668481783ea8b7d8dd641a5c33c895c13d5ec4dd8615ca" }, "downloads": -1, "filename": "spannerorm-0.1.3.tar.gz", "has_sig": false, "md5_digest": "a0645c69d1e9a82de8e538ab4cc35454", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 42657, "upload_time": "2018-07-31T14:55:07", "url": "https://files.pythonhosted.org/packages/16/8b/3de6b7d5b8c31c7d18ec4aa965a8c4345bff03db90151e64201cd9314e95/spannerorm-0.1.3.tar.gz" } ], "0.1.4": [ { "comment_text": "", "digests": { "md5": "8324741ba39c35cdd9c851b8e80dd237", "sha256": "8c94276104f6f1096a2205ca4066db56220e58d49c17f8522ef65983906ba23e" }, "downloads": -1, "filename": "spannerorm-0.1.4-py2-none-any.whl", "has_sig": false, "md5_digest": "8324741ba39c35cdd9c851b8e80dd237", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 32872, "upload_time": "2018-08-01T04:53:17", "url": "https://files.pythonhosted.org/packages/89/dd/9f872fa8845290c1a1420958ef74add6956608f5c8005ba3c1b6b9887129/spannerorm-0.1.4-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "57289673efe7290f79563e09a1c16bc1", "sha256": "ff0b4eb6f9c8d0077ad82e91f9b3af1849bbbfd4ffddac1db07c1ff021b94040" }, "downloads": -1, "filename": "spannerorm-0.1.4.tar.gz", "has_sig": false, "md5_digest": "57289673efe7290f79563e09a1c16bc1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43030, "upload_time": "2018-08-01T04:53:22", "url": "https://files.pythonhosted.org/packages/3d/b3/1000be4acb49e3a3ca07e0880357318683bf22ca6f6f5abdc1ace50166cd/spannerorm-0.1.4.tar.gz" } ], "0.1.5": [ { "comment_text": "", "digests": { "md5": "0078a44a81f4d242f5e6dcd64fdab842", "sha256": "1e3581a3f0b8cf217f2a04b7e2bb8fd4f61c0eb8332cbd555b41df4a3761c77f" }, "downloads": -1, "filename": "spannerorm-0.1.5-py2-none-any.whl", "has_sig": false, "md5_digest": "0078a44a81f4d242f5e6dcd64fdab842", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33187, "upload_time": "2018-08-05T11:46:53", "url": "https://files.pythonhosted.org/packages/07/b0/dff2fd23b90fb21ff7b08c2b36e12d6004643d587dd9b99ca8d028353ad1/spannerorm-0.1.5-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1c4cbdec210ae077cd97faf7ffd64e39", "sha256": "a3533b6bfa04d0bc426fc629dbd37981435f1352e70e29b6e4f6d2302e67825f" }, "downloads": -1, "filename": "spannerorm-0.1.5.tar.gz", "has_sig": false, "md5_digest": "1c4cbdec210ae077cd97faf7ffd64e39", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43440, "upload_time": "2018-08-05T11:46:57", "url": "https://files.pythonhosted.org/packages/45/c0/8834bcde1764759fb2b02a50eccb8e989f64aefe3d1a200b6823ec7da69f/spannerorm-0.1.5.tar.gz" } ], "0.1.6": [ { "comment_text": "", "digests": { "md5": "09a05bd6cf8a5ffbed24efd45f6e0835", "sha256": "9042d34e7cba7a857caded69138775a508f474ec293349ec25e246c6b6de2fda" }, "downloads": -1, "filename": "spannerorm-0.1.6-py2-none-any.whl", "has_sig": false, "md5_digest": "09a05bd6cf8a5ffbed24efd45f6e0835", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33179, "upload_time": "2018-08-06T05:49:41", "url": "https://files.pythonhosted.org/packages/26/62/126b79541f135cdb04447816c4c1317b7d212c7d6db2445a19c3319a52e9/spannerorm-0.1.6-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "9ad57f0c83a55cf2c94fc1cd7a693827", "sha256": "999927f97b9c54da927a81c7bc04554a44ba9f8ec6272fdf6c6fd9055720e39b" }, "downloads": -1, "filename": "spannerorm-0.1.6.tar.gz", "has_sig": false, "md5_digest": "9ad57f0c83a55cf2c94fc1cd7a693827", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43451, "upload_time": "2018-08-06T05:49:45", "url": "https://files.pythonhosted.org/packages/40/92/00c9320178ac51c5abd19029c7ff5b780057bbc4a6cc1cb9dcc6de5ee6ae/spannerorm-0.1.6.tar.gz" } ], "0.1.7": [ { "comment_text": "", "digests": { "md5": "8014b68cb2574abebfd554c317e55ebb", "sha256": "9d25ae2f6da2d67eaadba73c553065a0b4d71abe939df6b866a3c4da178b27ba" }, "downloads": -1, "filename": "spannerorm-0.1.7-py2-none-any.whl", "has_sig": false, "md5_digest": "8014b68cb2574abebfd554c317e55ebb", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33168, "upload_time": "2018-08-06T08:29:57", "url": "https://files.pythonhosted.org/packages/19/5a/9535b3cb3b7edd2be4a573a82cf5c408ab8177b0cb446cafcc4bb8c42ec7/spannerorm-0.1.7-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "55aee6910c1759832895ea09e2c272eb", "sha256": "de34f2920118a9d51414d84d92dcdf8241cb14b2019ca02463eca9f8a7d8782b" }, "downloads": -1, "filename": "spannerorm-0.1.7.tar.gz", "has_sig": false, "md5_digest": "55aee6910c1759832895ea09e2c272eb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43444, "upload_time": "2018-08-06T08:30:04", "url": "https://files.pythonhosted.org/packages/69/c2/4b61eae8d705b162f43272a78db34bca0db5e62d716cd9a983fe4c4b4e45/spannerorm-0.1.7.tar.gz" } ], "0.1.8": [ { "comment_text": "", "digests": { "md5": "73ee02f8c3a1289bad6d83c4214bf3f0", "sha256": "948e021a9fa5028d2e9f207a06fb3b7c0da5d033b4a0f6d9bc78fef45494e683" }, "downloads": -1, "filename": "spannerorm-0.1.8-py2-none-any.whl", "has_sig": false, "md5_digest": "73ee02f8c3a1289bad6d83c4214bf3f0", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33198, "upload_time": "2018-09-10T10:41:48", "url": "https://files.pythonhosted.org/packages/51/28/ab58b3e5d688dac9224b5714b69872336b2332a0c2537cde6484e85a4db9/spannerorm-0.1.8-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "facddcadca0f9f242ad5103c2e4b609e", "sha256": "7d12912e3c8dbc99b1502e0fe55dbd72a01b60aad08736c1ea1fb3aeada95952" }, "downloads": -1, "filename": "spannerorm-0.1.8.tar.gz", "has_sig": false, "md5_digest": "facddcadca0f9f242ad5103c2e4b609e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43468, "upload_time": "2018-09-10T10:42:04", "url": "https://files.pythonhosted.org/packages/de/64/3ed4123f35d2d63dc0995d1b0b168929fd908abd78cf50760fcb33abfff5/spannerorm-0.1.8.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "f2a5d00453d98261fd281096a589d3cd", "sha256": "51ca27baa83dd93b444e5cde4325343f2d53c176c6366e23ba934230c61f6de5" }, "downloads": -1, "filename": "spannerorm-0.1.17-py2-none-any.whl", "has_sig": false, "md5_digest": "f2a5d00453d98261fd281096a589d3cd", "packagetype": "bdist_wheel", "python_version": "py2", "requires_python": null, "size": 33235, "upload_time": "2018-09-11T12:18:04", "url": "https://files.pythonhosted.org/packages/ad/d4/73739f2e8be54fe15ba2d379db272bd21267859917f852a522ae3b3772af/spannerorm-0.1.17-py2-none-any.whl" }, { "comment_text": "", "digests": { "md5": "13df9c1fd27cc081f193d35b9aa85779", "sha256": "b2e8eb1ef11ee9ddca8e85b81eb8554f5747ea99a8aa44c3618384a648225107" }, "downloads": -1, "filename": "spannerorm-0.1.17.tar.gz", "has_sig": false, "md5_digest": "13df9c1fd27cc081f193d35b9aa85779", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43517, "upload_time": "2018-09-11T12:18:08", "url": "https://files.pythonhosted.org/packages/c9/1c/77bd81aa144388a6ae24ca109f2d84c383e2e4b13860461784d51f7cf6ad/spannerorm-0.1.17.tar.gz" } ] }