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

1. Przegląd

Ta seria ćwiczeń z programowania (samodzielnych samouczków praktycznych) ma pomóc deweloperom Google App Engine (Standard) w modernizacji aplikacji poprzez przeprowadzenie ich przez serię migracji. Najważniejszym krokiem jest odejście od usług dołączonych do oryginalnego środowiska wykonawczego, ponieważ środowiska wykonawcze nowej generacji są bardziej elastyczne i zapewniają użytkownikom większy wybór opcji usług. Przejście na środowisko wykonawcze nowszej generacji ułatwia integrację z usługami Google Cloud, korzystanie z szerszego zakresu obsługiwanych usług i obsługę bieżących wersji językowych.

W tym pierwszym samouczku pokazujemy pierwsze kroki migracji, które pozwolą zmodernizować platformę internetową w aplikacjach App Engine: przejście z webapp2 na Flask. W aplikacji możesz użyć dowolnej platformy internetowej, która obsługuje routing, ale w tym samouczku używamy platformy Flask, ponieważ jest ona powszechnie stosowana przez społeczność.

Dowiesz się, jak:

  • używać bibliotek innych firm (wbudowanych lub innych);
  • Aktualizowanie plików konfiguracji
  • Migracja prostej aplikacji z webapp2 na platformę Flask

Czego potrzebujesz

Ankieta

Jak zamierzasz wykorzystać to ćwiczenie?

Tylko przeczytaj Przeczytaj i wykonaj ćwiczenia

2. Tło

webapp framework został dołączony do App Engine, gdy usługa została po raz pierwszy uruchomiona w Pythonie 2.5 w 2008 roku. W 2013 r. został on zastąpiony przez następcę webapp2, gdy środowisko wykonawcze 2.7 zostało wycofane na rzecz wersji 2.5.

Chociaż webapp2 (patrz dokumentacja) nadal istnieje i może być używany poza App Engine jako zgodna z WSGI platforma internetowa, nie kieruje on żądań użytkowników do odpowiedniego kodu w aplikacji. Zamiast tego korzysta z App Engine, plików konfiguracyjnych i dewelopera, aby kierować ruch w internecie do odpowiednich „procedur obsługi”. Co więcej, podstawowe zalety webapp2 są nierozerwalnie związane z pakietami usług App Engine, co sprawia, że ta biblioteka jest przestarzała, mimo że działa w Pythonie 3 (zobacz też powiązany problem).

Ten moduł umożliwia praktyczne zapoznanie się z migracją prostej aplikacji webapp2 do Flaska, czyli frameworka obsługiwanego przez App Engine i wiele innych usług poza Google Cloud, co znacznie zwiększa przenośność aplikacji. Jeśli Flask nie jest odpowiednim frameworkiem do przeniesienia Twojej aplikacji, możesz wybrać inny, o ile ma własne routingi. Te warsztaty pokazują osobom decyzyjnym w zakresie technologii informatycznych i programistom, jakie są etapy migracji. Dzięki temu możesz zapoznać się z tym procesem niezależnie od tego, do którego frameworka migrujesz.

Oto główne etapy tej migracji:

  1. Konfiguracja/przygotowanie
  2. Dodawanie biblioteki Flask innej firmy
  3. Aktualizowanie plików aplikacji
  4. Aktualizowanie pliku szablonu HTML

3. Konfiguracja/przygotowanie

Zanim przejdziemy do głównej części samouczka, skonfigurujmy projekt, pobierzmy kod, a następnie przypomnijmy sobie polecenie gcloud i wdrożmy podstawową aplikację, aby mieć pewność, że zaczynamy od działającego kodu.

1. Konfigurowanie projektu

Jeśli jesteś deweloperem, w panelu App Engine prawdopodobnie widzisz już, które usługi są uruchomione. Na potrzeby tego samouczka zalecamy utworzenie nowego projektu lub ponowne użycie istniejącego. Sprawdź, czy projekt ma aktywne konto rozliczeniowe i czy usługa App Engine (aplikacja) jest włączona.

2. Pobieranie przykładowej aplikacji z wartościami podstawowymi

repozytorium migracji GAE znajdziesz cały potrzebny kod. Skopiuj go lub pobierz plik ZIP. W tym samouczku zaczniesz od kodu w folderze Moduł 0 (START), a po jego ukończeniu Twój kod powinien być zgodny z kodem w folderze Moduł 1 (FINISH). Jeśli nie, zapoznaj się z różnicami, aby przejść do następnego modułu.

Folder Module 0 powinien zawierać pliki podobne do tych, które są widoczne na ilustracji przedstawiającej polecenie POSIX ls:

$ ls
app.yaml        index.html      main.py

3. Przypomnij sobie poleceniagcloud

