API Blobstore para Python 3

En esta página se describe cómo usar la API Blobstore, uno de los servicios empaquetados antiguos, con el runtime de Python 3 en el entorno estándar. Tu aplicación puede acceder a los servicios agrupados a través del SDK de servicios de App Engine para Python 3.

Información general

Como webapp no es compatible con Python 3, debes hacer algunos cambios mínimos al migrar el código del controlador Blobstore de Python 2 a Python 3. Para usar la API Blobstore para Python 3, ten en cuenta lo siguiente:

  • Las clases de controlador de Blobstore son clases de utilidad. Esto significa que las clases de controlador ya no se basan en aplicaciones web y que no puedes usar el módulo blobstore_handlers proporcionado por el paquete de aplicaciones web (google.appengine.ext.webapp) ni los parámetros webapp2.RequestHandler en las subclases de estos controladores.

  • Todos los métodos de las clases de controlador de Blobstore requieren el diccionario environ WSGI como parámetro de entrada.

En las siguientes secciones se muestra cómo usar las clases BlobstoreUploadHandler y BlobstoreDownloadHandler para Python 3 en una aplicación Flask y en una aplicación WSGI que no usa un framework de Python. Puedes comparar los ejemplos de Python 3 con el código de ejemplo de Python 2 para obtener más información sobre las diferencias en el código.

Ejemplo: aplicación Flask

En Python 3, las clases de controlador de Blobstore forman parte del módulo google.appengine.ext.blobstore. En el caso de las aplicaciones Flask, todas las llamadas realizadas a métodos de las clases BlobstoreUploadHandler y BlobstoreDownloadHandler requieren el diccionario request.environ (request se importa del módulo flask).

Compara los cambios de código realizados de Python 2 (webapp2) a Python 3 (Flask). Fíjate en cómo usa la aplicación Flask el parámetro request.environ en los métodos get_uploads() y send_blob():

Python 2 (webapp2)

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(),
            blob_key=upload.key())
        user_photo.put()

        self.redirect('/view_photo/%s' % upload.key())

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

app = webapp2.WSGIApplication([
    ('/', PhotoUploadFormHandler),
    ('/upload_photo', PhotoUploadHandler),
    ('/view_photo/([^/]+)?', ViewPhotoHandler),
], debug=True)

Python 3 (Flask)

class PhotoUploadHandler(blobstore.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads(request.environ)[0]
        photo = PhotoUpload(blob_key=upload.key())
        photo.put()

        return redirect("/view_photo/%s" % upload.key())


class ViewPhotoHandler(blobstore.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            return "Photo key not found", 404
        else:
            headers = self.send_blob(request.environ, photo_key)

            # Prevent Flask from setting a default content-type.
            # GAE sets it to a guessed type if the header is not set.
            headers["Content-Type"] = None
            return "", headers


@app.route("/view_photo/<photo_key>")
def view_photo(photo_key):
    """View photo given a key."""
    return ViewPhotoHandler().get(photo_key)


@app.route("/upload_photo", methods=["POST"])
def upload_photo():
    """Upload handler called by blobstore when a blob is uploaded in the test."""
    return PhotoUploadHandler().post()

Para ver el código de ejemplo completo de Python 3 (Flask), consulta GitHub.

Ejemplo: aplicación WSGI sin framework web

El siguiente código de Python 3 (aplicación WSGI) muestra cómo añadir el parámetro environ al usar clases de controlador de Blobstore en una aplicación WSGI sin un framework web. Fíjate en cómo se usa el parámetro environ en los métodos get_uploads() y send_blob(), y compáralo con la versión de Python 2:

Python 2

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(),
            blob_key=upload.key())
        user_photo.put()

        self.redirect('/view_photo/%s' % upload.key())

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

app = webapp2.WSGIApplication([
    ('/', PhotoUploadFormHandler),
    ('/upload_photo', PhotoUploadHandler),
    ('/view_photo/([^/]+)?', ViewPhotoHandler),
], debug=True)

Python 3

class UploadPhotoHandler(blobstore.BlobstoreUploadHandler):
    """Upload handler called by blobstore when a blob is uploaded in the test."""

    def post(self, environ):
        upload = self.get_uploads(environ)[0]
        user_photo = UserPhoto(blob_key=upload.key())
        user_photo.put()

        # Redirect to the '/view_photo/<Photo Key>' URL
        return (
            "",
            http.HTTPStatus.FOUND,
            [("Location", "/view_photo/%s" % upload.key())],
        )


class ViewPhotoHandler(blobstore.BlobstoreDownloadHandler):
    def get_photo(self, environ, photo_key):
        if not blobstore.get(photo_key):
            return "Photo key not found", http.HTTPStatus.NOT_FOUND, []
        else:
            return (
                "",
                http.HTTPStatus.OK,
                list(self.send_blob(environ, photo_key).items()),
            )

    def get(self, environ):
        photo_key = (environ["app.url_args"])[0]
        return self.get_photo(environ, photo_key)


# map urls to functions
urls = [
    (r"^$", UploadFormHandler),
    (r"upload_photo/?$", UploadPhotoHandler),
    (r"view_photo/(.+)$", ViewPhotoHandler),
]

Para ver el código de muestra completo de Python 3, consulta GitHub.