From 7aa546f7280f3378288087480cf0013a876ff88e Mon Sep 17 00:00:00 2001 From: Jarriq Rolle Date: Wed, 1 Feb 2023 13:33:57 -0500 Subject: [PATCH 1/8] Get a single column's value from the first result of a query. --- src/masoniteorm/models/Model.py | 1 + src/masoniteorm/query/QueryBuilder.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/masoniteorm/models/Model.py b/src/masoniteorm/models/Model.py index 743c2483..78c8f3d4 100644 --- a/src/masoniteorm/models/Model.py +++ b/src/masoniteorm/models/Model.py @@ -265,6 +265,7 @@ class Model(TimeStampsMixin, ObservesEvents, metaclass=ModelMeta): "with_count", "latest", "oldest", + "value" ) ) diff --git a/src/masoniteorm/query/QueryBuilder.py b/src/masoniteorm/query/QueryBuilder.py index 2e98c5dc..7ae5ec88 100644 --- a/src/masoniteorm/query/QueryBuilder.py +++ b/src/masoniteorm/query/QueryBuilder.py @@ -2277,3 +2277,6 @@ def oldest(self, *fields): fields = ("created_at",) return self.order_by(column=",".join(fields), direction="ASC") + + def value(self, column: str): + return self.get().first()[column] From 7b6a269eac8afb5eaab4e6b24e8d70b3a722699d Mon Sep 17 00:00:00 2001 From: Jarriq Rolle Date: Wed, 1 Feb 2023 13:47:30 -0500 Subject: [PATCH 2/8] Get a single column's value from the first result of a query. --- src/masoniteorm/query/QueryBuilder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/masoniteorm/query/QueryBuilder.py b/src/masoniteorm/query/QueryBuilder.py index 7ae5ec88..1ac3d4ab 100644 --- a/src/masoniteorm/query/QueryBuilder.py +++ b/src/masoniteorm/query/QueryBuilder.py @@ -1749,6 +1749,9 @@ def sole(self, query=False): return result.first() + def sole_value(self, column: str, query=False): + return self.sole()[column] + def first_where(self, column, *args): """Gets the first record with the given key / value pair""" if not args: From e261a07c5007af4d6204c022efb225b5e6e97740 Mon Sep 17 00:00:00 2001 From: Benjamin Stout Date: Wed, 15 Feb 2023 11:54:09 -0600 Subject: [PATCH 3/8] Use dynamic model variables for timestamp column names --- src/masoniteorm/scopes/TimeStampsScope.py | 11 +++++---- tests/mysql/model/test_model.py | 3 ++- tests/scopes/test_default_global_scopes.py | 27 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/masoniteorm/scopes/TimeStampsScope.py b/src/masoniteorm/scopes/TimeStampsScope.py index d24ed957..5e4551cc 100644 --- a/src/masoniteorm/scopes/TimeStampsScope.py +++ b/src/masoniteorm/scopes/TimeStampsScope.py @@ -1,6 +1,5 @@ -from .BaseScope import BaseScope - from ..expressions.expressions import UpdateQueryExpression +from .BaseScope import BaseScope class TimeStampsScope(BaseScope): @@ -27,8 +26,8 @@ def set_timestamp_create(self, builder): builder._creates.update( { - "updated_at": builder._model.get_new_date().to_datetime_string(), - "created_at": builder._model.get_new_date().to_datetime_string(), + builder._model.date_updated_at: builder._model.get_new_date().to_datetime_string(), + builder._model.date_created_at: builder._model.get_new_date().to_datetime_string(), } ) @@ -38,6 +37,8 @@ def set_timestamp_update(self, builder): builder._updates += ( UpdateQueryExpression( - {"updated_at": builder._model.get_new_date().to_datetime_string()} + { + builder._model.date_updated_at: builder._model.get_new_date().to_datetime_string() + } ), ) diff --git a/tests/mysql/model/test_model.py b/tests/mysql/model/test_model.py index 4adfd010..6bc7623d 100644 --- a/tests/mysql/model/test_model.py +++ b/tests/mysql/model/test_model.py @@ -2,10 +2,11 @@ import json import os import unittest + import pendulum -from src.masoniteorm.exceptions import ModelNotFound from src.masoniteorm.collection import Collection +from src.masoniteorm.exceptions import ModelNotFound from src.masoniteorm.models import Model from tests.User import User diff --git a/tests/scopes/test_default_global_scopes.py b/tests/scopes/test_default_global_scopes.py index a12b53f0..f890be6f 100644 --- a/tests/scopes/test_default_global_scopes.py +++ b/tests/scopes/test_default_global_scopes.py @@ -28,6 +28,12 @@ class UserWithTimeStamps(Model, TimeStampsMixin): __dry__ = True +class UserWithCustomTimeStamps(Model, TimeStampsMixin): + __dry__ = True + date_updated_at = "updated_ts" + date_created_at = "created_ts" + + class UserSoft(Model, SoftDeletesMixin): __dry__ = True @@ -112,3 +118,24 @@ def test_timestamps_can_be_disabled(self): self.scope.set_timestamp_create(self.builder) self.assertNotIn("created_at", self.builder._creates) self.assertNotIn("updated_at", self.builder._creates) + + def test_uses_custom_timestamp_columns_on_create(self): + self.builder = MockBuilder(UserWithCustomTimeStamps) + self.scope.set_timestamp_create(self.builder) + created_column = UserWithCustomTimeStamps.date_created_at + updated_column = UserWithCustomTimeStamps.date_updated_at + self.assertNotIn("created_at", self.builder._creates) + self.assertNotIn("updated_at", self.builder._creates) + self.assertIn(created_column, self.builder._creates) + self.assertIn(updated_column, self.builder._creates) + self.assertIsInstance( + pendulum.parse(self.builder._creates[created_column]), pendulum.DateTime + ) + self.assertIsInstance( + pendulum.parse(self.builder._creates[updated_column]), pendulum.DateTime + ) + + def test_uses_custom_updated_column_on_update(self): + user = UserWithCustomTimeStamps.hydrate({"id": 1}) + sql = user.update({"id": 2}).to_sql() + self.assertTrue(UserWithCustomTimeStamps.date_updated_at in sql) From 013ccca7f0f54aa800e90a00ea96482044f0b1e2 Mon Sep 17 00:00:00 2001 From: Kieren Eaton Date: Sat, 11 Mar 2023 10:15:39 +0800 Subject: [PATCH 4/8] Fixed UUID PK Mixin not creating primary key --- src/masoniteorm/scopes/UUIDPrimaryKeyScope.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/masoniteorm/scopes/UUIDPrimaryKeyScope.py b/src/masoniteorm/scopes/UUIDPrimaryKeyScope.py index ef091de7..00f814ad 100644 --- a/src/masoniteorm/scopes/UUIDPrimaryKeyScope.py +++ b/src/masoniteorm/scopes/UUIDPrimaryKeyScope.py @@ -1,4 +1,5 @@ import uuid + from .BaseScope import BaseScope @@ -9,6 +10,9 @@ def on_boot(self, builder): builder.set_global_scope( "_UUID_primary_key", self.set_uuid_create, action="insert" ) + builder.set_global_scope( + "_UUID_primary_key", self.set_bulk_uuid_create, action="bulk_create" + ) def on_remove(self, builder): pass @@ -22,15 +26,21 @@ def generate_uuid(self, builder, uuid_version, bytes=False): return uuid_func(*args).bytes if bytes else str(uuid_func(*args)) + def build_uuid_pk(self, builder): + uuid_version = getattr(builder._model, "__uuid_version__", 4) + uuid_bytes = getattr(builder._model, "__uuid_bytes__", False) + return { + builder._model.__primary_key__: self.generate_uuid( + builder, uuid_version, uuid_bytes + ) + } + def set_uuid_create(self, builder): # if there is already a primary key, no need to set a new one if builder._model.__primary_key__ not in builder._creates: - uuid_version = getattr(builder._model, "__uuid_version__", 4) - uuid_bytes = getattr(builder._model, "__uuid_bytes__", False) - builder._creates.update( - { - builder._model.__primary_key__: self.generate_uuid( - builder, uuid_version, uuid_bytes - ) - } - ) + builder._creates.update(self.build_uuid_pk(builder)) + + def set_bulk_uuid_create(self, builder): + for idx, create_atts in enumerate(builder._creates): + if builder._model.__primary_key__ not in create_atts: + builder._creates[idx].update(self.build_uuid_pk(builder)) From 9fa48b90a5135d6c9403972c663ee8e65ba5715c Mon Sep 17 00:00:00 2001 From: Joe Mancuso Date: Sun, 24 Sep 2023 20:08:56 -0400 Subject: [PATCH 5/8] fixed duplicate id issue --- src/masoniteorm/models/Model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/masoniteorm/models/Model.py b/src/masoniteorm/models/Model.py index 822af6ec..bf34e066 100644 --- a/src/masoniteorm/models/Model.py +++ b/src/masoniteorm/models/Model.py @@ -563,7 +563,7 @@ def create( ).to_sql() return cls.builder.create( - dictionary, id_key=cls.__primary_key__, cast=cast, **kwargs + dictionary, cast=cast, **kwargs ) @classmethod From 3e08b0765ee8294822637b56eacb3f9e68ef6ca9 Mon Sep 17 00:00:00 2001 From: Joe Mancuso Date: Sun, 24 Sep 2023 20:15:41 -0400 Subject: [PATCH 6/8] formatted --- src/masoniteorm/models/Model.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/masoniteorm/models/Model.py b/src/masoniteorm/models/Model.py index bf34e066..deff4ff8 100644 --- a/src/masoniteorm/models/Model.py +++ b/src/masoniteorm/models/Model.py @@ -562,9 +562,7 @@ def create( dictionary, query=True, id_key=cls.__primary_key__, cast=cast, **kwargs ).to_sql() - return cls.builder.create( - dictionary, cast=cast, **kwargs - ) + return cls.builder.create(dictionary, cast=cast, **kwargs) @classmethod def cast_value(cls, attribute: str, value: Any): @@ -660,7 +658,6 @@ def serialize(self, exclude=None, include=None): remove_keys = [] for key, value in serialized_dictionary.items(): - if key in self.__hidden__: remove_keys.append(key) if hasattr(value, "serialize"): @@ -1020,7 +1017,6 @@ def set_appends(self, appends): return self def save_many(self, relation, relating_records): - if isinstance(relating_records, Model): raise ValueError( "Saving many records requires an iterable like a collection or a list of models and not a Model object. To attach a model, use the 'attach' method." @@ -1036,7 +1032,6 @@ def save_many(self, relation, relating_records): related.attach_related(self, related_record) def detach_many(self, relation, relating_records): - if isinstance(relating_records, Model): raise ValueError( "Detaching many records requires an iterable like a collection or a list of models and not a Model object. To detach a model, use the 'detach' method." From b2e6bcb6690168f18710089dcca71c7e1204ba99 Mon Sep 17 00:00:00 2001 From: Joe Mancuso Date: Sun, 24 Sep 2023 20:20:30 -0400 Subject: [PATCH 7/8] fixed id key again --- src/masoniteorm/models/Model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/masoniteorm/models/Model.py b/src/masoniteorm/models/Model.py index deff4ff8..4e397ee4 100644 --- a/src/masoniteorm/models/Model.py +++ b/src/masoniteorm/models/Model.py @@ -559,7 +559,7 @@ def create( """ if query: return cls.builder.create( - dictionary, query=True, id_key=cls.__primary_key__, cast=cast, **kwargs + dictionary, query=True, cast=cast, **kwargs ).to_sql() return cls.builder.create(dictionary, cast=cast, **kwargs) From a1121ced6aada5002dbf5bc3e2a143319fd1877b Mon Sep 17 00:00:00 2001 From: Joseph Mancuso Date: Sun, 24 Sep 2023 20:22:32 -0400 Subject: [PATCH 8/8] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4320978f..287a3b13 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ # Versions should comply with PEP440. For a discussion on single-sourcing # the version across setup.py and the project code, see # https://packaging.python.org/en/latest/single_source_version.html - version="2.19.1", + version="2.19.2", package_dir={"": "src"}, description="The Official Masonite ORM", long_description=long_description,