SQLAlchemy-ImageAttach is a SQLAlchemy extension for attaching images to entity objects. It provides the following features:
It’s easy to use with sqlalchemy.ext.declarative:
from sqlalchemy import Column, ForeignKey, Integer, Unicode, relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy_imageattach.entity import Image, image_attachment
Base = declarative_base()
class User(Base):
"""User model."""
id = Column(Integer, primary_key=True)
name = Column(Unicode, nullable=False)
picture = image_attachment('UserPicture')
__tablename__ = 'user'
class UserPicture(Base, Image):
"""User picture model."""
user_id = Column(Integer, ForeignKey('User.id'), primary_key=True)
user = relationship('User')
__tablename__ = 'user_picture'
In the above example, we declare two entity classes. UserPicture which inherits Image is an image entity, and User owns it. image_attachment() function is a specialized relationship() for image entities. You can understand it as one-to-many relationship.
Every image class has object_type string, which is used by the storage.
UserPicture in the above example omits object_type property, but it can be overridden if needed. Its default value is the table name (underscores will be replaced by hyphens).
When would you need to override object_type? The most common case is when you changed the table name. Identifiers like path names that are internally used by the stoage won’t be automatically renamed even if you change the table name in the relational database. So you need to maintain the same object_type value.
Every image instance has object_id number, which is used by the storage. A pair of (object_type, object_id is an unique key for an image.
UserPicture in the above example omits object_id property, because it provides the default value when the primary key is integer. It has to be explicitly implemented when the primary key is not integer or composite key.
For example, the most simple and easiest (although naive) way to implement object_id for the string primary key is hashing it:
@property
def object_id(self):
return int(hashlib.sha1(self.id).hexdigest(), 16)
If the primary key is a pair, encode a pair into an integer:
@property
def object_id(self):
a = self.id_a
b = self.id_b
return (a + b) * (a + b) + a
If the primary key is composite of three or more columns, encode a tuple into a linked list of pairs first, and then encode the pair into an integer. It’s just a way to encode, and there are many other ways to do the same.
There are currently only two implementations:
We recommend you to use fs on your local development box, and switch it to s3 when you deploy it to the production system.
If you need to use another storage backend, you can implement the interface by yourself: Implementing your own storage.
The most of computers have a filesystem, so using fs storage is suitable for development. It works even if you are offline.
Actually there are two kinds of filesystem storages:
It just stores the images, and simply assumes that you have a separate web server for routing static files e.g. Lighttpd, Nginx. For example, if you have a sever configuration like this:
server {
listen 80;
server_name images.yourapp.com;
root /var/local/yourapp/images;
}
FileSystemStore should be configured like this:
sqlalchemy_imageattach.stores.fs.FileSystemStore(
path='/var/local/yourapp/images',
base_url='http://images.yourapp.com/'
)
In addition to FileSystemStore‘s storing features, it does more for you: actually serving files through WSGI. It takes an optional prefix for url instead of base_url:
sqlalchemy_imageattach.stores.fs.HttpExposedFileSystemStore(
path='/var/local/yourapp/images',
prefix='static/images/'
)
The default prefix is simply images/.
It provides wsgi_middleware() method to inject its own server to your WSGI application. For example, if you are using Flask:
from yourapp import app
app.wsgi_app = store.wsgi_middleware(app.wsgi_app)
or if Pyramid:
app = config.make_wsgi_app()
app = store.wsgi_middleware(app)
or if Bottle:
app = bottle.app()
app = store.wsgi_middleware(app)
Note
The server provided by this isn’t production-ready quality, so do not use this for your production service. We recommend you to use FileSystemStore with a separate web server like Nginx or Lighttpd instead.
You can implement a new storage backend if you need. Every storage has to inherit Store and implement the following four methods:
The method puts a given image to the storage.
It takes a file that contains the image blob, four identifier values (object_type, object_id, width, height) for the image, a mimetype of the image, and a boolean value (reproducible) which determines whether it can be reproduced or not.
For example, if it’s a filesystem storage, you can make directory/file names using object_type, object_id, and size values, and suffix using mimetype. If it’s a S3 implementation, it can determine whether to use RRS (reduced redundancy storage) or standard storage using reproducible argument.
The method finds a requested image in the storage.
It takes four identifier values (object_type, object_id, width, height) for the image, and a mimetype of the image. The return type must be file-like.
It should raise IOError or its subtype when there’s no requested image in the storage.
The method is similar to get_file() except it returns a URL of the image instead of a file that contains the image blob.
It doesn’t have to raise errors when there’s no requested image in the storage. It’s okay even if the returned URL is a broken link. Because we assume that it’s called only when the requested image is sure to be there. It means you can quickly generate URLs by just calculation without any I/O.
Moreover, you can assume that these URLs are never cached, because SQLAlchemy-ImageAttach will automatically appends a query string that contains of its updated timestamp for you.
The method deletes a requested image in the storage.
It takes the same arguments to get_file() and get_url() methods.
It must doesn’t raise any exception even if there’s no requested image.
The constructor of it can be anything. It’s not part of the interface.
If you believe your storage implementation could be widely used as well as for others, please contribute your code by sending a pull request! We always welcome your contributions.
SQLAlchemy-ImageAttach provides a simple basic utility to migrate image data in an old storage to a new storage (although it’s not CLI but API). In order to migrate storage data you need used database as well, not only storage. Because some metadata are only saved to database.
The following code shows you how to migrate all image data in old_store to new_store:
plan = migrate(session, Base, old_store, new_store)
plan.execute()
In the above code, Base is declarative base class (which is created by sqlalchemy.ext.declarative.declarative_base()), and session is an instance of SQLAlchemy Session.
If you want to know progress of migration, iterating the result:
plan = migrate(session, Base, old_store, new_store)
for image in plan:
print('Migrated ' + repr(image))
Or pass a callback function to execute() method:
def progress(image):
print('Migrated ' + repr(image))
plan = migrate(session, Base, old_store, new_store)
plan.execute(progress)
You’ve declared entities and choose a storage, so then the next step is to actually attach images to objects! In order to determine what storage to save images into, you can set the current context.
A context knows what storage you are using now, and tell entities the storage to use. You can set a context using store_context() function in with block:
from sqlalchemy_imageattach.context import store_context
with store_context(store):
with open('image_to_attach.jpg') as f:
entity.picture.from_file(f)
You would face ContextError when you try attaching images without any context.
A way to attach an image to an object is loading it from a file object using from_file() method. The following example code shows how to attach a profile picture to an user:
from yourapp.config import session, store
def set_picture(request, user_id):
try:
user = session.query(User).get(int(user_id))
with store_context(store):
user.picture.from_file(request.files['picture'])
except Exception:
session.rollback()
raise
session.commit()
It takes any file-like objects as well e.g.:
from urllib2 import urlopen
def set_picture_url(request, user_id):
try:
user = session.query(User).get(int(user_id))
picture_url = request.values['picture_url']
with store_context(store):
user.picture.from_file(urlopen(picture_url))
except Exception:
session.rollback()
raise
session.commit()
Note that the responsibility to close files is yours. Because some file-like objects can be reused several times, or don’t have to be closed (or some of them even don’t have any close() method).
Of course you can load images from its byte strings. Use from_blob() method:
from requests import get
def set_picture_url(request, user_id):
try:
user = session.query(User).get(int(user_id))
picture_url = request.values['picture_url']
image_binary = get(picture_url).content
with store_context(store):
user.picture.from_blob(image_binary)
except Exception:
session.rollback()
raise
session.commit()
In web environment, the most case you need just an url of an image, not its binary content. So ImageSet object provide locate() method:
def user_profile(request, user_id):
user = session.query(User).get(int(user_id))
with store_context(store):
picture_url = user.picture.locate()
return render_template('user_profile.html',
user=user, picture_url=picture_url)
It returns the url of the original image (which is not resized). Read about Thumbnails if you want a thumbnail url.
ImageSet also implements de facto standard __html__() special method, so it can be directly rendered in the most of template engines like Jinja2, Mako. It’s expanded to <img> tag on templates:
<div class="user">
<a href="{{ url_for('user_profile', user_id=user.id) }}"
title="{{ user.name }}">{{ user.picture }}</a>
</div>
<div class="user">
<a href="${url_for('user_profile', user_id=user.id)}"
title="${user.name}">${user.picture}</a>
</div>
The above template codes are equivalent to:
<div class="user">
<a href="{{ url_for('user_profile', user_id=user.id) }}"
title="{{ user.name }}"><img src="{{ user.picture.locate() }}"
width="{{ user.picture.width }}"
height="{{ user.picture.height }}"></a>
</div>
<div class="user">
<a href="${url_for('user_profile', user_id=user.id)}"
title="${user.name}"><img src="${user.picture.locate()}"
width="${user.picture.width}"
height="${user.picture.height}"></a>
</div>
Note
Template expansion of ImageSet might raise ContextError. You should render the template in the context:
with store_context(store):
return render_template('user_profile.html', user=user)
Or use Implicit contexts.
ImageSet provides open_file() method. It returns a file-like object:
from shutil import copyfileobj
with store_context(store):
with user.picture.open_file() as f:
copyfileobj(f, dst)
Note that the responsibility to close an opened file is yours. Recommend to open it in with block.
There’s a shortcut to read byte string from an opened file. Use make_blob() method. The following two ways are equivalent:
# make_blob()
with store_context(store):
blob = user.picture.make_blob()
# open().read()
with store_context(store):
with user.picture.open_file() as f:
blob = f.read()
You can make thumbnails and then store them into the store using generate_thumbnail() method. It takes one of three arguments: width, height, or ratio:
with store_context(store):
# Make thumbnails
width_150 = user.picture.generate_thumbnail(width=150)
height_300 = user.picture.generate_thumbnail(height=300)
half = user.picture.generate_thumbnail(ratio=0.5)
# Get their urls
width_150_url = width_150.locate()
height_300_url = width_300.locate()
half = half.locate()
It returns a made Image object, and it shares the most of the same methods to ImageSet like locate(), open_file(), make_blob().
Once made thumbnails can be found using find_thumbnail(). It takes one of two arguments: width or height and returns a found Image object:
with store_context(store):
# Find thumbnails
width_150 = user.picture.find_thumbnail(width=150)
height_300 = user.picture.find_thumbnail(height=300)
# Get their urls
width_150_url = width_150.locate()
height_300_url = width_300.locate()
It raises NoResultFound exception when there’s no such size.
You can implement find-or-create pattern using these two methods:
def find_or_create(imageset, width=None, height=None):
assert width is not None or height is not None
try:
image = imageset.find_thumbnail(width=width, height=height)
except NoResultFound:
image = imageset.generate_thumbnail(width=width, height=height)
return image
We recommend you to queue generating thumbnails and make it done by backend workers rather than web applications. There are several tools for that like Celery.
It’s so ad-hoc, but there’s a way to explicit storage to use without any context: passing the storage to operations as an argument. Every methods that need the context also optionally take store keyword:
user.picture.from_file(file_, store=store)
user.picture.from_blob(blob, store=store)
user.picture.locate(store=store)
user.picture.open_file(store=store)
user.picture.make_blob(store=store)
user.picture.generate_thumbnail(width=150, store=store)
user.picture.find_thumbnail(width=150, store=store)
The above calls are all equivalent to the following calls in with block:
with store_context(store):
user.picture.from_file(file_)
user.picture.from_blob(blob)
user.picture.locate()
user.picture.open_file()
user.picture.make_blob()
user.picture.generate_thumbnail(width=150)
user.picture.find_thumbnail(width=150)
If your application already manage some context like request-response lifecycle, you can make context implicit by utilizing these hooks. SQLAlchemy-ImageAttach exposes underlayer functions like push_store_context() and pop_store_context() that are used for implementing store_context().
For example, use before_request() and teardown_request() if you are using Flask:
from sqlalchemy_imageattach.context import (pop_store_context,
push_store_context)
from yourapp import app
from yourapp.config import store
@app.before_request
def start_implicit_store_context():
push_store_context(store)
@app.teardown_request
def stop_implicit_store_context(exception=None):
pop_store_context()
Released on July 30, 2014.
Released on August 26, 2013.
Released on June 20, 2013.
Initially released on May 31, 2013.
Scoped context makes other modules able to vertically take an image store object without explicit parameter for it. It’s similar to Flask‘s design decision and Werkzeug‘s context locals. Context locals are workaround to use dynamic scoping in programming languages that doesn’t provide it (like Python).
For example, a function can take an image store to use as its parameter:
def func(store):
url = store.locate(image)
# ...
func(fs_store)
But, what if for various reasions it can’t take an image store as parameter? You should vertically take it using scoped context:
def func():
current_store.locate(image)
with store_context(fs_store):
func()
What if you have to pass the another store to other subroutine?:
def func(store):
decorated_store = DecoratedStore(store)
func2(decorated_store)
def func2(store):
url = store.locate(image)
# ...
func(fs_store)
The above code can be rewritten using scoped context:
def func():
decorated_store = DecoratedStore(current_store)
with store_context(decorated_store):
func2()
def func2():
url = current_store.locate(image)
# ...
with store_context(fs_store):
func()
The exception which rises when the current_store is required but there’s no currently set store context.
Proxy of another image storage.
Parameters: |
|
---|
(dict) The dictionary of concurrent contexts to their stacks.
(LocalProxyStore) The currently set context of the image store backend. It can be set using store_context().
Identifis which context it is (greenlet, stackless, or thread).
Returns: | the identifier of the current context. |
---|
The lower-level function of current_store. It returns the actual store instance while current_store is a just proxy of it.
Returns: | the actual object of the currently set image store |
---|---|
Return type: | Store |
Manually pops the current store from the stack.
Although store_context() and with keyword are preferred than using it, it’s useful when you have to push and pop the current stack on different hook functions like setup/teardown.
Returns: | the current image store |
---|---|
Return type: | Store |
Manually pushes a store to the current stack.
Although store_context() and with keyword are preferred than using it, it’s useful when you have to push and pop the current stack on different hook functions like setup/teardown.
Parameters: | store (Store) – the image store to set to the current_store |
---|
Sets the new (nested) context of the current image storage:
with store_context(store):
print current_store
It could be set nestedly as well:
with store_context(store1):
print current_store # store1
with store_context(store2):
print current_store # store2
print current_store # store1 back
Parameters: | store (Store) – the image store to set to the current_store |
---|
This module provides a short way to attach resizable images to other object-relationally mapped entity classes.
For example, imagine there’s a fictional entity named User and it has its picture and front_cover. So there should be two image entities that subclass Image mixin:
class UserPicture(Base, Image):
'''User's profile picture.'''
user_id = Column(Integer, ForeignKey('User.id'), primary_key=True)
user = relationship('User')
__tablename__ = 'user_picture'
You have to also inherit your own declarative_base() class (Base in the example).
Assume there’s also UserFrontCover in the same way.
Note that the class can override object_id property. Backend storages utilize this to identify images e.g. filename, S3 key. If the primary key of the image entity is integer, object_id automatically uses the primary key value by default, but it can be overridden if needed, and must be implemented if the primary key is not integer or composite key.
There’s also object_type property. Image provides the default value for it as well. It uses the class name (underscores will be replaced by hyphens) by default, but you can override it.
These Image subclasses can be related to the their ‘parent’ entity using image_attachment() function. It’s a specialized version of SQLAlchemy’s built-in relationship() function, so you can pass the same options as relationship() takes:
class User(Base):
'''Users have their profile picture and front cover.'''
id = Column(Integer, primary_key=True)
picture = image_attachment('UserPicture')
front_cover = image_attachment('UserFrontCover')
__tablename__ = 'user'
It’s done, you can store the actual image files using ImageSet.from_file() or ImageSet.from_blob() method:
with store_context(store):
user = User()
with open('picture.jpg', 'rb') as f:
user.picture.from_blob(f.read())
with open('front_cover.jpg', 'rb') as f:
user.front_cover.from_file(f)
with session.begin():
session.add(user)
Or you can resize the image to make thumbnails using ImageSet.generate_thumbnail() method:
with store_context(store):
user.picture.generate_thumbnail(ratio=0.5)
user.picture.generate_thumbnail(height=100)
user.front_cover.generate_thumbnail(width=500)
(collections.Set) The set of vector image types.
The image of the particular size.
Note that it implements __html__() method, a de facto standard special method for HTML templating. So you can simply use it in HTML templates like:
{{ user.profile.find_thumbnail(120) }}
The above template is equivalent to:
{% with thumbnail = user.profile.find_thumbnail(120) %}
<img src="{{ thumbnail.locate() }}"
width="{{ thumbnail.width }}"
height="{{ thumbnail.height }}">
{% endwith %}
(basestring) The identifier string of the image type. It uses __tablename__ (which replaces underscores with hyphens) by default, but can be overridden.
(datetime.datetime) The created time.
(numbers.Integral) The image’s height.”“”
Gets the URL of the image from the store.
Parameters: | store (Store) – the storage which contains the image. current_store by default |
---|---|
Returns: | the url of the image |
Return type: | basestring |
Gets the byte string of the image from the store.
Parameters: | store (Store) – the storage which contains the image. current_store by default |
---|---|
Returns: | the binary data of the image |
Return type: | str |
(basestring) The mimetype of the image e.g. 'image/jpeg', 'image/png'.
(numbers.Integral) The identifier number of the image. It uses the primary key if it’s integer, but can be overridden, and must be implemented when the primary key is not integer or composite key.
Opens the file-like object which is a context manager (that means it can used for with statement).
If use_seek is True (though False by default) it guarentees the returned file-like object is also seekable (provides seek() method).
Parameters: | store (Store) – the storage which contains image files. current_store by default |
---|---|
Returns: | the file-like object of the image, which is a context manager (plus, also seekable only if use_seek is True) |
Return type: | file, FileProxy, file-like object |
(bool) Whether it is original or resized.
(numbers.Integral) The image’s width.
The subtype of Query specialized for Image. It provides more methods and properties over Query.
Note that it implements __html__() method, a de facto standard special method for HTML templating. So you can simply use it in Jinja2 like:
{{ user.profile }}
instead of:
<img src="{{ user.profile|permalink }}"
width="{{ user.profile.original.width }}"
height="{{ user.profile.original.height }}">
Finds the thumbnail of the image with the given width and/or height.
Parameters: |
|
---|---|
Returns: | the thumbnail image |
Return type: | |
Raises sqlalchemy.orm.exc.NoResultFound: | |
when there’s no image of such size |
Stores the blob (byte string) for the image into the store.
Parameters: |
|
---|---|
Returns: | the created image instance |
Return type: |
Stores the file for the image into the store.
Parameters: |
|
---|---|
Returns: | the created image instance |
Return type: |
Similar to from_file() except it’s lower than that. It assumes that raw_file is readable and seekable while from_file() only assumes the file is readable. Also it doesn’t make any in-memory buffer while from_file() always makes an in-memory buffer and copy the file into the buffer.
If size and mimetype are passed, it won’t try to read image and will use these values instead.
It’s used for implementing from_file() and from_blob() methods that are higher than it.
Parameters: |
|
---|---|
Returns: | the created image instance |
Return type: |
Resizes the original (scales up or down) and then store the resized thumbnail into the store.
Parameters: |
|
---|---|
Returns: | the resized thumbnail image. it might be an already existing image if the same size already exists |
Return type: | |
Raises exceptions.IOError: | |
when there’s no original image yet |
The shorthand of locate() for the original.
Parameters: | store (Store) – the storage which contains the image files. current_store by default |
---|---|
Returns: | the url of the original image |
Return type: | basestring |
The shorthand of make_blob() for the original.
Parameters: | store (Store) – the storage which contains the image files. current_store by default |
---|---|
Returns: | the byte string of the original image |
Return type: | str |
The shorthand of open_file() for the original.
Parameters: |
|
---|---|
Returns: | the file-like object of the image, which is a context manager (plus, also seekable only if use_seek is True) |
Return type: | file, FileProxy, file-like object |
The helper function, decorates raw relationship() function, sepcialized for relationships between Image subtypes.
It takes the same parameters as relationship().
Parameters: |
|
---|---|
Returns: | the relationship property |
Return type: |
The file-like types which wraps/proxies an other file objects.
The complete proxy for wrapped file-like object.
Parameters: | wrapped (file, file-like object) – the file object to wrap |
---|
Closes the file. It’s a context manager as well, so prefer with statement than direct call of this:
with FileProxy(file_) as f:
print f.read()
Implementation of collections.Iterator protocol.
Reads at the most size bytes from the file. It maybe less if the read hits EOF before obtaining size bytes.
Parameters: | size – bytes to read. if it is negative or omitted, read all data until EOF is reached. default is -1 |
---|---|
Returns: | read bytes. an empty string when EOF is encountered immediately |
Return type: | str |
Reads an entire line from the file. A trailing newline character is kept in the string (but maybe absent when a file ends with an incomplete line).
Parameters: | size (numbers.Integral) – if it’s present and non-negative, it is maximum byte count (including trailing newline) and an incomplete line maybe returned |
---|---|
Returns: | read bytes |
Return type: | str |
Note
Unlike stdio‘s fgets(), the returned string contains null characters ('\0') if they occurred in the input.
Reads until EOF using readline().
Parameters: | sizehint (numbers.Integral) – if it’s present, instead of reading up to EOF, whole lines totalling approximately sizehint bytes (or more to accommodate a final whole line) |
---|---|
Returns: | a list containing the lines read |
Return type: | list |
It memorizes the current position (tell()) when the context enters and then rewinds (seek()) back to the memorized initial_offset when the context exits.
The almost same to FileProxy except it has seek() and tell() methods in addition.
Sets the file’s current position.
Parameters: |
|
---|
Gets the file’s current position.
Returns: | the file’s current position |
---|---|
Return type: | numbers.Integral |
Iterable object that yields migrated images.
Migrate all image data from source storage to destination storage. All data in source storage are not deleted.
It does not execute migration by itself alone. You need to execute() the plan it returns:
migrate(session, Base, source, destination).execute()
Or iterate it using for statement:
for i in migrate(session, Base, source, destination):
# i is an image just done migration
print(i)
Parameters: |
|
---|---|
Returns: | iterable migration plan which is not executed yet |
Return type: |
Migrate all image data of cls from source storage to destination storage. All data in source storage are not deleted.
It does not execute migration by itself alone. You need to execute() the plan it returns:
migrate_class(session, UserPicture, source, destination).execute()
Or iterate it using for statement:
for i in migrate_class(session, UserPicture, source, destination):
# i is an image just done migration
print(i)
Parameters: |
|
---|---|
Returns: | iterable migration plan which is not executed yet |
Return type: |
This module declares a common interface for physically agnostic storage backends. Whatever a way to implement a storage, it needs only common operations of the interface. This consists of some basic operations like writing, reading, deletion, and finding urls.
Modules that implement the storage interface inside sqlalchemy_imageattach.storages package might help to implement a new storage backend.
The interface of image storage backends. Every image storage backend implementation has to implement this.
Delete the file of the given image.
Parameters: | image (sqlalchemy_imageattach.entity.Image) – the image to delete |
---|
Deletes all reproducible files related to the image. It doesn’t raise any exception even if there’s no such file.
Parameters: |
|
---|
Gets the file-like object of the given criteria.
Parameters: |
|
---|---|
Returns: | the file of the image |
Return type: | file-like object, file |
Raises exceptions.IOError: | |
when such file doesn’t exist |
Note
This is an abstract method which has to be implemented (overridden) by subclasses.
It’s not for consumers but implementations, so consumers should use open() method instead of this.
Gets the file-like object of the given criteria.
Parameters: |
|
---|---|
Returns: | the url locating the image |
Return type: | basestring |
Note
This is an abstract method which has to be implemented (overridden) by subclasses.
It’s not for consumers but implementations, so consumers should use locate() method instead of this.
Gets the URL of the given image.
Parameters: | image (sqlalchemy_imageattach.entity.Image) – the image to get its url |
---|---|
Returns: | the url of the image |
Return type: | basestring |
Opens the file-like object of the given image. Returned file-like object guarantees:
To sum up: you definitely can read the file, in with statement and for loop.
Plus, if use_seek option is True:
For example, if you want to make a local copy of the image:
import shutil
with store.open(image) as src:
with open(filename, 'wb') as dst:
shutil.copyfileobj(src, dst)
Parameters: |
|
---|---|
Returns: | the file-like object of the image, which is a context manager (plus, also seekable only if use_seek is True) |
Return type: | file, FileProxy, file-like object |
Raises exceptions.IOError: | |
when such file doesn’t exist |
Puts the file of the image.
Parameters: |
|
---|
Note
This is an abstract method which has to be implemented (overridden) by subclasses.
It’s not for consumers but implementations, so consumers should use store() method instead of this.
Stores the actual data file of the given image.
with open(imagefile, 'rb') as f:
store.store(image, f)
Parameters: |
|
---|
This module provides some utility functions to manipulate docstrings at runtime. It’s useful for adjusting the docs built by Sphinx without making the code ugly.
Appends the docstring with given lines:
function.__doc__ = append_docstring(
function.__doc__,
'.. note::'
'',
' Appended docstring!'
)
Parameters: |
|
---|---|
Returns: | new docstring which is appended |
Return type: | basestring |
Manually appends class’ docstring with its attribute docstrings. For example:
class Entity(object):
# ...
__doc__ = append_docstring_attributes(
__doc__,
dict((k, v) for k, v in locals()
if isinstance(v, MyDescriptor))
)
Parameters: |
|
---|---|
Returns: | appended docstring |
Return type: | basestring |
Gets the minimum indent string from the docstring:
>>> get_minimum_indent('Hello')
''
>>> get_minimum_indent('Hello\n world::\n yeah')
' '
Parameters: |
|
---|---|
Returns: | the minimum indent string which consists of only whitespaces (tabs and/or spaces) |
Return type: | basestring |
(tuple) The triple of version numbers e.g. (1, 2, 3).
It provides two filesystem-backed image storage implementations:
Abstract base class of FileSystemStore and HttpExposedFileSystemStore.
Filesystem-backed storage implementation with hard-coded URL routing.
Filesystem-backed storage implementation with WSGI middleware which serves actual image files.
from flask import Flask
from sqlalchemy_imageattach.stores.fs import HttpExposedFileSystemStore
app = Flask(__name__)
fs_store = HttpExposedFileSystemStore('userimages', 'images/')
app.wsgi_app = fs_store.wsgi_middleware(app.wsgi_app)
WSGI middlewares that wraps the given app and serves actual image files.
fs_store = HttpExposedFileSystemStore('userimages', 'images/')
app = fs_store.wsgi_middleware(app)
Parameters: | app (collections.Callable) – the wsgi app to wrap |
---|---|
Returns: | the another wsgi app that wraps app |
Return type: | StaticServerMiddleware |
Simple static server WSGI middleware.
Parameters: |
|
---|
Finds the right filename extension (e.g. '.png') for the given mimetype (e.g. image/png).
Parameters: | mimetype (basestring) – mimetype string e.g. 'image/jpeg' |
---|---|
Returns: | filename extension for the mimetype |
Return type: | basestring |
The backend storage implementation for Simple Storage Service provided by Amazon Web Services.
(str) The format string of base url of AWS S3. Contains no trailing slash. Default is 'https://{0}.s3.amazonaws.com'.
(numbers.Integral) The default max-age seconds of Cache-Control. It’s the default value of S3Store.max_age attribute.
HTTP request for S3 REST API which does authentication.
It stores images into physically two separated S3 buckets while these look like logically exist in the same store. It takes two buckets for read-only and overwrite: underlying and overriding.
It’s useful for development/testing purpose, because you can use the production store in sandbox.
Parameters: |
|
---|
All keys marked as “deleted” have this mimetype as its Content-Type header.
Image storage backend implementation using S3. It implements Store interface.
If you’d like to use it with Amazon CloudFront, pass the base url of the distribution to public_base_url. Note that you should configure Forward Query Strings to Yes when you create the distribution. Because SQLAlchemy-ImageAttach will add query strings to public URLs to invalidate cache when the image is updated.
Parameters: |
|
---|
Changed in version 0.8.1: Added public_base_url parameter.
(basestring) The S3 bucket name.
(numbers.Integral) The max-age seconds of Cache-Control.
(basestring) The optional key prefix to logically separate stores with the same bucket.
(basestring) The optional url base for public urls.
SQLAlchemy-ImageAttach is an open source software written by Hong Minhee (initially written for Crosspop). The source code is distributed under MIT license and you can find it at GitHub repository. Check out now:
$ git clone git://github.com/dahlia/sqlalchemy-imageattach.git
If you find any bug, please create an issue to the issue tracker. Pull requests are also always welcome!
Check out SQLAlchemy-ImageAttach Changelog as well.