Jeśli nie masz jeszcze na komputerze polecenia gcloud, zainstaluj pakiet Google Cloud SDK i upewnij się, że polecenie gcloud jest dostępne w ścieżce wykonywania. Zapoznaj się też z tymi poleceniami gcloud:

  1. gcloud components update – aktualizowanie pakietu SDK Google Cloud
  2. gcloud auth login – zaloguj się na konto z uprawnieniami.
  3. gcloud config list – wyświetla ustawienia konfiguracji projektu GCP.
  4. gcloud config set project PROJECT_ID – ustaw identyfikator projektu GCP
  5. gcloud app deploy – wdrożyć aplikację App Engine,

Jeśli od jakiegoś czasu nie tworzysz aplikacji na platformę App Engine za pomocą gcloud, przed przejściem do kolejnych kroków uruchom pierwsze 4 polecenia (1–4), aby skonfigurować środowisko. Omówmy pokrótce te polecenia.

Po pierwsze, gcloud components update zapewnia, że masz najnowszą wersję Cloud SDK. Uruchomienie tego polecenia powinno dać wynik podobny do tego:

$ 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 kodu gcloud auth login, aby uwierzytelnić się w przypadku poleceń gcloud, które będziesz wydawać w przyszłości:

$ 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 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 w utworzeniu nowego projektu lub wybraniu już istniejącego. Jeśli wynik polecenia gcloud config list nie pasuje do wybranego projektu, którego chcesz użyć w tym samouczku, uruchom polecenie gcloud config set project PROJECT_ID, aby ustawić identyfikator projektu. Następnie sprawdź, czy ustawiony jest prawidłowy identyfikator projektu, uruchamiając ponownie polecenie gcloud config list.

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

Jeśli wolisz używać konsoli Cloud, możesz utworzyć nowy projekt za pomocą interfejsu użytkownika lub użyć dowolnego projektu, który już masz. W panelu projektu powinna być widoczna karta informacyjna projektu, na której znajduje się jego identyfikator (wraz z nazwą i numerem):

karta informacji o projekcie,

Ostatnie polecenie (nr 5), gcloud app deploy, służy do wdrażania aplikacji w App Engine. Dopiero zaczynamy, więc uruchomienie go teraz jest opcjonalne, ale zdecydowanie nie zniechęcamy do wdrożenia kodu modułu 0, aby sprawdzić, czy działa. Po wykonaniu wybierz region geograficzny, w którym ma działać aplikacja (zwykle jest to miejsce, w którym się znajdujesz). Po ustawieniu nie można go jednak zmienić. Następnie zapoznaj się z pozostałymi informacjami o wdrożeniu. Gdy proces się zakończy, otrzymasz powiadomienie o adresie URL, pod którym będzie dostępna Twoja aplikacja. Oto skrócona wersja tego, co możesz zobaczyć:

$ 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 od jakiegoś czasu nie korzystasz z App Engine, możesz zauważyć, że pierwotne appcfg.py updatepolecenie wdrażania zostało zastąpione poleceniem gcloud app deploy. Więcej informacji o gcloud app deploy znajdziesz na stronie dokumentacji.

Kolejną niedawną zmianą jest adres URL wdrożonych aplikacji, który został zmieniony z http://PROJECT_ID.appspot.com na http://PROJECT_ID.REG_ABBR.r.appspot.com. Większość aplikacji zostanie ostatecznie przekonwertowana do nowego formatu. Więcej informacji o formacie adresu URL znajdziesz w dokumentacji dotyczącej żądań i routingu.

Po wdrożeniu aplikacji odśwież przeglądarkę (może być konieczne kilkukrotne odświeżenie), aby zobaczyć najnowsze wizyty:

aplikacja visitme

Jeśli Twoja aplikacja jest nowa, zobaczysz tylko kilka wizyt.

4. Dodawanie biblioteki Flask innej firmy

Środowisko wykonawcze App Engine w Pythonie 2 udostępnia zestaw „wbudowanych” bibliotek innych firm, które wystarczy określić w pliku app.yaml, aby ich używać. Chociaż ta migracja nie wymaga ich użycia, pojawią się w następnym samouczku dotyczącym migracji (w module 2).

Biblioteki innych firm, które nie są wbudowane, muszą być określone w pliku o nazwie requirements.txt i zainstalowane lokalnie w folderze lib w tym samym katalogu co kod aplikacji, w którym wszystko jest przesyłane do App Engine. Więcej informacji znajdziesz w dokumentacji dotyczącej pakowania bibliotek innych firm.

