From 44ec1250fa88ed0c002165a6d39e255d461cb13f Mon Sep 17 00:00:00 2001 From: German Pineda Date: Mon, 20 Jul 2020 14:31:13 -0700 Subject: [PATCH 1/2] add date field type and support for array types --- spanner_orm/__init__.py | 7 ++- spanner_orm/field.py | 103 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/spanner_orm/__init__.py b/spanner_orm/__init__.py index 1a77d85..39ea783 100644 --- a/spanner_orm/__init__.py +++ b/spanner_orm/__init__.py @@ -65,8 +65,13 @@ Index = index.Index Relationship = relationship.Relationship String = field.String -StringArray = field.StringArray +Date = field.Date Timestamp = field.Timestamp +StringArray = field.StringArray +BoolArray = field.BoolArray +IntegerArray = field.IntegerArray +FloatArray = field.FloatArray +DateArray = field.DateArray equal_to = condition.equal_to force_index = condition.force_index diff --git a/spanner_orm/field.py b/spanner_orm/field.py index 17bdf06..a574572 100644 --- a/spanner_orm/field.py +++ b/spanner_orm/field.py @@ -174,6 +174,25 @@ def validate_type(value) -> None: raise error.ValidationError('{} is not of type str'.format(value)) +class Date(FieldType): + """Represents a date type.""" + + @staticmethod + def ddl() -> str: + return 'DATE' + + @staticmethod + def grpc_type() -> type_pb2.Type: + return type_pb2.Type(code=type_pb2.DATE) + + @staticmethod + def validate_type(value) -> None: + try: + datetime.datetime.strptime(value, "%Y-%m-%d") + except: + raise error.ValidationError('{} is not of type date (YYYY-[M]M-[D]D)'.format(value)) + + class StringArray(FieldType): """Represents an array of strings type.""" @@ -194,6 +213,88 @@ def validate_type(value: Any) -> None: raise error.ValidationError('{} is not of type str'.format(item)) +class BoolArray(FieldType): + """Represents an array of booleans type.""" + + @staticmethod + def ddl() -> str: + return 'ARRAY' + + @staticmethod + def grpc_type() -> type_pb2.Type: + return type_pb2.Type(code=type_pb2.ARRAY) + + @staticmethod + def validate_type(value: Any) -> None: + if not isinstance(value, list): + raise error.ValidationError('{} is not of type list'.format(value)) + for item in value: + if not isinstance(item, bool): + raise error.ValidationError('{} is not of type bool'.format(item)) + + +class IntegerArray(FieldType): + """Represents an array of integers type.""" + + @staticmethod + def ddl() -> str: + return 'ARRAY' + + @staticmethod + def grpc_type() -> type_pb2.Type: + return type_pb2.Type(code=type_pb2.ARRAY) + + @staticmethod + def validate_type(value: Any) -> None: + if not isinstance(value, list): + raise error.ValidationError('{} is not of type list'.format(value)) + for item in value: + if not isinstance(item, int): + raise error.ValidationError('{} is not of type int'.format(item)) + + +class FloatArray(FieldType): + """Represents an array of floats type.""" + + @staticmethod + def ddl() -> str: + return 'ARRAY' + + @staticmethod + def grpc_type() -> type_pb2.Type: + return type_pb2.Type(code=type_pb2.ARRAY) + + @staticmethod + def validate_type(value: Any) -> None: + if not isinstance(value, list): + raise error.ValidationError('{} is not of type list'.format(value)) + for item in value: + if not isinstance(item, float): + raise error.ValidationError('{} is not of type float'.format(item)) + + +class DateArray(FieldType): + """Represents an array of dates type.""" + + @staticmethod + def ddl() -> str: + return 'ARRAY' + + @staticmethod + def grpc_type() -> type_pb2.Type: + return type_pb2.Type(code=type_pb2.ARRAY) + + @staticmethod + def validate_type(value: Any) -> None: + if not isinstance(value, list): + raise error.ValidationError('{} is not of type list'.format(value)) + for item in value: + try: + datetime.datetime.strptime(item, "%Y-%m-%d") + except: + raise error.ValidationError('{} is not of type date (YYYY-[M]M-[D]D)'.format(item)) + + class Timestamp(FieldType): """Represents a timestamp type.""" @@ -211,4 +312,4 @@ def validate_type(value: Any) -> None: raise error.ValidationError('{} is not of type datetime'.format(value)) -ALL_TYPES = [Boolean, Integer, Float, String, StringArray, Timestamp] +ALL_TYPES = [Boolean, Integer, Float, String, Date, Timestamp, StringArray, BoolArray, IntegerArray, FloatArray, DateArray] From 7fdac79c16517721440f32b28c1de1c0388b0f51 Mon Sep 17 00:00:00 2001 From: German Pineda Date: Mon, 20 Jul 2020 14:53:56 -0700 Subject: [PATCH 2/2] add new types to tests --- spanner_orm/tests/models.py | 5 +++++ spanner_orm/tests/update_test.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/spanner_orm/tests/models.py b/spanner_orm/tests/models.py index 4dfb37f..92cd298 100644 --- a/spanner_orm/tests/models.py +++ b/spanner_orm/tests/models.py @@ -86,6 +86,11 @@ class UnittestModel(model.Model): string_2 = field.Field(field.String, nullable=True) timestamp = field.Field(field.Timestamp) timestamp_2 = field.Field(field.Timestamp, nullable=True, allow_commit_timestamp=True) + date = field.Field(field.Date, nullable=True) + bool_array = field.Field(field.BoolArray, nullable=True) + int_array = field.Field(field.IntegerArray, nullable=True) + float_array = field.Field(field.FloatArray, nullable=True) + date_array = field.Field(field.DateArray, nullable=True) string_array = field.Field(field.StringArray, nullable=True) test_index = index.Index(['string_2']) diff --git a/spanner_orm/tests/update_test.py b/spanner_orm/tests/update_test.py index 0ed5810..627b4b9 100644 --- a/spanner_orm/tests/update_test.py +++ b/spanner_orm/tests/update_test.py @@ -71,6 +71,11 @@ def test_create_table(self, get_model): ' string STRING(MAX) NOT NULL, string_2 STRING(MAX),' ' timestamp TIMESTAMP NOT NULL,' ' timestamp_2 TIMESTAMP OPTIONS (allow_commit_timestamp=true),' + ' date DATE,' + ' bool_array ARRAY,' + ' int_array ARRAY,' + ' float_array ARRAY,' + ' date_array ARRAY,' ' string_array ARRAY) PRIMARY KEY (int_, float_, string)') self.assertEqual(test_update.ddl(), test_model_ddl)