def __new__(cls, name, bases, attrs): if name in cls.models: raise ModelError("'{}' model is already defined in '{}'".format( name, cls.models[name].__module__)) fields = { k: v for k, v in attrs.items() if not k.startswith('_') and isinstance(v, Field) } pk = None for base in bases: if hasattr(base, 'primary_key'): pk = getattr(base, 'primary_key') or pk pk = attrs.get('primary_key') or pk # Find primary key among fields if `primary_key` property has not explicitly set anywhere in model hierarchy if not pk: pks = list(k for k, v in attrs.items() if isinstance(v, Field) and v.primary_key) if fields and len(pks) == 0: raise ModelError("No primary key in model '{}'".format(name)) elif len(pks) > 1: raise ModelError( "More than one field is primary key in model '{}'".format( name)) elif pks: attrs['primary_key'] = pks[0] attrs['_meta'] = weakref.proxy(cls) c = super(ModelMeta, cls).__new__(cls, name, bases, attrs) if name != 'BaseModel': cls.models[name] = c # Pass model class to each foreign field for k, v in fields.items(): if isinstance(v, ForeignRel): attrs[k].init(c) for base in bases: bforeigns = list( k for k, v in base.__dict__.items() if not k.startswith('_') and isinstance(v, ForeignRel)) for k in bforeigns: getattr(base, k).init(c) return c
def save(self): """ Save the model :return: self """ if self._deleted: raise ModelError('Cannot save already deleted record') # Raises ValidationError if validation failed if self._validate_on_write: self.validate() data = {k: self._data.get(v) for k, v in self._request_fields.items()} if self[self.primary_key] is None: res = self._impl_object.create(self.__class__, data) else: res = self._impl_object.update(self.__class__, data) # Update model with returned data if any # Reduce result dict according with fields since it may contain extra keys # Don't use `update` method since the data has a mapped value for fields with enums, while those method sets # value as enum value if res is not None: res = { self._request_fields[k]: v for k, v in res.items() if k in self._request_fields } self._data = res return self
def _related(self): """Related model class""" if self._my_model_cls is None: raise ModelError("Model not initialized") if self._to_model_cached is None: mdl_cls = self._my_model_cls._meta.models.get( self._to) if isinstance(self._to, str) else self._to if mdl_cls is not None: self._to_model_cached = mdl_cls else: raise ModelError( "Model '{}' related from '{}' is not found".format( self._to, self._my_model_cls.__name__)) return self._to_model_cached
def _get_to_field(self, to_model_cls): if self._my_model_cls is None: raise ModelError("Model not initialized") our_name = inflection.underscore(self._my_model_cls.__name__) our_pk = 'id' return our_name + '_' + our_pk
def delete(self): """ Delete record. After deletion the model becomes read-only. :return: self """ if self._deleted: raise ModelError('Record is already deleted') pk = self.primary_key pk_val = self._data[self._fields[pk]] if pk_val is None: raise ModelError('Primary key is not set') self._impl_object.delete(self.__class__, pk, pk_val) self._deleted = True return self
def to_field(self): if self._to_field is None: f = self._get_to_field(self._related) else: f = self._to_field if not hasattr(self._related, f): raise ModelError("'{}' field does not exist in model '{}'".format( f, self._related.__name__)) return f
def _get_impl(self, layer_class): """ Try to get implementation class from given layer. If not found try to find it in default one. If not found again raise exception :param layer_class: layer class :raises ModelError: implementation class not found :return: implementation class """ cls = self.__class__ iobj = layer_class.get_impl(cls) if iobj is None and self._search_impl_in_default_layer: iobj = self._default_layer.get_impl(cls) if iobj is None: raise ModelError( "Cannot find implementation for '{}' in layers [{}, {}]". format(cls.__name__, layer_class.__name__, self._default_layer.__name__)) return iobj