Skopiowane biblioteki, takie jak Flask, wymagają, aby w pliku konfiguracyjnym appengine_config.py poinformować App Engine, że ma ich szukać w folderze lib. Plik konfiguracyjny appengine_config.py jest umieszczany w tym samym folderze aplikacji najwyższego poziomu co pliki requirements.txtlib. W tej części samouczka:

  • Utwórz requirements.txt (określ skopiowane [nie wbudowane] biblioteki innych firm)
  • Utwórz appengine_config.py (rozpoznawanie bibliotek innych firm)
  • 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 potrzebna jest biblioteka innej firmy – Flask. W momencie pisania tego artykułu najnowsza wersja to 1.1.2, więc utwórz requirements.txt za pomocą tego wiersza:

Flask==1.1.2

Więcej informacji o akceptowanych formatach znajdziesz w requirements.txtdokumentacji.

2. Utwórz: appengine_config.py

Następnym krokiem jest skonfigurowanie App Engine tak, aby rozpoznawał zewnętrzne biblioteki innych firm. 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 robi dokładnie to, co opisaliśmy wcześniej, czyli wskazuje App Engine folder lib, w którym znajdują się skopiowane biblioteki.

3. Instalowanie pakietów i zależności

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

$ pip install -t lib -r requirements.txt

Niezależnie od tego, czy używasz pip czy pip2, po zakończeniu instalacji pakietu powinien pojawić się folder lib, którego zawartość będzie podobna do tej:

$ 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. Aktualizowanie plików aplikacji

Teraz zaktualizujmy plik aplikacji, main.py.

1. Importy

Importy są umieszczane na początku, tak jak we wszystkich plikach Pythona. Po zaimportowaniu platformy webapp2 następuje import biblioteki ndb Datastore, a na końcu rozszerzenia App Engine, które przetwarza szablony w stylu Django. Strona powinna wyglądać tak:

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

Podczas przechodzenia na Flask importujesz jednocześnie Flask i elementy renderowania szablonu. Usuń parę importów powiązanych z webapp2 i zastąp je w ten sposób (import ndb pozostaw bez zmian):

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

2. Uruchomienie

Aplikacje korzystające z webapp2 wymagają pojedynczej tablicy (listy w języku Python), która zawiera listę wszystkich ścieżek i procedur obsługi w dowolnym pliku Pythona (mogą być inne):

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

Pamiętaj, że app.yaml wykonuje routing wyższego poziomu i może wywoływać różne procedury obsługi. Przykładowa aplikacja jest na tyle prosta, że wszystkie ścieżki prowadzą do funkcji obsługi main.py.

Flask nie używa takich tabel routingu, więc usuń te wierszemain.py. Flask wymaga też inicjowania, więc dodaj ten wiersz na górze pliku main.py tuż pod instrukcjami importu:

  • PO:
app = Flask(__name__)

W Flasku inicjujesz framework, a następnie używasz dekoratorów do definiowania ścieżek. Trasy są też powiązane z funkcjami, a nie z klasami ani metodami.

W tym ćwiczeniu z programowania nie będziemy omawiać samouczka Flask, więc poświęć trochę czasu na zapoznanie się z samouczkiem Flaskdokumentacją Flask, aby lepiej poznać ten framework.

3. Model danych

Nie ma tu żadnych zmian. Datastore będzie tematem kolejnych zajęć z programowania.

4. Moduły obsługi

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

  1. Obsługa żądań GET dotyczących ścieżki głównej (/)
  2. Rejestrowanie „wizyty” na stronie internetowej (tworzenie i przechowywanie obiektu Visit)
  3. Wyświetl 10 ostatnich wizyt (za pomocą predefiniowanego szablonu index.html).

webapp2 framework korzysta z modelu wykonywania opartego na klasach, w którym dla każdej obsługiwanej metody HTTP tworzone są moduły obsługi. W naszym prostym przypadku mamy tylko GET, więc definiujemy 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 ma własny system routingu. Zamiast klasy obsługi piszesz funkcje i oznaczasz je trasą, dla której mają być wywoływane. Użytkownicy mogą określić metody HTTP obsługiwane w wywołaniu dekoratora, np. @app.route('/app/', methods=['GET', 'POST']). Domyślną wartością jest tylko GET (i niejawnie HEAD), więc można ją pominąć.

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

  • 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 reprezentatywne dla Twojej aplikacji, która z pewnością będzie bardziej złożona niż ten przykład. Głównym celem tych samouczków jest pomoc w rozpoczęciu pracy, wyrobieniu sobie pewnych nawyków i zrozumieniu, gdzie wprowadzać zmiany w kodzie specyficznym dla App Engine. Aby sprawdzić, czy zmiana została wprowadzona prawidłowo, porównaj ją z modułem 1main.py.

5. Pliki pomocnicze

