API de Blobstore para Python 3

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

Descripción general

Debido a que webapp no es compatible con Python 3, debes realizar algunos cambios mínimos cuando migres el código del controlador de Blobstore de Python 2 a Python 3. Si deseas usar la API de 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 webapp, y no puedes usar el módulo blobstore_handlers proporcionado por el paquete de webapp (google.appengine.ext.webapp) ni los parámetros webapp2.RequestHandler en subclases de estos controladores.

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

En las siguientes secciones, se muestra cómo usar las clases BlobstoreUploadHandler y BlobstoreDownloadHandler para Python 3 en una app de Flask y una app 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 de cambio de código.

Ejemplo: Aplicación Flask

En Python 3, las clases de controlador de Blobstore son parte del módulo google.appengine.ext.blobstore. Para una app de Flask, todas las llamadas realizadas a los métodos en las clases BlobstoreUploadHandler y BlobstoreDownloadHandler requieren el diccionario request.environ (request se importa desde el módulo flask).

Compara los cambios de código realizados de Python 2 (webapp2) a Python 3 (Flask). Ten en cuenta cómo la app de Flask usa 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 la muestra de código completa de Python 3 (Flask), consulta GitHub.

Ejemplo: Aplicación WSGI sin framework web

El siguiente código de Python 3 (app WSGI) muestra cómo agregar el parámetro environ cuando se usan clases de controlador de Blobstore para una app WSGI sin un framework web. Observa 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 la muestra de código completo de Python 3, consulta GitHub.