Пример #1
0
    def resolve_model(cls, name):
        if inspect.isclass(name) and issubclass(name, cls.BaseClass):
            return name

        subclasses = {
            ModelClass.__name__: ModelClass
            for ModelClass in cls.all_models()
        }
        permutations = [name, camel(name), camel(name, upper_segments=0)]
        for name in permutations:
            if name in subclasses:
                return subclasses[name]

            if name.lower().endswith('s'):
                singular = name.rstrip('sS')
                if singular in subclasses:
                    return subclasses[singular]

            if name.lower().endswith('ies'):
                singular = name[:-3] + 'y'
                if singular in subclasses:
                    return subclasses[singular]

        for name in permutations:
            if name in cls.BaseClass.metadata.tables:
                return cls.BaseClass.metadata.tables[name]

        raise ValueError('Unrecognized model: {}'.format(name))
Пример #2
0
def JSONColumnMixin(column_name, fields, admin_only=False):
    """
    Creates a new mixin class with a JSON column named column_name.

    The newly created mixin class will have a SQLAlchemy JSON Column, named
    `column_name`, along with two other attributes column_name_fields and
    column_name_qualified_fields which describe the fields that the JSON
    Column is expected to hold.

    For example::

        >>> SocialMediaMixin = JSONColumnMixin('social_media', ['Twitter', 'LinkedIn'])
        >>> SocialMediaMixin.social_media # doctest: +ELLIPSIS
        Column('social_media', JSON(), ... server_default=DefaultClause('{}', for_update=False))
        >>> SocialMediaMixin._social_media_fields
        OrderedDict([('twitter', 'Twitter'), ('linked_in', 'LinkedIn')])
        >>> SocialMediaMixin._social_media_qualified_fields
        OrderedDict([('social_media__twitter', 'twitter'), ('social_media__linked_in', 'linked_in')])

    Instances of the newly created mixin class have convenience accessors for
    the attributes defined by `fields`, both directly and using their fully
    qualified forms::

        >>> sm = SocialMediaMixin()
        >>> sm.twitter = 'https://twitter.com/MAGFest'  # Get and set "twitter" directly
        >>> sm.twitter
        'https://twitter.com/MAGFest'
        >>> sm.social_media__twitter  # Get and set qualified "social_media__twitter"
        'https://twitter.com/MAGFest'
        >>> sm.social_media__twitter = '@MAGFest'
        >>> sm.social_media__twitter
        '@MAGFest'
        >>> sm.social_media  # Actual column updated appropriately
        {'linked_in': '', 'twitter': '@MAGFest'}


    Args:
        column_name (str): The name of the column.
        fields (list): A list of field names you expect the column to hold.
            This can be:
              - A single string, if you're only expecting the column to hold a
                single field.
              - A list of strings, which will be treated as the column labels,
                and converted from CamelCase to under_score for the fields.
              - A map of {string: string}, which will be treated as a mapping
                of field names to field labels.

    Returns:
        type: A new mixin class with a JSON column named column_name.

    """

    fields_name = '_{}_fields'.format(column_name)
    qualified_fields_name = '_{}_qualified_fields'.format(column_name)
    if isinstance(fields, Mapping):
        fields = OrderedDict([(fieldify(k), v) for k, v in fields.items()])
    else:
        fields = OrderedDict([(fieldify(s), s) for s in listify(fields)])

    qualified_fields = OrderedDict([(column_name + '__' + s, s)
                                    for s in fields.keys()])
    column = Column(column_name, JSON, default={}, server_default='{}')
    attrs = {
        column_name: column,
        fields_name: fields,
        qualified_fields_name: qualified_fields
    }

    _Mixin = type(camel(column_name) + 'Mixin', (object, ), attrs)

    def _Mixin__init__(self, *args, **kwargs):
        setattr(self, column_name, {})
        for attr in getattr(self.__class__, fields_name).keys():
            setattr(self, attr, kwargs.pop(attr, ''))
        super(_Mixin, self).__init__(*args, **kwargs)

    _Mixin.__init__ = _Mixin__init__

    def _Mixin__declare_last__(cls):
        setattr(getattr(cls, column_name), 'admin_only', admin_only)
        column = cls.__table__.columns.get(column_name)
        setattr(column, 'admin_only', admin_only)

    _Mixin.__declare_last__ = classmethod(_Mixin__declare_last__)

    def _Mixin__unqualify(cls, name):
        if name in getattr(cls, qualified_fields_name):
            return getattr(cls, qualified_fields_name)[name]
        else:
            return name

    _Mixin.unqualify = classmethod(_Mixin__unqualify)

    def _Mixin__getattr__(self, name):
        name = self.unqualify(name)
        if name in getattr(self.__class__, fields_name):
            return getattr(self, column_name).get(name, '')
        else:
            return super(_Mixin, self).__getattr__(name)

    _Mixin.__getattr__ = _Mixin__getattr__

    def _Mixin__setattr__(self, name, value):
        name = self.unqualify(name)
        if name in getattr(self.__class__, fields_name):
            fields = getattr(self, column_name)
            if fields.get(name) != value:
                fields[name] = value
                super(_Mixin, self).__setattr__(column_name, dict(fields))
        else:
            super(_Mixin, self).__setattr__(name, value)

    _Mixin.__setattr__ = _Mixin__setattr__

    return _Mixin