Plik .gcloudignore nie uległ zmianie. Jego celem jest określenie plików, które nie powinny być wdrażane w App Engine, ponieważ nie są potrzebne do wdrożenia i wykonania aplikacji. Mogą to być m.in. pomocnicze pliki Pythona, pliki kontroli źródła, pliki szablonowe repozytorium i inne pliki. Nasz .gcloudignore wygląda tak (komentarze zostały usunięte dla zwięzłości):

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

6. Aktualizowanie pliku szablonu HTML

1. Przenoszenie pliku szablonu

W folderze repozytorium podstawowego (moduł 0) index.html plik szablonu znajduje się w tym samym folderze co pliki aplikacji. Flask wymaga, aby pliki HTML znajdowały się w folderze templates, więc musisz go utworzyć (mkdir templates) i przenieść do niego plik index.html. W systemie zgodnym ze standardem POSIX, takim jak Linux lub Mac OS X, polecenia będą wyglądać tak:

mkdir templates
mv index.html templates

2. Aktualizowanie pliku szablonu

Po przeniesieniu index.html do templates czas na wprowadzenie niewielkiej, ale wymaganej zmiany. Przyjrzyjmy się w całości oryginalnemu plikowi 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>

webapp2 używa szablonów Django, które wykonują wywoływalne elementy, takie jak visit.timestamp.ctime bez nawiasów ( ), natomiast Jinja2 wymaga ich wyraźnego podania. Chociaż może się to wydawać niewielką zmianą, szablony Jinja są bardziej zaawansowane, ponieważ umożliwiają przekazywanie argumentów w wywołaniach.

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

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

To jedyna wymagana zmiana. W przypadku pozostałych ćwiczeń z kodem dotyczących migracji nie musisz wprowadzać żadnych dodatkowych zmian w index.html.

7. Podsumowanie i czyszczenie

Wdróż aplikację

Po wprowadzeniu wszystkich zmian opisanych w tym samouczku pliki w folderze aplikacji powinny być identyczne (lub prawie identyczne) z plikami w folderze repozytorium modułu 1. Teraz wdróż aplikację i sprawdź, czy działa tak samo jak wersja webapp2 modułu 0.

Użyj polecenia gcloud app deploy, tak jak wcześniej podczas wdrażania oryginalnego kodu modułu 0. Otwórz aplikację na stronie PROJECT_ID.appspot.com w przeglądarce lub za pomocą polecenia curl lub wget, aby sprawdzić, czy działa zgodnie z oczekiwaniami.

Jeśli pojawi się błąd serwera, zwykle oznacza to, że w kodzie w Pythonie jest jakiś błąd. Aby to sprawdzić, zapoznaj się z logami aplikacji. Porównaj też swoje pliki z plikami w repozytorium modułu 1 (link powyżej).

Opcjonalnie: zwalnianie miejsca

A co z wyczyszczeniem danych, aby uniknąć opłat do czasu, aż będziesz gotowy(-a) na kolejny etap migracji? Jako obecni deweloperzy prawdopodobnie znasz już informacje o cenach App Engine.

Opcjonalnie: wyłączanie aplikacji

Jeśli nie chcesz jeszcze przechodzić do następnego samouczka, wyłącz aplikację, aby uniknąć naliczania opłat. Gdy zechcesz przejść do kolejnych ćwiczeń, możesz ponownie włączyć tę funkcję. Gdy aplikacja jest wyłączona, nie generuje ruchu, a tym samym nie powoduje naliczania opłat. Możesz jednak ponosić koszty związane z korzystaniem z Datastore, jeśli przekroczysz bezpłatny limit. W takim przypadku usuń wystarczającą ilość danych, aby zmieścić się w limicie.

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

Dalsze kroki

Istnieją 2 moduły migracji, które ROZPOCZYNAJĄ się od ukończonego kodu modułu 1: moduły 2 i 7.

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

8. Dodatkowe materiały

Problemy i opinie dotyczące ćwiczeń z programowania modułu migracji App Engine

Jeśli zauważysz jakieś problemy z tym kursem, najpierw poszukaj rozwiązania, a dopiero potem zgłoś problem. Linki do wyszukiwania i tworzenia nowych problemów:

Materiały dotyczące migracji

Linki do folderów repozytorium dla modułu 0 (START) i modułu 1 (FINISH) znajdziesz w tabeli poniżej. Możesz też uzyskać do nich dostęp w repozytorium wszystkich migracji App Engine, które możesz sklonować lub pobrać jako plik ZIP.

Ćwiczenia z programowania

Python 2

Python 3

Część 0.

kod

(n/a)

Moduł 1

kod

(n/a)

Zasoby App Engine

Poniżej znajdziesz dodatkowe materiały dotyczące tej konkretnej migracji: