Moduł 1. Migracja z aplikacji internetowej App Engine2 do Flask

1. Omówienie

Ta seria ćwiczeń z programowania (do samodzielnego ukończenia, praktycznych samouczków) ma pomóc deweloperom w modernizacji aplikacji korzystających z Google App Engine (wersja standardowa) przez przeprowadzenie serii migracji. Najważniejszym krokiem jest odejście od pierwotnych pakietów usług w ramach środowiska wykonawczego, ponieważ środowiska wykonawcze nowej generacji są bardziej elastyczne, co daje użytkownikom większą różnorodność opcji usług. Przejście na środowisko wykonawcze nowej generacji umożliwia łatwiejsze integrację z usługami Google Cloud, korzystanie z szerszego zakresu obsługiwanych usług i obsługę bieżących wersji językowych.

Ten początkowy samouczek przedstawia pierwsze kroki migracji niezbędne do modernizacji platformy internetowej w aplikacjach App Engine: przejście z webapp2 do Flask. W swojej aplikacji możesz użyć dowolnej platformy internetowej obsługującej routing, ale w tym samouczku używamy platformy Flask, która jest powszechnie używana przez społeczność.

Dowiesz się,

  • Korzystanie z bibliotek innych firm (wbudowanych lub innych)
  • Aktualizowanie plików konfiguracji
  • Przeprowadź migrację prostej aplikacji z webapp2 do Flask

Czego potrzebujesz

Ankieta

Jak będziesz używać tego ćwiczenia z programowania?

tylko do przeczytania. Przeczytaj go i wykonaj ćwiczenia
.

2. Tło

Platforma webapp została dodana do pakietu w 2008 roku, gdy App Engine po raz pierwszy wprowadzono na rynek Pythona 2.5. Po wielu latach została ona zastąpiona przez następcę webapp2, gdy w 2013 r. środowisko wykonawcze 2.7 wycofało wersję 2.5.

Chociaż usługa webapp2 (patrz dokumentacja) nadal istnieje i może być używana poza App Engine jako platforma internetowa zgodna z WSGI, nie kieruje własnych żądań użytkowników do odpowiedniego kodu w aplikacji. Zamiast tego kieruje ruch internetowy do odpowiednich „modułów obsługi” za pomocą platformy App Engine, plików konfiguracji i programisty. Poza tym podstawowe zalety webapp2 są nierozerwalnie powiązane z pakietami usług App Engine, co w praktyce powoduje ich wycofanie, mimo że działa w języku Python 3 (zobacz też powiązany problem).

Ten moduł zawiera praktyczne informacje o migracji prostej aplikacji w webapp2 do platformy Flask, platformy obsługiwanej przez App Engine i wielu innych usług spoza Google Cloud, dzięki której aplikacje stają się bardziej przenośne. Jeśli Flask nie jest odpowiednią platformą do przenoszenia Twojej aplikacji, możesz wybrać inną platformę, o ile ma ona własną routing. To ćwiczenie w Codelabs pokazuje osobom podejmującym decyzje związane z technologią informacyjną (ITDM) i deweloperom, na czym polegają kroki migracji. Dzięki temu możesz zapoznać się z tym procesem niezależnie od platformy, na którą faktycznie przeprowadzasz migrację.

Oto podstawowe kroki tej migracji:

  1. Konfiguracja/praca
  2. Dodaj zewnętrzną bibliotekę Flask
  3. Aktualizacja plików aplikacji
  4. Zaktualizuj plik szablonu HTML

3. Konfiguracja/praca

Zanim przejdziemy do głównej części samouczka, skonfigurujmy projekt, pobierz kod, a potem jeszcze raz zapoznaj się z poleceniem gcloud i wdróż aplikację podstawową. Dzięki temu będziemy wiedzieć, że zaczynamy od działającego kodu.

1. Konfigurowanie projektu

Jeśli jesteś deweloperem, w panelu App Engine prawdopodobnie znajdziesz już uruchomione usługi. Na potrzeby tego samouczka zalecamy utworzenie nowego projektu lub ponowne wykorzystanie istniejącego projektu. Sprawdź, czy projekt ma aktywne konto rozliczeniowe i jest włączone (aplikacja) App Engine.

