2 # -------------------------------------------------------------------------
3 # Copyright (c) 2015-2017 AT&T Intellectual Property
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # -------------------------------------------------------------------------
20 """Music ORM - Model"""
22 from abc import ABCMeta
23 from abc import abstractmethod
26 from oslo_config import cfg
27 from oslo_log import log as logging
30 from conductor.common.classes import abstractclassmethod
31 from conductor.common.classes import classproperty
32 from conductor.common.music import api
33 from conductor.common.music.model import search
35 LOG = logging.getLogger(__name__)
40 @six.add_metaclass(ABCMeta)
42 """A custom declarative base ORM-style class.
44 Provides some Elixir-inspired shortcuts as well.
47 # These must be set in the derived class!
52 def query(cls): # pylint: disable=E0213
53 """Return a query object a la sqlalchemy"""
54 return search.Query(cls)
58 """Return common keyword args"""
60 'keyspace': cls.__keyspace__,
61 'table': cls.__tablename__,
66 def table_create(cls):
68 kwargs = cls.__kwargs()
69 kwargs['schema'] = cls.schema()
70 api.MUSIC_API.table_create(**kwargs)
74 """Use atomic operations"""
84 """Primary key name"""
89 """Primary key value"""
99 kwargs = self.__kwargs()
100 kwargs['pk_name'] = self.pk_name()
101 kwargs['values'] = self.values()
102 kwargs['atomic'] = self.atomic()
103 pk_name = kwargs['pk_name']
104 if pk_name not in kwargs['values']:
105 # TODO(jdandrea): Make uuid4() generation a default method in Base.
106 the_id = str(uuid.uuid4())
107 kwargs['values'][pk_name] = the_id
108 kwargs['pk_value'] = the_id
109 setattr(self, pk_name, the_id)
111 kwargs['pk_value'] = kwargs['values'][pk_name]
112 api.MUSIC_API.row_create(**kwargs)
116 kwargs = self.__kwargs()
117 kwargs['pk_name'] = self.pk_name()
118 kwargs['pk_value'] = self.pk_value()
119 kwargs['values'] = self.values()
120 kwargs['atomic'] = self.atomic()
121 # FIXME(jdandrea): Do we need this test/pop clause?
122 pk_name = kwargs['pk_name']
123 if pk_name in kwargs['values']:
124 kwargs['values'].pop(pk_name)
125 api.MUSIC_API.row_update(**kwargs)
129 kwargs = self.__kwargs()
130 kwargs['pk_name'] = self.pk_name()
131 kwargs['pk_value'] = self.pk_value()
132 kwargs['atomic'] = self.atomic()
133 api.MUSIC_API.row_delete(**kwargs)
136 def filter_by(cls, **kwargs):
138 return cls.query.filter_by(**kwargs) # pylint: disable=E1101
140 def flush(self, *args, **kwargs):
141 """Flush changes to storage"""
142 # TODO(jdandrea): Implement in music? May be a no-op
146 """Return object representation as a dictionary"""
147 return dict((k, v) for k, v in self.__dict__.items()
148 if not k.startswith('_'))
151 def create_dynamic_model(keyspace, classname, baseclass):
152 """Create a dynamic ORM class with a custom keyspace/class/table.
154 Given a keyspace, a camelcase class name, and a base class
155 derived from Base, create a dynamic model that adopts a
156 table name based on a lower-cased version of the class name,
157 then create the table in the keyspace if it doesn't already exist.
158 If the baseclass already has __tablename__ or __keyspace__ set, those
159 will take precedence. Set those to None to use keyspace/classname here.
162 # The comma after baseclass belongs there! Tuple of length 1.
164 classname, (baseclass,), {
165 '__tablename__': baseclass.__tablename__ or classname.lower(),
166 '__keyspace__': baseclass.__keyspace__ or keyspace})