Source code for ferris.components.upload

from ferris.core import inflector
from settings import app_config
from google.appengine.ext import blobstore, ndb
import wtforms
import logging
import cgi


[docs]class Upload(object): """ Automatically handles file upload fields that need to use the blobstore. This works by: * Detecting if you're on an add or edit action (you can add additional actions with ``upload_actions``, or set ``process_uploads`` to True) * Adding the ``upload_url`` template variable that points to the blobstore * Updating the ``form_action`` and ``form_encoding`` scaffolding variables to use the new blobstore action * Processing uploads when they come back * Adding each upload's key to the form data so that it can be saved to the model Does not require that the handler subclass ``BlobstoreUploadHandler``, however to serve blobs you must subclass ``BlobstoreDownloadHandler``. """ def __init__(self, handler): self.handler = handler self.__uploads = None self.process_uploads = False self.upload_actions = ('add', 'edit') self.gs_bucket_name = None handler.events.before_startup += self.on_before_startup handler.events.scaffold_before_apply += self.on_scaffold_before_apply handler.events.after_dispatch += self.on_after_dispatch def on_before_startup(self, handler): if handler.action in self.upload_actions: self.process_uploads = True def on_scaffold_before_apply(self, handler, form, item): if self.process_uploads: self.process(form) def on_after_dispatch(self, handler, response): """ This will additionally check if ?start is the query string. If so, it will return just the upload url. This is great for rest apis. """ if self.process_uploads: if not handler.get('upload_url'): handler.set(upload_url=self.generate_upload_url(self.handler.action)) if hasattr(handler, 'scaffold'): handler.scaffold.form_action = handler.get('upload_url') handler.scaffold.form_encoding = 'multipart/form-data' if 'start' in handler.request.params: if not response: handler.set(data=handler.get('upload_url')) if 'json' in handler.components: handler.components.json.render() def process(self, form, item=None): """ Process all of the incoming file upload and populate the form with them. Only processes file fields that are present in the form """ for field in [x for x in form if isinstance(x, wtforms.fields.FileField)]: files = self.get_uploads(field.name) if files and files[0]: getattr(form, field.name).data = files[0].key() else: delattr(form, field.name) def generate_upload_url(self, action=None): if not action: action = self.handler.action return blobstore.create_upload_url( success_path=self.handler.uri(action=action, _pass_all=True, _full=True), gs_bucket_name=self.gs_bucket_name ) def serve(self, item, property): if not item: return 404 self.handler.send_blob(getattr(item, property)) return self.handler.response def get_uploads(self, field_name=None): """Get uploads sent to this handler. Args: field_name: Only select uploads that were sent as a specific field. Returns: A list of BlobInfo records corresponding to each upload. Empty list if there are no blob-info records for field_name. """ if self.__uploads is None: self.__uploads = {} for key, value in self.handler.request.params.items(): if isinstance(value, cgi.FieldStorage): if 'blob-key' in value.type_options: info = blobstore.parse_blob_info(value) self.__uploads.setdefault(key, []).append(info) if field_name: try: return list(self.__uploads[field_name]) except KeyError: return [] else: results = [] for uploads in self.__uploads.itervalues(): results += uploads return results