SQL Alchemy:嵌套复合列类型和自定义类型
SQL Alchemy: Nested Composite Column Types and Custom Types
我对 SQLAlchemy 比较陌生,并且有一个关于复合和自定义类型的一些方法的想法和建议的问题。
挑战:我大量使用值对象,一些值对象具有由值对象组成的属性。即
class Customer:
name = CustomerName(first_name, last_name)
country = Country(country_name, region)
其中 Customer 是一个实体。 CustomerName 是一个原始类型的值对象(first_name, last_name),Country 是由值对象(CountryName, Region) 组成的值对象。
我希望值对象全部保留在单个客户中 table。对于像 CustomerName 这样的东西,我可以很容易地使用复合类型:
class Customer(Base)
first_name = Column(String)
last_name = Column(String)
customer_name = composite(CustomerName, first_name, last_name)
简单。
我发现你不能嵌套复合类型(这在 Postgres 中使用 sqlalchemy-utils 是可能的,但我不适合)。
我对我正在尝试的一些方法的反馈很感兴趣,如果有人有任何其他想法或更好的方法...
选项 1:
当需要嵌套值对象时,创建自定义类型。在许多情况下,一旦我到达嵌套对象,它们就会包装一个基元。
class CountryNameType(TypeDecorator):
impl = String
def process_bind_param(self, country_name_object, dialect):
if country_name_object is not None:
value = country_name_object.value
return value
def process_result_value(self, value, dialect):
if value is not None:
country_name_object = CountryName(value)
return country_object
我什至在考虑创建通用类型。其中单一类型可用于任何具有字符串、数字等的 VO...:[=21=]
SingleStringValueObjectType
SingleNumericValueObjectType
他们将有一个将 class 作为参数的 init 方法:
__init__(self, class_of_value_object)
super...
然后传入所表示的Type的class。然后可以在两个过程方法中使用它来动态检查它是否是正确的 class 类型并根据传入的 class 创建新对象。您甚至可以将泛型类型(String、Numeric)作为 arg 传递,并使用 load_dialect_impl() 为任何单个原始值对象创建单个泛型类型...
然后
class Customer(Base)
first_name = Column(String)
last_name = Column(String)
country_name = Column(CountryNameType)
region = Column(RegionType)
或
country_name = Column(SingleStringValueObjectType(CountryName))
region = Column(SingleStringValueObjectType(Region))
和
customer_name = composite(CustomerName, first_name, last_name)
country = composite(Country, country_name, region)
选项 2:
在class中使用@hybrid_property映射值对象。
即
@hybrid_property
def country(self):
country_name = CountryName(self.db_country_name)
region = Region(self.db_country_region)
return Country(country_name, region)
@country.setter
def country(self, country):
self.db_country_name = country.name.value
self.db_country_region = country.region.value
self._country = country
当 db_country_name 和 db_country_region 定义为 table 中的列时。
class 客户(基础)
db_country_name = Column(String)
db_country_region = Column(String)
...
但只能通过混合 属性.
设置和检索
自定义类型感觉更干净。但如果有人对任何其他(更好的)解决方案有任何经验,我会感兴趣吗?
Michael Bayer 在 Bitbucket 上的一个线程上与我分享了一些非常有用的代码。
他直接将自定义比较器与复合器一起使用,以说明如何实现 2 级复合器/比较器。
您可以在此处查看讨论和代码:
https://bitbucket.org/zzzeek/sqlalchemy/issues/4168/nested-composite-column-types
对于简单的值对象,我创建了一个似乎也能正常工作的自定义类型:
class UnaryValueObjectType(TypeDecorator):
impl = Numeric
def __init__(self, class_of_value_object, type):
self.class_of_value_object = class_of_value_object
self.type = type
super(UnaryValueObjectType, self).__init__()
def load_dialect_impl(self, dialect):
return dialect.type_descriptor(self.type)
def process_bind_param(self, value_object, dialect):
if isinstance(value_object, self.class_of_value_object) and value_object is not None:
value = value_object.value
return value
def process_result_value(self, value, dialect):
if value is not None:
value_object = self.class_of_value_object(value)
return value_object
然后
Column("country_name", UnaryValueObjectType(CountryName, String))
希望这两种方法都能对某人有用。它们都符合我的用例(一个简短、快速的解决方案,另一个更强大但更长的解决方案)。
我对 SQLAlchemy 比较陌生,并且有一个关于复合和自定义类型的一些方法的想法和建议的问题。
挑战:我大量使用值对象,一些值对象具有由值对象组成的属性。即
class Customer:
name = CustomerName(first_name, last_name)
country = Country(country_name, region)
其中 Customer 是一个实体。 CustomerName 是一个原始类型的值对象(first_name, last_name),Country 是由值对象(CountryName, Region) 组成的值对象。
我希望值对象全部保留在单个客户中 table。对于像 CustomerName 这样的东西,我可以很容易地使用复合类型:
class Customer(Base)
first_name = Column(String)
last_name = Column(String)
customer_name = composite(CustomerName, first_name, last_name)
简单。
我发现你不能嵌套复合类型(这在 Postgres 中使用 sqlalchemy-utils 是可能的,但我不适合)。
我对我正在尝试的一些方法的反馈很感兴趣,如果有人有任何其他想法或更好的方法...
选项 1: 当需要嵌套值对象时,创建自定义类型。在许多情况下,一旦我到达嵌套对象,它们就会包装一个基元。
class CountryNameType(TypeDecorator):
impl = String
def process_bind_param(self, country_name_object, dialect):
if country_name_object is not None:
value = country_name_object.value
return value
def process_result_value(self, value, dialect):
if value is not None:
country_name_object = CountryName(value)
return country_object
我什至在考虑创建通用类型。其中单一类型可用于任何具有字符串、数字等的 VO...:[=21=]
SingleStringValueObjectType
SingleNumericValueObjectType
他们将有一个将 class 作为参数的 init 方法:
__init__(self, class_of_value_object)
super...
然后传入所表示的Type的class。然后可以在两个过程方法中使用它来动态检查它是否是正确的 class 类型并根据传入的 class 创建新对象。您甚至可以将泛型类型(String、Numeric)作为 arg 传递,并使用 load_dialect_impl() 为任何单个原始值对象创建单个泛型类型...
然后
class Customer(Base)
first_name = Column(String)
last_name = Column(String)
country_name = Column(CountryNameType)
region = Column(RegionType)
或
country_name = Column(SingleStringValueObjectType(CountryName))
region = Column(SingleStringValueObjectType(Region))
和
customer_name = composite(CustomerName, first_name, last_name)
country = composite(Country, country_name, region)
选项 2:
在class中使用@hybrid_property映射值对象。
即
@hybrid_property
def country(self):
country_name = CountryName(self.db_country_name)
region = Region(self.db_country_region)
return Country(country_name, region)
@country.setter
def country(self, country):
self.db_country_name = country.name.value
self.db_country_region = country.region.value
self._country = country
当 db_country_name 和 db_country_region 定义为 table 中的列时。
class 客户(基础)
db_country_name = Column(String)
db_country_region = Column(String)
...
但只能通过混合 属性.
设置和检索自定义类型感觉更干净。但如果有人对任何其他(更好的)解决方案有任何经验,我会感兴趣吗?
Michael Bayer 在 Bitbucket 上的一个线程上与我分享了一些非常有用的代码。
他直接将自定义比较器与复合器一起使用,以说明如何实现 2 级复合器/比较器。
您可以在此处查看讨论和代码:
https://bitbucket.org/zzzeek/sqlalchemy/issues/4168/nested-composite-column-types
对于简单的值对象,我创建了一个似乎也能正常工作的自定义类型:
class UnaryValueObjectType(TypeDecorator):
impl = Numeric
def __init__(self, class_of_value_object, type):
self.class_of_value_object = class_of_value_object
self.type = type
super(UnaryValueObjectType, self).__init__()
def load_dialect_impl(self, dialect):
return dialect.type_descriptor(self.type)
def process_bind_param(self, value_object, dialect):
if isinstance(value_object, self.class_of_value_object) and value_object is not None:
value = value_object.value
return value
def process_result_value(self, value, dialect):
if value is not None:
value_object = self.class_of_value_object(value)
return value_object
然后
Column("country_name", UnaryValueObjectType(CountryName, String))
希望这两种方法都能对某人有用。它们都符合我的用例(一个简短、快速的解决方案,另一个更强大但更长的解决方案)。