def _discover_polymorphic_submodels(cls): if not cls._is_polymorphic_base: raise ModelException( '_discover_polymorphic_submodels can only be called on polymorphic base classes' ) def _discover(klass): if not klass._is_polymorphic_base and klass.__polymorphic_key__ is not None: cls._polymorphic_map[klass.__polymorphic_key__] = klass for subklass in klass.__subclasses__(): _discover(subklass) _discover(cls)
def __new__(cls, name, bases, attrs): """ """ #move column definitions into columns dict #and set default column names column_dict = OrderedDict() primary_keys = OrderedDict() pk_name = None #get inherited properties inherited_columns = OrderedDict() for base in bases: for k, v in getattr(base, '_defined_columns', {}).items(): inherited_columns.setdefault(k, v) #short circuit __abstract__ inheritance is_abstract = attrs['__abstract__'] = attrs.get('__abstract__', False) #short circuit __polymorphic_key__ inheritance attrs['__polymorphic_key__'] = attrs.get('__polymorphic_key__', None) def _transform_column(col_name, col_obj): column_dict[col_name] = col_obj if col_obj.primary_key: primary_keys[col_name] = col_obj col_obj.set_column_name(col_name) #set properties attrs[col_name] = ColumnDescriptor(col_obj) column_definitions = [(k, v) for k, v in attrs.items() if isinstance(v, columns.Column)] #column_definitions = sorted(column_definitions, lambda x,y: cmp(x[1].position, y[1].position)) column_definitions = sorted(column_definitions, key=lambda x: x[1].position) is_polymorphic_base = any( [c[1].polymorphic_key for c in column_definitions]) column_definitions = [x for x in inherited_columns.items() ] + column_definitions polymorphic_columns = [ c for c in column_definitions if c[1].polymorphic_key ] is_polymorphic = len(polymorphic_columns) > 0 if len(polymorphic_columns) > 1: raise ModelDefinitionException( 'only one polymorphic_key can be defined in a model, {} found'. format(len(polymorphic_columns))) polymorphic_column_name, polymorphic_column = polymorphic_columns[ 0] if polymorphic_columns else (None, None) if isinstance(polymorphic_column, (columns.BaseContainerColumn, columns.Counter)): raise ModelDefinitionException( 'counter and container columns cannot be used for polymorphic keys' ) # find polymorphic base class polymorphic_base = None if is_polymorphic and not is_polymorphic_base: def _get_polymorphic_base(bases): for base in bases: if getattr(base, '_is_polymorphic_base', False): return base klass = _get_polymorphic_base(base.__bases__) if klass: return klass polymorphic_base = _get_polymorphic_base(bases) defined_columns = OrderedDict(column_definitions) # check for primary key if not is_abstract and not any( [v.primary_key for k, v in column_definitions]): raise ModelDefinitionException( "At least 1 primary key is required.") counter_columns = [ c for c in defined_columns.values() if isinstance(c, columns.Counter) ] data_columns = [ c for c in defined_columns.values() if not c.primary_key and not isinstance(c, columns.Counter) ] if counter_columns and data_columns: raise ModelDefinitionException( 'counter models may not have data columns') has_partition_keys = any(v.partition_key for (k, v) in column_definitions) #transform column definitions for k, v in column_definitions: # don't allow a column with the same name as a built-in attribute or method if k in BaseModel.__dict__: raise ModelDefinitionException( "column '{}' conflicts with built-in attribute/method". format(k)) # counter column primary keys are not allowed if (v.primary_key or v.partition_key) and isinstance( v, (columns.Counter, columns.BaseContainerColumn)): raise ModelDefinitionException( 'counter columns and container columns cannot be used as primary keys' ) # this will mark the first primary key column as a partition # key, if one hasn't been set already if not has_partition_keys and v.primary_key: v.partition_key = True has_partition_keys = True _transform_column(k, v) partition_keys = OrderedDict(k for k in primary_keys.items() if k[1].partition_key) clustering_keys = OrderedDict(k for k in primary_keys.items() if not k[1].partition_key) #setup partition key shortcut if len(partition_keys) == 0: if not is_abstract: raise ModelException( "at least one partition key must be defined") if len(partition_keys) == 1: pk_name = [x for x in partition_keys.keys()][0] attrs['pk'] = attrs[pk_name] else: # composite partition key case, get/set a tuple of values _get = lambda self: tuple(self._values[c].getval() for c in partition_keys.keys()) _set = lambda self, val: tuple(self._values[c].setval(v) for (c, v) in zip( partition_keys.keys(), val)) attrs['pk'] = property(_get, _set) # some validation col_names = set() for v in column_dict.values(): # check for duplicate column names if v.db_field_name in col_names: raise ModelException( "{} defines the column {} more than once".format( name, v.db_field_name)) if v.clustering_order and not (v.primary_key and not v.partition_key): raise ModelException( "clustering_order may be specified only for clustering primary keys" ) if v.clustering_order and v.clustering_order.lower() not in ( 'asc', 'desc'): raise ModelException( "invalid clustering order {} for column {}".format( repr(v.clustering_order), v.db_field_name)) col_names.add(v.db_field_name) #create db_name -> model name map for loading db_map = {} for field_name, col in column_dict.items(): db_map[col.db_field_name] = field_name #add management members to the class attrs['_columns'] = column_dict attrs['_primary_keys'] = primary_keys attrs['_defined_columns'] = defined_columns # maps the database field to the models key attrs['_db_map'] = db_map attrs['_pk_name'] = pk_name attrs['_dynamic_columns'] = {} attrs['_partition_keys'] = partition_keys attrs['_clustering_keys'] = clustering_keys attrs['_has_counter'] = len(counter_columns) > 0 # add polymorphic management attributes attrs['_is_polymorphic_base'] = is_polymorphic_base attrs['_is_polymorphic'] = is_polymorphic attrs['_polymorphic_base'] = polymorphic_base attrs['_polymorphic_column'] = polymorphic_column attrs['_polymorphic_column_name'] = polymorphic_column_name attrs['_polymorphic_map'] = {} if is_polymorphic_base else None #setup class exceptions DoesNotExistBase = None for base in bases: DoesNotExistBase = getattr(base, 'DoesNotExist', None) if DoesNotExistBase is not None: break DoesNotExistBase = DoesNotExistBase or attrs.pop( 'DoesNotExist', BaseModel.DoesNotExist) attrs['DoesNotExist'] = type('DoesNotExist', (DoesNotExistBase, ), {}) MultipleObjectsReturnedBase = None for base in bases: MultipleObjectsReturnedBase = getattr(base, 'MultipleObjectsReturned', None) if MultipleObjectsReturnedBase is not None: break MultipleObjectsReturnedBase = DoesNotExistBase or attrs.pop( 'MultipleObjectsReturned', BaseModel.MultipleObjectsReturned) attrs['MultipleObjectsReturned'] = type( 'MultipleObjectsReturned', (MultipleObjectsReturnedBase, ), {}) #create the class and add a QuerySet to it klass = super(ModelMetaClass, cls).__new__(cls, name, bases, attrs) return klass
def _get_model_by_polymorphic_key(cls, key): if not cls._is_polymorphic_base: raise ModelException( '_get_model_by_polymorphic_key can only be called on polymorphic base classes' ) return cls._polymorphic_map.get(key)