Esempio n. 1
0
    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)
Esempio n. 2
0
    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
Esempio n. 3
0
 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)