Source code for abilian.core.models.attachment

# coding=utf-8
"""
"""
from __future__ import absolute_import, print_function, division

import abc

import sqlalchemy as sa
from sqlalchemy import Column, UnicodeText, Integer, ForeignKey
from sqlalchemy.orm import relationship, backref

from abilian.core.entities import Entity

from .blob import Blob

#: name of backref on target :class:`Entity` object
ATTRIBUTE = '__attachments__'


[docs]class SupportAttachment(object): """ """ __metaclass__ = abc.ABCMeta
[docs]def register(cls): """ Register an :class:`~.Entity` as a attachmentable class. Can be used as a class decorator: .. code-block:: python @attachment.register class MyContent(Entity): .... """ if not issubclass(cls, Entity): raise ValueError('Class must be a subclass of abilian.core.entities.Entity') SupportAttachment.register(cls) return cls
[docs]def is_support_attachments(obj): """ :param obj: a class or instance """ if isinstance(obj, type): return issubclass(obj, SupportAttachment) if not isinstance(obj, SupportAttachment): return False if obj.id is None: return False return True
[docs]def for_entity(obj, check_support_attachments=False): """ Return attachments on an entity. """ if check_support_attachments and not is_support_attachments(obj): return [] return getattr(obj, ATTRIBUTE)
[docs]class Attachment(Entity): """ An Attachment owned by an :class:`Entity`. """ __auditable_entity__ = ('entity', 'attachment', ('id', 'name')) @sa.ext.declarative.declared_attr def __mapper_args__(cls): # we cannot use super(Attachment, cls): declared_attr happens during class # construction. super(cls, cls) could work; as long as `cls` is not a # subclass of `Attachment`: it would enter into an infinite loop. # # Entity.__mapper_args__ calls the descriptor with 'Entity', not `cls`. args = Entity.__dict__['__mapper_args__'].fget(cls) args['order_by'] = cls.created_at return args entity_id = Column(Integer, ForeignKey(Entity.id), nullable=False) #: owning entity entity = relationship( Entity, lazy='immediate', foreign_keys=[entity_id], backref=backref(ATTRIBUTE, lazy='select', order_by='Attachment.created_at', cascade="all, delete-orphan", ) ) blob_id = Column(Integer, sa.ForeignKey(Blob.id), nullable=False) #: file. Stored in a :class:`Blob` blob = relationship(Blob, cascade='all, delete', foreign_keys=[blob_id]) description = Column(UnicodeText(), nullable=False, default=u'', server_default=u'',) def __repr__(self): class_ = self.__class__ mod_ = class_.__module__ classname = class_.__name__ return '<{}.{} instance at 0x{:x} entity id={!r}'\ .format(mod_, classname, id(self), self.entity_id)
@sa.event.listens_for(Attachment, 'before_insert', propagate=True) @sa.event.listens_for(Attachment, 'before_update', propagate=True)
[docs]def set_attachment_name(mapper, connection, target): if target.name: return blob = target.blob if not blob: return filename = blob.meta.get('filename') if filename is not None: target.name = filename