2. Pobierz przykładową aplikację bazową

Repozytorium migracji GAE zawiera cały potrzebny kod. Skopiuj go lub pobierz plik ZIP. W tym samouczku zaczniesz od kodu znajdującego się w folderze modułu 0 (START), a gdy go ukończysz, powinien być zgodny z folderem modułu 1 (FINISH). Jeśli nie, zapoznaj się z różnicami i przejdź do kolejnego modułu.

Folder Moduł 0 powinien zawierać pliki podobne do poniższego, zgodnie z przykładem polecenia POSIX ls:

$ ls
app.yaml        index.html      main.py

3. (Ponowne) zapoznanie się z poleceniami gcloud

Jeśli na swoim komputerze nie masz jeszcze polecenia gcloud, zainstaluj Google Cloud SDK i upewnij się, że w ramach ścieżki wykonywania jest dostępny pakiet gcloud, oraz zapoznaj się z tymi poleceniami gcloud:

  1. gcloud components update – zaktualizuj pakiet Google Cloud SDK
  2. gcloud auth login – zaloguj się na konto z danymi logowania
  3. gcloud config list – wyświetlanie ustawień konfiguracji projektu GCP
  4. gcloud config set project PROJECT_ID – ustaw identyfikator projektu GCP
  5. gcloud app deploy – wdróż swoją aplikację App Engine

Jeśli nie zajmujesz się ostatnio programowaniem w App Engine za pomocą platformy gcloud, uruchom pierwsze 4 polecenia (nr 1–4), aby skonfigurować ustawienia, zanim przejdziesz do kolejnych kroków. Omówmy pokrótce te polecenia.

Przede wszystkim gcloud components update upewnia się, że masz najnowszą wersję pakietu SDK Cloud. Po uruchomieniu tego polecenia dane wyjściowe powinny wyglądać tak:

$ gcloud components update

Your current Cloud SDK version is: 317.0.0
You will be upgraded to version: 318.0.0

┌──────────────────────────────────────────────────┐
│        These components will be updated.         │
├──────────────────────────┬────────────┬──────────┤
│           Name           │  Version   │   Size   │
├──────────────────────────┼────────────┼──────────┤
│ Cloud SDK Core Libraries │ 2020.11.06 │ 15.5 MiB │
│ gcloud cli dependencies  │ 2020.11.06 │ 10.6 MiB │
└──────────────────────────┴────────────┴──────────┘

The following release notes are new in this upgrade.
Please read carefully for information about new features, breaking changes,
and bugs fixed.  The latest full release notes can be viewed at:
  https://cloud.google.com/sdk/release_notes

318.0.0 (2020-11-10)

      . . .
      (release notes)
      . . .

    Subscribe to these release notes at
    https://groups.google.com/forum/#!forum/google-cloud-sdk-announce.

Do you want to continue (Y/n)?

╔════════════════════════════════════════════════════════════╗
╠═ Creating update staging area                             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Uninstalling: Cloud SDK Core Libraries                   ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Uninstalling: gcloud cli dependencies                    ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud SDK Core Libraries                     ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: gcloud cli dependencies                      ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Creating backup and activating new installation          ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.

Update done!

To revert your SDK to the previously installed version, you may run:
  $ gcloud components update --version 317.0.0

Następnie użyj gcloud auth login, aby uwierzytelnić się w przypadku poleceń gcloud, które będziesz wykonywać od tej pory:

$ gcloud auth login
Your browser has been opened to visit:

    https://accounts.google.com/o/oauth2/auth?response_type=code&client_id= . . .

You are now logged in as [YOUR_EMAIL].
Your current project is [PROJECT_ID].  You can change this setting by running:
  $ gcloud config set project PROJECT_ID

Użyj narzędzia gcloud config list, aby sprawdzić bieżące ustawienia projektu:

$ gcloud config list
[core]
account = YOUR_EMAIL
disable_usage_reporting = False
project = PROJECT_ID

