keepluggable.storage_metadata.sql module

Component that stores file metadata in a relational database.

class keepluggable.storage_metadata.sql.BaseFile[source]

Bases: bag.sqlalchemy.tricks.ID, bag.sqlalchemy.tricks.MinimalBase

Base mixin class for a model that represents file metadata.

The file MAY be an image.

property aspect_ratio

self.image_width / self.image_height.

created = Column(None, DateTime(), table=None, nullable=False, default=ColumnDefault(<function datetime.utcnow>))
file_name = Column(None, Unicode(length=300), table=None)
get_original(sas)[source]

Return the file this instance is derived from.

image_height = Column(None, Integer(), table=None)
image_width = Column(None, Integer(), table=None)
property is_the_original

self.original_id is None.

length = Column(None, Integer(), table=None, nullable=False)
md5 = Column(None, Unicode(length=32), table=None, nullable=False)
mime_type = Column(None, Unicode(length=255), table=None)
q_versions(sas=None, order_by='image_width')[source]

Query that returns files derived from this instance.

version = Column(None, Unicode(length=20), table=None, default=ColumnDefault('original'))
versions: List[BaseFile]
class keepluggable.storage_metadata.sql.SQLAlchemyMetadataStorage(orchestrator: keepluggable.orchestrator.Orchestrator)[source]

Bases: Generic[keepluggable.storage_metadata.sql.TFile]

File metadata storage backend base class using SQLAlchemy.

This class certainly needs to be subclassed for your specific use case. You must examine the source code and override methods as necessary. The most important reason for this is that each application will need to namespace the stored files differently.

Creating your File model class

Your File model class must inherit from the BaseFile mixin class that we provide. Here is an example in which files are separated by user:

from bag import fk_rel
from keepluggable.storage_metadata.sql import (
    BaseFile, SQLAlchemyMetadataStorage)
from sqlalchemy import Column, UniqueConstraint
from sqlalchemy.types import Unicode

from myapp.db import Base
from myapp.modules.user.models import User


class File(Base, BaseFile):
    __table_args__ = (
        UniqueConstraint('user_id', 'md5', name='file_user_md5_key'),
        {})

    # You can add any columns for information entered by the user:
    description = Column(Unicode(320), nullable=False, default='')
    # title = Column(Unicode(80), nullable=False)
    # alt

    # Relationships
    user_id, user = fk_rel(User, backref='files')

    @property  # Your File model must define a "namespace" property.
    def namespace(self):  # In this example a user has her own files.
        return str(self.user_id)

# Create a self-referential foreign key and relationship so that
# a file can point to its original version:
File.original_id, File.versions = fk_rel(File, nullable=True)
# When original_id is null, this is the original file.
delete(namespace: str, key: str, sas=None) None[source]

Delete one file.

delete_with_versions(namespace: str, key: str, sas=None) None[source]

Delete a file along with all its versions.

gen_all(namespace: str, filters=None, sas=None) Generator[Dict[str, Any], None, None][source]

Generate all the files (originals and derivations).

Versions must be organized later – this is a flat listing.

gen_keys(namespace: str, filters=None, sas=None) Generator[str, None, None][source]

Generate the keys in a namespace.

gen_originals(namespace: str, filters=None, sas=None) Generator[Dict[str, Any], None, None][source]

Generate original files (not derived versions).

get(namespace: str, key: str, sas=None) Dict[str, Any][source]

Return a dict: the metadata of one file, or None if not found.

get_entity(namespace: str, key: str, sas=None) Optional[keepluggable.storage_metadata.sql.TFile][source]

Return a model instance representing file metadata, or None.

put(namespace: str, metadata: Dict[str, Any], sas=None) Dict[str, Any][source]

Create or update a file corresponding to the given metadata.

This method returns a 2-tuple containing the ID of the entity and a boolean saying whether the entity is new or existing.

Instead of overriding this method, it is probably better for you to override the methods it calls.

update(namespace: str, id, metadata: Dict[str, Any], sas=None) Dict[str, Any][source]

Update a file metadata. It must exist in the database.

class keepluggable.storage_metadata.sql.StorageConfigSchema(*args, **kw)[source]

Bases: colander.Schema

Validated configuration for SQLAlchemyMetadataStorage.

  • metadata_model_cls must point to a certain model class to store the file metadata.

  • sql_session should point to a scoped session global variable. But instead of using this setting, you may override the SQLAlchemyMetadataStorage._get_session() method.

keepluggable.storage_metadata.sql.file_to_dict(obj, flavor='', **kw)[source]

Convert instance to a dictionary, usually for JSON output.