Blobstore API for Python 3

בדף הזה נסביר איך להשתמש ב-Blobstore API, אחד מהשירותים המצורפים מדור קודם, עם סביבת זמן הריצה של Python 3 בסביבה הרגילה. האפליקציה יכולה לגשת לשירותים הכלולים בחבילה באמצעות App Engine services SDK for Python 3.

סקירה כללית

מכיוון שאין תמיכה ב-webapp ב-Python 3, צריך לבצע כמה שינויים מינימליים כשמעבירים קוד של Blobstore handler מ-Python 2 ל-Python 3. כדי להשתמש ב-Blobstore API ל-Python 3, חשוב לזכור את הדברים הבאים:

  • מחלקות של תוכניות לטיפול ב-Blobstore הן מחלקות של כלי עזר. המשמעות היא שמחלקות ה-handler כבר לא מבוססות על webapp, ואי אפשר להשתמש במודול blobstore_handlers שסופק על ידי חבילת webapp‏ (google.appengine.ext.webapp) או בפרמטרים webapp2.RequestHandler במחלקות משנה של ה-handler האלה.

  • כל השיטות במחלקות של Blobstore handler דורשות את מילון ה-WSGI‏ environ כפרמטר קלט.

בקטעים הבאים מוסבר איך להשתמש במחלקות BlobstoreUploadHandler ו-BlobstoreDownloadHandler ב-Python 3 באפליקציית Flask ובאפליקציית WSGI שלא משתמשת ב-framework של Python.

דוגמה: אפליקציית Flask

ב-Python 3, מחלקות ה-handler של Blobstore הן חלק מהמודול google.appengine.ext.blobstore. באפליקציית Flask, כל הקריאות לשיטות במחלקות BlobstoreUploadHandler ו-BlobstoreDownloadHandler דורשות את המילון request.environ (request מיובא מהמודול flask).

השוואה בין שינויים בקוד שבוצעו מ-Python 2 ‏ (webapp2) ל-Python 3 ‏ (Flask). שימו לב איך אפליקציית Flask משתמשת בפרמטר request.environ בשיטות get_uploads() ו-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()

כדי לראות את דוגמת הקוד המלאה ל-Python 3 (Flask), אפשר לעיין ב-GitHub.

דוגמה: אפליקציית WSGI ללא מסגרת אינטרנט

בדוגמת הקוד הבאה ב-Python 3 (אפליקציית WSGI) אפשר לראות איך מוסיפים את הפרמטר environ כשמשתמשים במחלקות של Blobstore handler באפליקציית WSGI ללא framework אינטרנטי. שימו לב איך הפרמטר environ משמש בשיטות get_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.