Python 3 用 Blobstore API

このページでは、以前のバンドル サービスの 1 つである Blobstore API を、スタンダード環境の Python 3 ランタイムとともに使用する方法を説明します。アプリを使用すると Python 3 用 App Engine サービス SDK を介してバンドル サービスにアクセスできます。

概要

webapp は Python 3 でサポートされていないため、Blobstore ハンドラのコードを Python 2 から Python 3 に移行する際は、最小限の変更が必要です。Python 3 用の Blobstore API を使用する場合は、次の点に注意してください。

  • Blobstore ハンドラクラスはユーティリティ クラスです。つまり、ハンドラクラスは webapp ベースではなくなり、webapp パッケージ(google.appengine.ext.webapp)が提供する blobstore_handlers モジュールを使用することはできません。また、これらのハンドラのサブクラスで webapp2.RequestHandler パラメータを使用することもできません。

  • Blobstore ハンドラクラスのすべてのメソッドで、入力パラメータとして WSGI environ ディクショナリが必要です。

以降のセクションでは、Flask アプリと Python フレームワークを使用しない WSGI アプリで、Python 3 の BlobstoreUploadHandler クラスと BlobstoreDownloadHandler クラスを使用する方法について説明します。Python 3 の例と Python 2 のサンプルコードを比較して、コード変更の違いを詳細に確認できます。

例: Flask アプリ

Python 3 では、Blobstore ハンドラクラスはモジュール google.appengine.ext.blobstore の一部です。Flask アプリでは、BlobstoreUploadHandler および BlobstoreDownloadHandler クラスのメソッドに対するすべての呼び出しで、request.environ ディクショナリが必要です(requestflask モジュールからインポートされます)。

Python 2(webapp2)で行われたコードの変更を Python 3(Flask)と比較してみましょう。Flask アプリでは get_uploads() メソッドと send_blob() メソッドで request.environ パラメータが使用されている点に注意してください。

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()

Python 3(Flask)の完全なコードサンプルについては、GitHub をご覧ください。

例: ウェブ フレームワークを使用しない WSGI アプリ

次の Python 3(WSGI アプリ)コードでは、ウェブ フレームワークなしで WSGI アプリに Blobstore ハンドラクラスを使用する場合に、environ パラメータを追加する方法を示します。environget_uploads() メソッドと send_blob() メソッドでどのように使用されているかに着目して、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),
]

Python 3 の完全なコードサンプルについては、GitHub をご覧ください。