Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions spanner_orm/admin/column.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ class ColumnSchema(schema.InformationSchema):
is_nullable = field.Field(field.String)
spanner_type = field.Field(field.String)

@property
def nullable(self) -> bool:
return self.is_nullable == 'YES'

@property
def field_type(self) -> Type[field.FieldType]:
for field_type in field.ALL_TYPES:
if self.spanner_type == field_type.ddl():
Expand Down
2 changes: 1 addition & 1 deletion spanner_orm/admin/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def tables(cls) -> Dict[str, Dict[str, Any]]:
condition.equal_to('table_schema', ''))
for column_row in columns:
new_field = field.Field(
column_row.field_type(), nullable=column_row.nullable())
column_row.field_type, nullable=column_row.nullable)
new_field.name = column_row.column_name
new_field.position = column_row.ordinal_position
column_data[column_row.table_name][column_row.column_name] = new_field
Expand Down
12 changes: 6 additions & 6 deletions spanner_orm/admin/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ def __init__(self, model_: Type[model.Model]):

def ddl(self) -> str:
fields = [
'{} {}'.format(name, field.ddl())
for name, field in self._model.fields.items()
'{} {}'.format(field.name, field.ddl())
for field in self._model.fields.values()
]
index_ddl = 'PRIMARY KEY ({})'.format(', '.join(self._model.primary_keys))
statement = 'CREATE TABLE {} ({}) {}'.format(self._model.table,
Expand Down Expand Up @@ -154,9 +154,9 @@ def validate(self) -> None:
model_ = metadata.SpannerMetadata.model(self._table)
if not model_:
raise error.SpannerError('Table {} does not exist'.format(self._table))
if not self._field.nullable():
if not self._field.nullable:
raise error.SpannerError('Column {} is not nullable'.format(self._column))
if self._field.primary_key():
if self._field.primary_key:
raise error.SpannerError('Column {} is a primary key'.format(
self._column))

Expand Down Expand Up @@ -218,10 +218,10 @@ def validate(self) -> None:

old_field = model_.fields[self._column]
# Validate that the only alteration is to change column nullability
if self._field.field_type() != old_field.field_type():
if self._field.field_type != old_field.field_type:
raise error.SpannerError('Column {} is changing type'.format(
self._column))
if self._field.nullable() == old_field.nullable():
if self._field.nullable == old_field.nullable:
raise error.SpannerError('Column {} has no changes'.format(self._column))


Expand Down
4 changes: 2 additions & 2 deletions spanner_orm/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ def _validate(self, model_class: Type[Any]) -> None:
self.destination_column, self.destination_model_class.table))
dest = self.destination_model_class.fields[self.destination_column]

if (origin.field_type() != dest.field_type() or
origin.nullable() != dest.nullable()):
if (origin.field_type != dest.field_type or
origin.nullable != dest.nullable):
raise error.ValidationError('Types of {} and {} do not match'.format(
origin.name, dest.name))

Expand Down
19 changes: 16 additions & 3 deletions spanner_orm/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import abc
import datetime
from typing import Any, Type
from typing import Any, Type, Optional

from spanner_orm import error

Expand Down Expand Up @@ -48,8 +48,9 @@ class Field(object):
def __init__(self,
field_type: Type[FieldType],
nullable: bool = False,
primary_key: bool = False):
self.name = None
primary_key: bool = False,
name: Optional[str] = None):
self._name = name
self._type = field_type
self._nullable = nullable
self._primary_key = primary_key
Expand All @@ -59,18 +60,30 @@ def ddl(self) -> str:
return self._type.ddl()
return '{field_type} NOT NULL'.format(field_type=self._type.ddl())

@property
def field_type(self) -> Type[FieldType]:
return self._type

def grpc_type(self) -> str:
return self._type.grpc_type()

@property
def nullable(self) -> bool:
return self._nullable

@property
def primary_key(self) -> bool:
return self._primary_key

@property
def name(self) -> Optional[str]:
return self._name

@name.setter
def name(self, value: str) -> None:
if not self._name:
self._name = value

def validate(self, value) -> None:
if value is None:
if not self._nullable:
Expand Down
44 changes: 37 additions & 7 deletions spanner_orm/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,45 @@ def __init__(self,
parent: Optional[str] = None,
null_filtered: bool = False,
unique: bool = False,
storing_columns: Optional[List[str]] = None):
storing_columns: Optional[List[str]] = None,
name: Optional[str] = None):
if not columns:
raise error.ValidationError('An index must have at least one column')
self.columns = columns
self.name = None
self.parent = parent
self.null_filtered = null_filtered
self.unique = unique
self.storing_columns = storing_columns or []
self._columns = columns
self._name = name
self._parent = parent
self._null_filtered = null_filtered
self._unique = unique
self._storing_columns = storing_columns or []

@property
def columns(self) -> List[str]:
return self._columns

@property
def name(self) -> Optional[str]:
return self._name

@name.setter
def name(self, value: str) -> None:
if not self._name:
self._name = value

@property
def parent(self) -> Optional[str]:
return self._parent

@property
def null_filtered(self) -> bool:
return self._null_filtered

@property
def unique(self) -> bool:
return self._unique

@property
def storing_columns(self) -> List[str]:
return self._storing_columns

@property
def primary(self) -> bool:
Expand Down
6 changes: 5 additions & 1 deletion spanner_orm/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def finalize(self) -> None:
sorted_fields = list(sorted(self.fields.values(), key=lambda f: f.position))

