La valeur composite est un modèle dans PofEAA qui mappe un objet de valeur simple, qui est un attribut d'une entité, sous la forme de plusieurs colonnes dans la table sur laquelle l'entité est mappée.
Il existe également un modèle similaire appelé Serialized LOB, qui sérialise un objet en JSON, etc. et le place dans un BLOB ou un CLOB. La valeur composite a l'avantage de pouvoir être utilisée à partir de SQL par rapport au LOB sérialisé.
Un exemple de création de valeur composite avec SQLAlchemy peut être trouvé dans la documentation d'origine. Composite Column Types
Cependant, dans cet exemple, aucun opérateur n'est défini, donc seule la comparaison de valeurs égales est possible.
Vous pouvez également personnaliser la partie de génération SQL en utilisant comparator_factory
, mais comme l'échantillon de cette partie a été séparé, j'ai créé un échantillon à assembler.
Cet exemple est plus simple car il utilise un tuple nommé pour créer un objet de valeur.
composite_example.py
# -*- coding: utf-8 -*-
from __future__ import division, print_function, absolute_import
from collections import namedtuple
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.properties import CompositeProperty
from sqlalchemy.orm import composite, sessionmaker, configure_mapper
#Copier à partir du didacticiel ORM
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(engine)
#Type de plage qui est l'objet de valeur cette fois. Il y a début et fin.
class Range(namedtuple('Range', 'start end')):
#Cette méthode renvoie la valeur mappée dans le taple,
#Puisque namedtuple est un tapple, il suffit de renvoyer self
def __composite_values__(self):
return self
#Ceci est juste une méthode. Utiliser via un membre d'instance
def includes(self, value):
return self.start <= value < self.end
#C'est la personne qui génère du SQL
class RangeComparator(CompositeProperty.Comparator):
#Méthode pour générer du SQL.Associons la méthode et l'utilisation de Value Object
def includes(self, value):
#Extrayez la colonne mappée. Cette partie est une copie.
start, end = self.__clause_element__().clauses
# and_()Générer du SQL en utilisant.
return and_(start <= value, value < end)
#Fonction d'assistance
def range_composite(start, end):
return composite(Range, start, end, comparator_factory=RangeComparator)
class MyTable(Base):
__tablename__ = 'mytable'
id = Column(Integer, primary_key=True)
foo_start = Column(Integer)
foo_end = Column(Integer)
foo_range = range_composite(foo_start, foo_end)
def __repr__(self):
return "MyTable(foo_start={0.foo_start!r}, foo_end={0.foo_end!r}".format(self)
#Non requis pour cet échantillon.Mais pour les projets complexes, vous devez le faire
#Comme il y a des cas où la génération SQL échoue parce que le mappage qui s'étend sur plusieurs classes n'est pas terminé,
#Faisons-le après avoir défini tous les modèles.
# configure_mappers()
print("Create tables")
Base.metadata.create_all(engine)
session = Session()
print("Insert test data")
session.add(MyTable(foo_start=10, foo_end=100))
session.add(MyTable(foo_start=100, foo_end=200))
session.add(MyTable(foo_start=1, foo_end=10))
session.commit()
print("Select using filter")
# RangeComparator.includes()Peut être utilisé pour construire la partie filtre
values = session.query(MyTable).filter(MyTable.foo_range.includes(42)).all()
print("values:", values)
#Bien sûr, Range.includes()Peut être utilisé normalement dans une instance
v = values[0]
print("test")
print(9, v.foo_range.includes(9))
print(10, v.foo_range.includes(10))
print(99, v.foo_range.includes(99))
print(100, v.foo_range.includes(100))
La sortie ressemble à ceci.
Create tables
2014-01-15 22:59:15,334 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("mytable")
2014-01-15 22:59:15,334 INFO sqlalchemy.engine.base.Engine ()
2014-01-15 22:59:15,335 INFO sqlalchemy.engine.base.Engine
CREATE TABLE mytable (
id INTEGER NOT NULL,
foo_start INTEGER,
foo_end INTEGER,
PRIMARY KEY (id)
)
2014-01-15 22:59:15,335 INFO sqlalchemy.engine.base.Engine ()
2014-01-15 22:59:15,335 INFO sqlalchemy.engine.base.Engine COMMIT
Insert test data
2014-01-15 22:59:15,336 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2014-01-15 22:59:15,337 INFO sqlalchemy.engine.base.Engine INSERT INTO mytable (foo_start, foo_end) VALUES (?, ?)
2014-01-15 22:59:15,337 INFO sqlalchemy.engine.base.Engine (10, 100)
2014-01-15 22:59:15,337 INFO sqlalchemy.engine.base.Engine INSERT INTO mytable (foo_start, foo_end) VALUES (?, ?)
2014-01-15 22:59:15,337 INFO sqlalchemy.engine.base.Engine (100, 200)
2014-01-15 22:59:15,338 INFO sqlalchemy.engine.base.Engine INSERT INTO mytable (foo_start, foo_end) VALUES (?, ?)
2014-01-15 22:59:15,338 INFO sqlalchemy.engine.base.Engine (1, 10)
2014-01-15 22:59:15,338 INFO sqlalchemy.engine.base.Engine COMMIT
Select using filter
2014-01-15 22:59:15,339 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2014-01-15 22:59:15,340 INFO sqlalchemy.engine.base.Engine SELECT mytable.id AS mytable_id, mytable.foo_start AS mytable_foo_start, mytable.foo_end AS mytable_foo_end
FROM mytable
WHERE mytable.foo_start <= ? AND mytable.foo_end > ?
2014-01-15 22:59:15,340 INFO sqlalchemy.engine.base.Engine (42, 42)
values: [MyTable(foo_start=10, foo_end=100]
test
9 False
10 True
99 True
100 False
Recommended Posts