Your active configuration is: [default]

Powyższe polecenie powinno pomóc Ci utworzyć nowy projekt lub wybrać już istniejący. Jeśli dane wyjściowe funkcji gcloud config list nie są zgodne z wybranym projektem, którego chcesz używać w tym samouczku, uruchom polecenie gcloud config set project PROJECT_ID, aby ustawić identyfikator projektu. Następnie ponownie uruchom gcloud config list, aby sprawdzić, czy ustawiono właściwy identyfikator projektu.

$ gcloud config set project PROJECT_ID
Updated property [core/project].

Jeśli wolisz użyć konsoli Google Cloud, możesz utworzyć nowy projekt, korzystając z interfejsu użytkownika, lub użyć istniejącego projektu. W panelu projektu powinna się wyświetlić karta z informacjami o projekcie, na której widać jego identyfikator (wraz z nazwą i numerem projektu):

karta informacyjna o projekcie

Ostatnie polecenie (5) (gcloud app deploy) dotyczy wdrożenia aplikacji w App Engine. Ponieważ to dopiero początek, jego uruchomienie jest opcjonalne, ale zdecydowanie zniechęcamy do wdrażania kodu Modułu 0 w celu potwierdzenia jego działania. Po uruchomieniu wybierz region geograficzny, w którym chcesz uruchomić aplikację (zwykle jest to miejsce, w którym się znajdujesz). Nie można go jednak zmienić po ustawieniu. Następnie zapoznaj się z pozostałymi informacjami o wdrożeniu. Po zakończeniu procesu otrzymasz powiadomienie o adresie URL, pod którym aplikacja będzie dostępna. Oto skrócona wersja wyświetlanych informacji:

$ gcloud app deploy
Services to deploy:

descriptor:      [/private/tmp/mod0-baseline/app.yaml]
source:          [/private/tmp/mod0-baseline]
target project:  [PROJECT_ID]
target service:  [default]
target version:  [20201116t220827]
target url:      [https://PROJECT_ID.REG_ABBR.r.appspot.com]


Do you want to continue (Y/n)?

Beginning deployment of service [default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 1 file to Google Cloud Storage                 ═╣
╚════════════════════════════════════════════════════════════╝
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://PROJECT_ID.REG_ABBR.r.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

Jeśli przez jakiś czas nie korzystasz z App Engine, możesz zauważyć, że pierwotne polecenie appcfg.py update wdrożenia zostało zastąpione przez gcloud app deploy. Więcej informacji o usłudze gcloud app deploy znajdziesz na jej stronie dokumentacji.

Kolejną niedawną zmianą są wdrożone aplikacje Adres URL zmieniony z http://PROJECT_ID.appspot.com na http://PROJECT_ID.REG_ABBR.r.appspot.com. Większość aplikacji zostanie ostatecznie przekonwertowana na nowy format. Więcej informacji o formacie adresów URL znajdziesz w dokumentacji dotyczącej żądań i routingu.

Po wdrożeniu aplikacji odśwież przeglądarkę (prawdopodobnie kilka razy), aby zobaczyć ostatnie wizyty:

aplikacja visitme

Jeśli aplikacja jest nowa, będzie widoczna tylko jedna wizyta lub kilka wizyt.

4. Dodaj zewnętrzną bibliotekę Flask

Środowisko wykonawcze App Engine w języku Python 2 udostępnia zestaw wbudowanych bibliotek zewnętrznych. Wystarczy, że określisz je w pliku app.yaml. Ta migracja nie wymaga ich użycia, ale zostaną one omówione w następnym samouczku migracji (w module 2).

Biblioteki innych firm, które nie są wbudowane, należy określić w pliku o nazwie requirements.txt i zainstalować lokalnie w folderze lib, który znajduje się w tym samym katalogu co kod aplikacji, w którym wszystko jest przesyłane do App Engine. Więcej informacji znajdziesz w dokumentacji łączenia bibliotek zewnętrznych.

Skopiowane biblioteki, takie jak Flask, wymagają określenia dla App Engine ich wyszukiwania w folderze lib przy użyciu pliku konfiguracji appengine_config.py. Plik konfiguracji appengine_config.py znajduje się w tym samym folderze aplikacji najwyższego poziomu co requirements.txt i lib. W tej części samouczka:

  • Utwórz requirements.txt (określ skopiowane [niewbudowane] biblioteki zewnętrzne)
  • Utwórz appengine_config.py (rozpoznawaj biblioteki zewnętrzne)
  • Instalowanie pakietów i zależności (innych firm)

1. Utwórz: requirements.txt

Utwórz plik requirements.txt, aby określić pakiety. W naszym przypadku Flask to potrzebna biblioteka zewnętrzna. W momencie tworzenia tego tekstu najnowsza wersja to 1.1.2, utwórz plik requirements.txt z tym jednym wierszem:

Flask==1.1.2

Więcej informacji o akceptowanych formatach znajdziesz w dokumentacji usługi requirements.txt.

2. Utwórz: appengine_config.py

Następnym krokiem jest rozpoznanie zewnętrznych bibliotek przez App Engine. Utwórz plik o nazwie appengine_config.py z tą zawartością:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

Ten kod ma dokładnie to, co określiliśmy wcześniej, czyli wskazuje App Engine na folder lib skopiowanych bibliotek.

3. Zainstaluj pakiety i zależności

Teraz uruchom polecenie pip install, aby utworzyć folder lib i zainstalować w nim Flask oraz jego zależności:

$ pip install -t lib -r requirements.txt

Niezależnie od tego, czy używałeś(-aś) usługi pip czy pip2, po zakończeniu instalacji pakietu powinien istnieć folder lib o zawartości podobnej do:

$ ls lib
bin/
click/
click-7.1.2.dist-info/
flask/
Flask-1.1.2.dist-info/
itsdangerous/
itsdangerous-1.1.0.dist-info/
jinja2/
Jinja2-2.11.2.dist-info/
markupsafe/
MarkupSafe-1.1.1.dist-info/
werkzeug/
Werkzeug-1.0.1.dist-info/

5. Aktualizacja plików aplikacji

Teraz zaktualizujmy plik aplikacji main.py.

1. Importy

Importy są na pierwszym miejscu, tak jak we wszystkich plikach Pythona. Po imporcie platformy webapp2 następuje biblioteka Datastore ndb, a na końcu rozszerzenie App Engine, które przetwarza szablony o stylu Django. Strona powinna wyglądać tak:

  • PRZED:
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

W przypadku przenoszenia do Flask importujesz jednocześnie elementy Flask i mechanizm renderowania szablonu. Usuń parę importów związanych z webapp2 i zastąp je w następujący sposób (pozostaw import ndb bez zmian):

  • PO:
from flask import Flask, render_template, request
from google.appengine.ext import ndb

2. Uruchamianie

Aplikacje korzystające z dyrektywy webapp2 wymagają jednej tablicy (listy w języku Python) z listą wszystkich tras i modułów obsługi w dowolnym pliku Pythona (mogą istnieć inne elementy):

  • PRZED:
app = webapp2.WSGIApplication([
    ('/', MainHandler),
], debug=True)

Pamiętaj, że funkcja app.yaml obsługuje routing wyższego poziomu i może wywoływać inne moduły obsługi. Przykładowa aplikacja jest na tyle prosta, że wszystkie trasy trafiają do modułu obsługi main.py.

Flask nie używa takich tabel routingu, więc usuń te wiersze w main.py. Flask wymaga też zainicjowania, więc dodaj ten wiersz na górze obiektu main.py tuż pod importami:

  • PO:
app = Flask(__name__)

W Flsk inicjujesz platformę, a następnie używasz dekoratorów do zdefiniowania tras. Trasy są też połączone z funkcjami, a nie klasami i metodami.

Dodanie samouczka dotyczącego platformy Flask do tego ćwiczenia w programie nie ma zastosowania, dlatego poświęć trochę czasu na zapoznanie się z samouczkiem Flask i dokumentację Flask, aby lepiej poznać platformę.

3. Model danych

Nie ma tu żadnych zmian. W następnym ćwiczeniu z programowania skupimy się na Datastore.

4. Moduły obsługi

Aplikacja niezależnie od używanej platformy (webapp2 czy Flask) wykonuje 3 czynności:

  1. Obsługuj żądania GET ścieżki głównej (/)
  2. Rejestrowanie „wizyty” strony internetowej (utwórz/zapisz obiekt Visit)
  3. Wyświetl 10 ostatnich wizyt (za pomocą wstępnie zdefiniowanego szablonu, index.html)

Platforma webapp2 wykorzystuje model wykonywania oparty na klasach, w którym moduły obsługi są tworzone dla każdej obsługiwanej metody HTTP. W naszym prostym przypadku mamy tylko GET, więc zdefiniowano metodę get():

  • PRZED:
class MainHandler(webapp2.RequestHandler):
    def get(self):
        store_visit(self.request.remote_addr, self.request.user_agent)
        visits = fetch_visits(10) or ()  # empty sequence if None
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(tmpl, {'visits': visits}))

Jak wspomnieliśmy powyżej, Flask robi to samodzielnie. Zamiast klasy obsługi możesz napisać funkcje i dekorować je trasą, dla której powinny być wywoływane. Użytkownicy mogą określać metody HTTP obsługiwane w wywołaniu dekoratora, np. @app.route('/app/', methods=['GET', 'POST']). Wartość domyślna to tylko GET (i domyślnie HEAD), więc można ją pominąć.

Podczas migracji do Flask zastąp klasę MainHandler i jej metodę get() tą funkcją routingu Flask:

  • PO:
@app.route('/')
def root():
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10) or ()  # empty sequence if None
    return render_template('index.html', visits=visits)