Пример #3
0
def JSONColumnMixin(column_name, fields, admin_only=False):
    """
    Creates a new mixin class with a JSON column named column_name.

    The newly created mixin class will have a SQLAlchemy JSON Column, named
    `column_name`, along with two other attributes column_name_fields and
    column_name_qualified_fields which describe the fields that the JSON
    Column is expected to hold.

    For example::

        >>> SocialMediaMixin = JSONColumnMixin('social_media', ['Twitter', 'LinkedIn'])
        >>> SocialMediaMixin.social_media # doctest: +ELLIPSIS
        Column('social_media', JSON(), ... server_default=DefaultClause('{}', for_update=False))
        >>> SocialMediaMixin._social_media_fields
        OrderedDict([('twitter', 'Twitter'), ('linked_in', 'LinkedIn')])
        >>> SocialMediaMixin._social_media_qualified_fields
        OrderedDict([('social_media__twitter', 'twitter'), ('social_media__linked_in', 'linked_in')])

    Instances of the newly created mixin class have convenience accessors for
    the attributes defined by `fields`, both directly and using their fully
    qualified forms::

        >>> sm = SocialMediaMixin()
        >>> sm.twitter = 'https://twitter.com/MAGFest'  # Get and set "twitter" directly
        >>> sm.twitter
        'https://twitter.com/MAGFest'
        >>> sm.social_media__twitter  # Get and set qualified "social_media__twitter"
        'https://twitter.com/MAGFest'
        >>> sm.social_media__twitter = '@MAGFest'
        >>> sm.social_media__twitter
        '@MAGFest'
        >>> sm.social_media  # Actual column updated appropriately
        {'linked_in': '', 'twitter': '@MAGFest'}


    Args:
        column_name (str): The name of the column.
        fields (list): A list of field names you expect the column to hold.
            This can be:
              - A single string, if you're only expecting the column to hold a
                single field.
              - A list of strings, which will be treated as the column labels,
                and converted from CamelCase to under_score for the fields.
              - A map of {string: string}, which will be treated as a mapping
                of field names to field labels.

    Returns:
        type: A new mixin class with a JSON column named column_name.

    """

    fields_name = '_{}_fields'.format(column_name)
    qualified_fields_name = '_{}_qualified_fields'.format(column_name)
    if isinstance(fields, Mapping):
        fields = OrderedDict([(fieldify(k), v) for k, v in fields.items()])
    else:
        fields = OrderedDict([(fieldify(s), s) for s in listify(fields)])

    qualified_fields = OrderedDict([(column_name + '__' + s, s) for s in fields.keys()])
    column = Column(column_name, JSON, default={}, server_default='{}')
    attrs = {
        column_name: column,
        fields_name: fields,
        qualified_fields_name: qualified_fields
    }

    _Mixin = type(camel(column_name) + 'Mixin', (object,), attrs)

    def _Mixin__init__(self, *args, **kwargs):
        setattr(self, column_name, {})
        for attr in getattr(self.__class__, fields_name).keys():
            setattr(self, attr, kwargs.pop(attr, ''))
        super(_Mixin, self).__init__(*args, **kwargs)
    _Mixin.__init__ = _Mixin__init__

    def _Mixin__declare_last__(cls):
        setattr(getattr(cls, column_name), 'admin_only', admin_only)
        column = cls.__table__.columns.get(column_name)
        setattr(column, 'admin_only', admin_only)
    _Mixin.__declare_last__ = classmethod(_Mixin__declare_last__)

    def _Mixin__unqualify(cls, name):
        if name in getattr(cls, qualified_fields_name):
            return getattr(cls, qualified_fields_name)[name]
        else:
            return name
    _Mixin.unqualify = classmethod(_Mixin__unqualify)

    def _Mixin__getattr__(self, name):
        name = self.unqualify(name)
        if name in getattr(self.__class__, fields_name):
            return getattr(self, column_name).get(name, '')
        else:
            return super(_Mixin, self).__getattr__(name)
    _Mixin.__getattr__ = _Mixin__getattr__

    def _Mixin__setattr__(self, name, value):
        name = self.unqualify(name)
        if name in getattr(self.__class__, fields_name):
            fields = getattr(self, column_name)
            if fields.get(name) != value:
                fields[name] = value
                super(_Mixin, self).__setattr__(column_name, dict(fields))
        else:
            super(_Mixin, self).__setattr__(name, value)
    _Mixin.__setattr__ = _Mixin__setattr__

    return _Mixin