if index.Index.PRIMARY_INDEX not in self.indexes:
primary_keys = [f.name for f in sorted_fields if f.primary_key()]
primary_keys = [f.name for f in sorted_fields if f.primary_key]
primary_index = index.Index(primary_keys)
primary_index.name = index.Index.PRIMARY_INDEX
self.indexes[index.Index.PRIMARY_INDEX] = primary_index
Expand All @@ -94,6 +94,8 @@ def add_metadata(self, metadata: 'ModelMetadata') -> None:
def add_field(self, name: str, new_field: field.Field) -> None:
new_field.name = name
new_field.position = len(self.fields)
if new_field.name in self.fields:
raise error.SpannerError('Already contains a field named "{}"'.format(new_field.name))
self.fields[name] = new_field

def add_relation(self, name: str,
Expand All @@ -103,4 +105,6 @@ def add_relation(self, name: str,

def add_index(self, name: str, new_index: index.Index) -> None:
new_index.name = name
if new_index.name in self.indexes:
raise error.SpannerError('Already contains an index named "{}"'.format(new_index.name))
self.indexes[name] = new_index
2 changes: 1 addition & 1 deletion spanner_orm/relationship.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# limitations under the License.
"""Helps define a foreign key relationship between two models."""

from typing import Any, Dict, List, Type, Union
from typing import Any, Dict, List, Type, Union, Optional

import dataclasses
from spanner_orm import error
Expand Down
36 changes: 23 additions & 13 deletions spanner_orm/tests/admin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ def make_test_columns(self, model):
'table_name': model.table,
'column_name': row.name,
'ordinal_position': iteration,
'is_nullable': 'YES' if row.nullable() else 'NO',
'spanner_type': row.field_type().ddl()
'is_nullable': 'YES' if row.nullable else 'NO',
'spanner_type': row.field_type.ddl()
})
iteration += 1
return [column.ColumnSchema(row) for row in columns]
Expand Down Expand Up @@ -109,10 +109,10 @@ def test_metadata(self, tables, columns, index_columns, indexes):
self.assertEqual(meta.table, model.table)
self.assertEqual(meta.columns, model.columns)
for row in model.columns:
self.assertEqual(meta.fields[row].field_type(),
model.fields[row].field_type())
self.assertEqual(meta.fields[row].nullable(),
model.fields[row].nullable())
self.assertEqual(meta.fields[row].field_type,
model.fields[row].field_type)
self.assertEqual(meta.fields[row].nullable,
model.fields[row].nullable)
self.assertEqual(meta.primary_keys, model.primary_keys)
self.assertEqual(
getattr(meta, index.Index.PRIMARY_INDEX).columns, model.primary_keys)
Expand Down Expand Up @@ -163,13 +163,23 @@ def test_secondary_index(self, tables, columns, index_columns, indexes):
self.assertEqual(getattr(meta, name).columns, index_cols)

def test_model_creation_ddl(self):
expected_ddl = [
'CREATE TABLE IndexTestModel (key STRING(MAX) NOT NULL,'
' value STRING(MAX) NOT NULL) PRIMARY KEY (key)',
'CREATE INDEX value_index ON IndexTestModel (value)'
]
ddl = update.model_creation_ddl(models.IndexTestModel)
self.assertEqual(ddl, expected_ddl)
expected_ddl = [
'CREATE TABLE IndexTestModel (key STRING(MAX) NOT NULL,'
' value STRING(MAX) NOT NULL) PRIMARY KEY (key)',
'CREATE INDEX value ON IndexTestModel (value)'
]
ddl = update.model_creation_ddl(models.IndexTestModel)
self.assertEqual(ddl, expected_ddl)
self.assertCountEqual(models.IndexTestModel.meta.indexes.keys(), ['PRIMARY_KEY', 'value_idx'])

def test_model_creation_ddl2(self):
expected_ddl = [
'CREATE TABLE FieldCustomNameTestModel (key2 STRING(MAX) NOT NULL)'
' PRIMARY KEY (key2)'
]
ddl = update.model_creation_ddl(models.FieldCustomNameTestModel)
self.assertEqual(ddl, expected_ddl)
self.assertCountEqual(models.FieldCustomNameTestModel.meta.indexes.keys(), ['PRIMARY_KEY'])


if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions spanner_orm/tests/model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ def test_object_changes(self):

def test_field_exists_on_model_class(self):
self.assertIsInstance(models.SmallTestModel.key, field.Field)
self.assertEqual(models.SmallTestModel.key.field_type(), field.String)
self.assertFalse(models.SmallTestModel.key.nullable())
self.assertEqual(models.SmallTestModel.key.field_type, field.String)
self.assertFalse(models.SmallTestModel.key.nullable)
self.assertEqual(models.SmallTestModel.key.name, 'key')

def test_field_inheritance(self):
Expand Down
8 changes: 7 additions & 1 deletion spanner_orm/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ class IndexTestModel(model.Model):
key = field.Field(field.String, primary_key=True)
value = field.Field(field.String)

value_index = index.Index(['value'])
value_idx = index.Index(['value'], name='value')


class FieldCustomNameTestModel(model.Model):
__table__ = 'FieldCustomNameTestModel'

key = field.Field(field.String, primary_key=True, name='key2')


class RelationshipTestModel(model.Model):
Expand Down