Oczywiście nie jest to reprezentatywny obraz Twojej aplikacji, co z pewnością będzie bardziej złożone niż w przykładzie. Głównym celem tych samouczków jest pomoc w rozpoczęciu pracy nad „pamięcią mięśniową”, i dowiedz się, gdzie wprowadzić zmiany w kodzie App Engine. Aby potwierdzić, że ta zmiana jest prawidłowa, porównaj ją z Modułem 1 main.py.

5. Pliki pomocnicze

Plik .gcloudignore nie zawiera żadnych zmian. Jego celem jest określenie plików do nie wdrażania w App Engine, które są niepotrzebne do wdrażania i wykonywania aplikacji. Dotyczy to m.in. pomocniczego Pythona, pliku kontrolnego źródła, stałego repozytorium itp. Tak wygląda .gcloudignore (z usuniętymi komentarzami dla zwięzłości):

.gcloudignore
.git
.gitignore
.hgignore
.hg/
*.pyc
*.pyo
__pycache__/
/setup.cfg
README.md

6. Zaktualizuj plik szablonu HTML

1. Przenieś plik szablonu

W folderze bazowego repozytorium (moduł 0) plik szablonu index.html znajduje się w tym samym folderze co pliki aplikacji. Flask wymaga umieszczenia plików HTML w folderze templates, więc musisz utworzyć ten folder (mkdir templates) i przenieść do niego index.html. W systemie zgodnym z POSIX, takim jak Linux lub Mac OS X, polecenia będą następujące:

mkdir templates
mv index.html templates

2. Zaktualizuj plik szablonu

Po przeniesieniu index.html do templates musisz wprowadzić niewielką, ale wymaganą zmianę. Spójrzmy na całość oryginalnego pliku szablonu:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

</body>
</html>

Natomiast webapp2 korzysta z szablonów Django, które uruchamiają wywołania, takie jak visit.timestamp.ctime bez nawiasów ( ), natomiast Jinja2 wymaga ich bezpośrednio. Choć brzmi to jak drobna poprawka, szablony Jinja są skuteczniejsze od razu, ponieważ możesz przekazywać argumenty w wywołaniach.

W Django należy utworzyć „tag szablonu” lub napisz filtr. Mając to na uwadze, zaktualizuj funkcję index.html, dodając do wywołania visit.timestamp.ctime parę nawiasów:

  • PRZED:
<li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
  • PO:
<li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>

To jedyna wymagana zmiana. we wszystkich pozostałych ćwiczeniach z programowania dotyczących migracji nie są wymagane żadne dodatkowe zmiany w index.html.

7. Podsumowanie/Czyszczenie

Wdróż aplikację

Gdy zakończysz wszystkie zmiany wprowadzone w tym samouczku, pliki w folderze aplikacji powinny być takie same (lub prawie takie same) jak pliki w folderze repozytorium modułu 1. Wdróż teraz i sprawdź, czy aplikacja Module 1 Flask działa tak samo jak wersja webapp2 modułu 0.

Podczas wdrażania oryginalnego kodu modułu 0 użyj polecenia gcloud app deploy, tak jak poprzednio. Otwórz aplikację na stronie PROJECT_ID.appspot.com (w przeglądarce albo za pomocą polecenia curl lub wget, aby potwierdzić, że działa ona zgodnie z oczekiwaniami).

Jeśli wystąpi jakiś błąd serwera, zwykle oznacza to literówkę w kodzie Pythona. Aby dowiedzieć się więcej, przejrzyj dzienniki aplikacji. Porównaj także swoje pliki z plikami w repozytorium modułu 1 (link powyżej).

Opcjonalnie: wyczyść

A co z czyszczeniem, aby uniknąć opłat, dopóki nie wszystko będzie gotowe do przejścia do kolejnego ćwiczenia z programowania dotyczącego migracji? Jako obecni deweloperzy pewnie już znasz informacje o cenach w App Engine.

Opcjonalnie: wyłącz aplikację

Jeśli nie chcesz na razie przejść do następnego samouczka, wyłącz aplikację, aby uniknąć naliczania opłat. Gdy uznasz, że chcesz przejść do kolejnego ćwiczenia w programowaniu, możesz je ponownie włączyć. Gdy Twoja aplikacja jest wyłączona, nie generuje ona opłat za ruch, ale opłaty mogą być jednak naliczane za wykorzystanie magazynu danychw przypadku przekroczenia bezpłatnego limitu, więc usuń tyle miejsca, aby nie przekroczyć tego limitu.

Jeśli natomiast nie zamierzasz kontynuować migracji i chcesz całkowicie usunąć wszystko, możesz wyłączyć projekt.

Dalsze kroki

Składają się z 2 modułów migracji, które rozpoczynają się od ukończonego kodu w części 1, modułów 2 i 7:

  • Moduł 2 (wymagany, jeśli korzystasz z Datastore)
    • Migracja z App Engine ndb do Cloud NDB
    • Po przejściu na Cloud NDB dostępnych jest wiele innych opcji.
      • Konteneryzowanie aplikacji w celu uruchamiania jej w Cloud Run
      • Dalsza migracja aplikacji do biblioteki klienta Cloud Datastore
      • Migracja aplikacji do Cloud Firestore w celu uzyskania dostępu do funkcji Firebase
  • Moduł 7 (wymagany, jeśli korzystasz z kolejek zadań [push])
    • Dodaj wykorzystanie taskqueue App Engine (push)
    • Przygotowuje aplikację modułu 1 do migracji do Cloud Tasks w module 8

8. Dodatkowe materiały

Problemy/opinie dotyczące modułu migracji App Engine

Jeśli podczas korzystania z tych ćwiczeń z programowania zauważysz jakiekolwiek problemy, najpierw je wyszukaj. Linki do wyszukiwania i tworzenia nowych problemów:

Zasoby migracji

Linki do folderów repozytorium w częściach 0 (START) i modułach 1 (FINISH) znajdziesz w tabeli poniżej. Dostęp do nich możesz też uzyskać z repozytorium wszystkich migracji App Engine, które możesz sklonować lub pobrać w postaci pliku ZIP.

Codelab

Python 2

Python 3

Część 0.

kod

(nd.)

Część 1

kod

(nd.)

Zasoby App Engine

Poniżej znajdziesz dodatkowe materiały na temat tej migracji: