Moduł 3. Migracja z Google Cloud NDB do Cloud Datastore

1. Przegląd

Ta seria ćwiczeń z programowania (samodzielnych samouczków praktycznych) ma pomóc deweloperom Google App Engine (środowisko standardowe) 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.

Ten opcjonalny samouczek pokazuje programistom, jak przeprowadzić migrację z Cloud NDB do Cloud Datastore jako biblioteki klienta do komunikacji z usługą Datastore. Deweloperzy, którzy wolą NDB, mogą nadal z niej korzystać, ponieważ jest ona zgodna z Pythonem 3. Dlatego ta migracja jest opcjonalna. Ta migracja jest przeznaczona tylko dla osób, które chcą utworzyć spójną bazę kodu i biblioteki współdzielone z innymi aplikacjami korzystającymi już z Cloud Datastore. Wyjaśniliśmy to w sekcji „Informacje ogólne”.

Dowiesz się, jak:

  • Korzystanie z Cloud NDB (jeśli nie znasz tej usługi)
  • Migracja z Cloud NDB do Cloud Datastore
  • Dalsze przenoszenie aplikacji do Pythona 3

Czego potrzebujesz

  • Projekt Google Cloud Platform z aktywnym kontem rozliczeniowym GCP
  • podstawowe umiejętności w zakresie Pythona,
  • Praktyczna znajomość podstawowych poleceń systemu Linux
  • Podstawowa wiedza na temat tworzenia i wdrażania aplikacji App Engine
  • działająca aplikacja App Engine w wersji 2.x lub 3.x z modułu 2.

Ankieta

Jak zamierzasz wykorzystać to ćwiczenie?

Tylko przeczytaj Przeczytaj i wykonaj ćwiczenia

2. Tło

Cloud NDB to świetne rozwiązanie Datastore dla deweloperów App Engine, którzy od dawna korzystają z tej platformy. Ułatwia ono przejście na Pythona 3, ale nie jest jedynym sposobem, w jaki deweloperzy App Engine mogą uzyskać dostęp do Datastore. Gdy w 2013 r. usługa Datastore w App Engine stała się osobnym produktem, utworzyliśmy nową bibliotekę klienta Cloud Datastore, aby wszyscy użytkownicy mogli korzystać z Datastore.

Deweloperzy aplikacji App Engine w Pythonie 3 i niekorzystający z App Engine powinni używać Cloud Datastore (a nie Cloud NDB). Zachęcamy programistów aplikacji App Engine w Pythonie 2 do migracji z ndb do Cloud NDB i przeniesienia kodu do Pythona 3. Mogą oni też zdecydować się na dalszą migrację do Cloud Datastore. Jest to logiczna decyzja, zwłaszcza w przypadku deweloperów, którzy mają już kod korzystający z Cloud Datastore, np. wspomniany wyżej, i chcą tworzyć biblioteki współdzielone we wszystkich swoich aplikacjach. Ponowne wykorzystywanie kodu i jego spójność to sprawdzone metody, które przyczyniają się do ogólnego obniżenia kosztów konserwacji, co podsumowano poniżej:

Migracja z Cloud NDB do Cloud Datastore

  • Umożliwia programistom skupienie się na jednej bazie kodu na potrzeby dostępu do Datastore
  • Unikanie utrzymywania części kodu przy użyciu Cloud NDB, a innej części przy użyciu Cloud Datastore
  • Zapewnia większą spójność bazy kodu i lepszą możliwość ponownego wykorzystania kodu.
  • Umożliwia korzystanie z popularnych/udostępnionych bibliotek, co przyczynia się do obniżenia ogólnych kosztów utrzymania.

Proces migracji obejmuje te główne etapy:

  1. Konfiguracja/przygotowanie
  2. Zastępowanie Cloud NDB bibliotekami klienta Cloud Datastore
  3. Aktualizuj aplikację

3. Konfiguracja/przygotowanie

Zanim przejdziemy do głównej części samouczka, skonfigurujmy projekt, pobierzmy kod i wdrożymy aplikację podstawową, aby mieć pewność, że zaczynamy od działającego kodu.

1. Konfigurowanie projektu

Jeśli masz za sobą laboratorium z modułu 2, zalecamy ponowne użycie tego samego projektu (i kodu). Możesz też utworzyć zupełnie nowy projekt lub ponownie wykorzystać inny istniejący projekt. Sprawdź, czy projekt ma aktywne konto rozliczeniowe i czy usługa App Engine (aplikacja) jest włączona.

2. Pobieranie przykładowej aplikacji podstawowej

Jednym z wymagań wstępnych jest działająca przykładowa aplikacja z modułu 2. Jeśli udało Ci się ukończyć ten samouczek, użyj swojego rozwiązania. Możesz to zrobić teraz (link powyżej) lub, jeśli chcesz pominąć ten krok, skopiuj repozytorium modułu 2 (link poniżej).

Niezależnie od tego, czy używasz własnego kodu, czy naszego, ZACZNIEMY od kodu modułu 2. W tym module 3 znajdziesz instrukcje krok po kroku. Po jego ukończeniu kod powinien wyglądać jak w punkcie FINISH. Ten samouczek jest dostępny w wersjach dla Pythona 2 i 3, więc pobierz odpowiednie repozytorium kodu poniżej.

Python 2

Katalog plików STARTowych modułu 2 Pythona 2 (Twój lub nasz) powinien wyglądać tak:

$ ls
README.md               appengine_config.py     requirements.txt
app.yaml                main.py                 templates

Jeśli wykonasz samouczek z modułu 2, będziesz mieć też folder lib z Flaskiem i jego zależnościami. Jeśli nie masz folderu lib, utwórz go za pomocą polecenia pip install -t lib -r requirements.txt, abyśmy mogli w następnym kroku wdrożyć tę podstawową aplikację. Jeśli masz zainstalowane obie wersje Pythona, zalecamy używanie polecenia pip2 zamiast pip, aby uniknąć pomyłki z Pythonem 3.

Python 3

Katalog plików STARTOWYCH modułu 2 języka Python 3 (Twój lub nasz) powinien wyglądać tak:

$ ls
README.md               main.py                 templates
app.yaml                requirements.txt

W przypadku Pythona 3 nie używa się ani lib, ani appengine_config.py.

3. (Ponowne) wdrażanie aplikacji z modułu 2

Pozostałe czynności przygotowawcze, które musisz teraz wykonać:

  1. Przypomnij sobie, jak korzystać z narzędzia wiersza poleceń gcloud (w razie potrzeby).
  2. (Ponowne) wdrożenie kodu modułu 1 w App Engine (w razie potrzeby)

Po pomyślnym wykonaniu tych czynności i potwierdzeniu, że wszystko działa, przejdziemy do dalszej części tego samouczka, zaczynając od plików konfiguracyjnych.

4. Zastępowanie Cloud NDB bibliotekami klienta Cloud Datastore

Jedyna zmiana konfiguracji to niewielka zamiana pakietów w pliku requirements.txt.

1. Zaktualizuj: requirements.txt

Po ukończeniu modułu 2 plik requirements.txt wyglądał tak:

  • PRZED (Python 2 i 3):
Flask==1.1.2
google-cloud-ndb==1.7.1

Zaktualizuj requirements.txt, zastępując bibliotekę Cloud NDB (google-cloud-ndb) najnowszą wersją biblioteki Cloud Datastore (google-cloud-datastore), pozostawiając wpis dotyczący Flaska bez zmian. Pamiętaj, że ostatnia wersja Cloud Datastore zgodna z Pythonem 2 to 1.15.3:

  • PO (Python 2):
Flask==1.1.2
google-cloud-datastore==1.15.3
  • PO (Python 3):
Flask==1.1.2
google-cloud-datastore==2.1.0

Pamiętaj, że repozytorium jest aktualizowane częściej niż ten samouczek, więc plik requirements.txt może odzwierciedlać nowsze wersje. Zalecamy korzystanie z najnowszych wersji każdej biblioteki, ale jeśli nie działają, możesz wycofać zmiany do starszej wersji. Podane powyżej numery wersji są aktualne w momencie ostatniej aktualizacji tego laboratorium.

2. Inne pliki konfiguracji

Pozostałe pliki konfiguracyjne, app.yamlappengine_config.py, powinny pozostać niezmienione w stosunku do poprzedniego kroku migracji:

  • app.yaml powinna (nadal) odwoływać się do pakietów firm zewnętrznych grpciosetuptools.
  • appengine_config.py powinny (nadal) wskazywać pkg_resourcesgoogle.appengine.ext.vendor na zasoby zewnętrzne w lib.

Przejdźmy teraz do plików aplikacji.

5. Aktualizowanie plików aplikacji

W przypadku template/index.html nie wprowadziliśmy żadnych zmian, ale w main.py jest kilka aktualizacji.

1. Importy

Kod początkowy sekcji importu powinien wyglądać tak:

  • PRZED:
from flask import Flask, render_template, request
from google.cloud import ndb

Zastąp import google.cloud.ndb importem dla Cloud Datastore: google.cloud.datastore. Biblioteka klienta Datastore nie obsługuje automatycznego tworzenia pola sygnatury czasowej w obiekcie, więc zaimportuj też moduł biblioteki standardowej datetime, aby utworzyć je ręcznie. Zgodnie z konwencją importy biblioteki standardowej umieszcza się nad importami pakietów innych firm. Po wprowadzeniu tych zmian powinno to wyglądać tak:

  • PO:
from datetime import datetime
from flask import Flask, render_template, request
from google.cloud import datastore

2. Inicjowanie i model danych

Po zainicjowaniu Flaska przykładowa aplikacja z modułu 2, która tworzy klasę modelu danych NDB i jej pola, wygląda tak:

  • PRZED:
app = Flask(__name__)
ds_client = ndb.Client()

class Visit(ndb.Model):
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

Biblioteka Cloud Datastore nie ma takiej klasy, więc usuń deklarację klasy Visit. Nadal potrzebujesz klienta do komunikacji z Datastore, więc zmień ndb.Client() na datastore.Client(). Biblioteka Datastore jest bardziej „elastyczna”, ponieważ umożliwia tworzenie encji bez „wcześniejszego deklarowania” ich struktury, jak w przypadku NDB. Po wprowadzeniu tej zmiany ta część main.py powinna wyglądać tak:

  • PO:
app = Flask(__name__)
ds_client = datastore.Client()

3. Dostęp do Datastore

Migracja do Cloud Datastore wymaga zmiany sposobu tworzenia, przechowywania i wysyłania zapytań dotyczących encji Datastore (na poziomie użytkownika). W przypadku Twoich aplikacji trudność tej migracji zależy od złożoności kodu Datastore. W naszej przykładowej aplikacji staraliśmy się, aby aktualizacja była jak najprostsza. Oto nasz kod początkowy:

  • PRZED:
def store_visit(remote_addr, user_agent):
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    with ds_client.context():
        return (v.to_dict() for v in Visit.query().order(
                -Visit.timestamp).fetch_page(limit)[0])

Za pomocą Cloud Datastore utwórz ogólną encję, identyfikującą zgrupowane obiekty w encji za pomocą „klucza”. Utwórz rekord danych z obiektem JSON (Python dict) par klucz-wartość, a następnie zapisz go w Datastore z oczekiwanym put(). W przypadku Datastore zapytania są podobne, ale prostsze. Tutaj możesz zobaczyć, czym różni się odpowiedni kod Datastore:

  • PO:
def store_visit(remote_addr, user_agent):
    entity = datastore.Entity(key=ds_client.key('Visit'))
    entity.update({
        'timestamp': datetime.now(),
        'visitor': '{}: {}'.format(remote_addr, user_agent),
    })
    ds_client.put(entity)

def fetch_visits(limit):
    query = ds_client.query(kind='Visit')
    query.order = ['-timestamp']
    return query.fetch(limit=limit)

Zaktualizuj treści funkcji store_visit()fetch_visits() zgodnie z powyższymi instrukcjami, zachowując ich sygnatury identyczne z poprzednią wersją. Główny moduł obsługi root() pozostaje bez zmian. Po wprowadzeniu tych zmian aplikacja będzie mogła korzystać z Cloud Datastore i będzie gotowa do testowania.

6. Podsumowanie i czyszczenie

Wdróż aplikację

Ponownie wdróż aplikację za pomocą gcloud app deploy i sprawdź, czy działa. Kod powinien teraz być zgodny z kodem w folderach repozytorium modułu 3:

Jeśli zaczniesz od tego artykułu, nie wykonując żadnych poprzednich ćwiczeń z programowania, aplikacja się nie zmieni. Będzie rejestrować wszystkie wizyty na głównej stronie internetowej (/) i po wystarczającej liczbie wizyt będzie wyglądać tak:

aplikacja visitme

Gratulujemy ukończenia ćwiczenia z modułu 3. Wiesz już, że do uzyskiwania dostępu do Datastore możesz używać bibliotek klienta Cloud NDB i Cloud Datastore. Dzięki migracji do tego rozwiązania możesz teraz korzystać z bibliotek udostępnionych, wspólnego kodu i ponownego wykorzystania kodu, co zapewnia spójność i obniża koszty utrzymania.

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

Zapoznaj się z tymi modułami migracji:

  • Moduł 3 (dodatkowy): przejdź do sekcji dodatkowej, aby dowiedzieć się, jak przenieść aplikację do Pythona 3 i środowiska wykonawczego App Engine nowej generacji.
  • Moduł 7: kolejki zadań push App Engine (wymagane, jeśli używasz kolejek zadań [push])
    • Dodaje zadania push App Engine taskqueue do aplikacji Moduł 1
    • Przygotowuje użytkowników do migracji do Cloud Tasks w module 8.
  • Moduł 4. Migracja do Cloud Run za pomocą Dockera
    • Konteneryzowanie aplikacji do uruchamiania w Cloud Run za pomocą Dockera
    • Umożliwia pozostanie przy Pythonie 2
  • Moduł 5. Migracja do Cloud Run za pomocą Cloud Buildpacks
    • Konteneryzowanie aplikacji do uruchamiania w Cloud Run za pomocą pakietów kompilacji Cloud Build
    • Nie musisz nic wiedzieć o Dockerze, kontenerach ani Dockerfile.
    • Wymaga przeniesienia aplikacji do Pythona 3.
  • Moduł 6. Migracja do Cloud Firestore
    • Migracja do Cloud Firestore w celu uzyskania dostępu do funkcji Firebase
    • Cloud Firestore obsługuje Pythona 2, ale te ćwiczenia są dostępne tylko w Pythonie 3.

7. BONUS: migracja do Pythona 3

Aby mieć dostęp do najnowszego środowiska wykonawczego i funkcji App Engine, zalecamy migrację do Pythona 3. W naszej przykładowej aplikacji Datastore była jedyną wbudowaną usługą, z której korzystaliśmy. Po przejściu z ndb na Cloud NDB możemy teraz przenieść aplikację do środowiska wykonawczego Pythona 3 w App Engine.

Przegląd

Przenoszenie kodu do języka Python 3 nie jest tematem samouczka Google Cloud, ale ta część laboratorium daje programistom wyobrażenie o tym, czym różni się środowisko wykonawcze App Engine w Pythonie 3. Jedną z najważniejszych funkcji środowiska wykonawczego nowej generacji jest uproszczony dostęp do pakietów innych firm: nie musisz określać wbudowanych pakietów w app.yaml ani kopiować ani przesyłać niewbudowanych bibliotek – są one instalowane niejawnie na podstawie informacji w requirements.txt.

Ponieważ nasz przykład jest bardzo prosty i Cloud Datastore jest zgodny z Pythonem 2 i 3, nie trzeba przenosić kodu aplikacji do wersji 3.x: aplikacja działa w wersjach 2.x i 3.x bez zmian, co oznacza, że w tym przypadku jedyne wymagane zmiany dotyczą konfiguracji:

  1. Uprość app.yaml, aby odwoływać się do Pythona 3, i usuń odniesienie do dołączonych bibliotek innych firm.
  2. Usuń appengine_config.py i folder lib, ponieważ nie są już potrzebne.

Pliki aplikacji main.pytemplates/index.html pozostaną niezmienione.

Zaktualizuj: requirements.txt

Ostatnia wersja Cloud Datastore obsługująca Pythona 2 to 1.15.3. Zaktualizuj requirements.txt do najnowszej wersji Pythona 3 (może być nowsza). W momencie pisania tego samouczka najnowszą wersją była 2.1.0, więc zmień ten wiersz, aby wyglądał tak (lub tak, jak najnowsza wersja):

google-cloud-datastore==2.1.0

Uprość app.yaml

PRZED:

Jedyną prawdziwą zmianą w tej przykładowej aplikacji jest znaczne skrócenie app.yaml. Przypominamy, co mieliśmy w app.yaml na koniec modułu 3:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

PO:

W Pythonie 3 dyrektywy threadsafe, api_versionlibraries są przestarzałe. Zakłada się, że wszystkie aplikacje są bezpieczne dla wątków, a dyrektywa api_version nie jest używana w Pythonie 3. W usługach App Engine nie ma już wstępnie zainstalowanych pakietów innych firm, więc libraries też jest wycofany. Więcej informacji o tych zmianach znajdziesz w dokumentacji dotyczącej zmian w app.yaml. W związku z tym usuń wszystkie 3 wersje z app.yaml i zaktualizuj je do obsługiwanej wersji Pythona 3 (patrz poniżej).

Opcjonalnie: użycie dyrektywy handlers

Poza tym wycofana została dyrektywa handlers, która kieruje ruch do aplikacji App Engine. Środowisko wykonawcze nowej generacji oczekuje, że frameworki internetowe będą zarządzać routingiem aplikacji, dlatego wszystkie „skrypty obsługi” muszą zostać zmienione na „auto”. Łącząc powyższe zmiany, otrzymasz ten kod: app.yaml.

runtime: python38

handlers:
- url: /.*
  script: auto

Więcej informacji o script: auto znajdziesz na app.yaml stronie referencyjnej.

Usuwanie dyrektywy handlers

Ponieważ element handlers jest wycofany, możesz też usunąć całą sekcję, pozostawiając jedno wierszowy element app.yaml:

runtime: python38

Domyślnie uruchomi to serwer WWW Gunicorn WSGI, który jest dostępny dla wszystkich aplikacji. Jeśli znasz gunicorn, to jest polecenie wykonywane, gdy jest ono uruchamiane domyślnie z najprostszym app.yaml:

gunicorn main:app --workers 2 -c /config/gunicorn.py

Opcjonalnie: użycie dyrektywy entrypoint

Jeśli jednak aplikacja wymaga określonego polecenia uruchamiania, można je określić za pomocą dyrektywy entrypoint, co spowoduje utworzenie app.yaml w tym formacie:

runtime: python38
entrypoint: python main.py

W tym przykładzie wyraźnie żądamy użycia serwera deweloperskiego Flask zamiast gunicorn. Do aplikacji należy też dodać kod, który uruchamia serwer deweloperski na interfejsie 0.0.0.0 na porcie 8080. W tym celu dodaj ten mały fragment na końcu pliku main.py:

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

Więcej informacji o entrypoint znajdziesz na app.yaml stronie referencyjnej. Więcej przykładów i sprawdzonych metod znajdziesz w dokumentacji dotyczącej uruchamiania standardowego środowiska App Engine oraz w dokumentacji dotyczącej uruchamiania elastycznego środowiska App Engine.

Usuń: appengine_config.pylib

Usuń plik appengine_config.py i folder lib. Podczas migracji do Pythona 3 App Engine pobiera i instaluje pakiety wymienione w requirements.txt.

Plik konfiguracyjny appengine_config.py służy do rozpoznawania bibliotek i pakietów innych firm, niezależnie od tego, czy zostały skopiowane przez Ciebie, czy są już dostępne na serwerach App Engine (wbudowane). Podczas przechodzenia na język Python 3 najważniejsze zmiany to:

  1. Brak pakietów skopiowanych bibliotek innych firm (wymienionych w requirements.txt)
  2. Brak pip install w folderze lib, co oznacza, że nie ma folderu lib.
  3. Brak wbudowanych bibliotek zewnętrznych w app.yaml
  4. Nie trzeba odwoływać się do aplikacji w bibliotekach innych firm, więc nie ma pliku appengine_config.py.

Wystarczy wymienić wszystkie wymagane biblioteki innych firm w requirements.txt.

Wdróż aplikację

Ponownie wdróż aplikację, aby mieć pewność, że działa. Możesz też sprawdzić, jak blisko Twoje rozwiązanie jest przykładowego kodu w Pythonie 3 z modułu 3. Aby zobaczyć różnice w Pythonie 2, porównaj kod z jego wersją w Pythonie 2.

Gratulujemy ukończenia kroku dodatkowego w module 3. Zapoznaj się z dokumentacją dotyczącą przygotowywania plików konfiguracyjnych dla środowiska wykonawczego Python 3. Na koniec zapoznaj się z podsumowaniem powyżej, aby poznać dalsze kroki i sposoby usuwania danych.

Przygotowywanie Twojej aplikacji

Gdy nadejdzie czas na migrację aplikacji, musisz przenieść pliki main.py i inne pliki aplikacji do wersji 3.x. Dlatego warto zadbać o to, aby aplikacja w wersji 2.x była jak najbardziej „kompatybilna z przyszłymi wersjami”.

W sieci znajdziesz wiele materiałów, które Ci w tym pomogą. Oto kilka najważniejszych wskazówek:

  1. Upewnij się, że wszystkie zależności aplikacji są w pełni zgodne z wersją 3.x.
  2. Upewnij się, że aplikacja działa w wersji co najmniej 2.6 (najlepiej 2.7).
  3. Upewnij się, że aplikacja przechodzi cały zestaw testów (i ma co najmniej 80% pokrycia).
  4. Korzystaj z bibliotek zgodności, takich jak six, Future lub Modernize.
  5. Poznaj najważniejsze różnice między wersjami 2.x i 3.x, które nie są ze sobą zgodne
  6. Każda operacja wejścia-wyjścia prawdopodobnie spowoduje niezgodności między ciągami Unicode a ciągami bajtów.

Aplikacja przykładowa została zaprojektowana z uwzględnieniem tych kwestii, dlatego działa od razu w wersjach 2.x i 3.x. Dzięki temu możemy skupić się na pokazaniu Ci, co należy zmienić, aby korzystać z platformy nowej generacji.

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 2 (START) i modułu 3 (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

Moduł 2

kod

kod

Moduł 3

kod

kod

Zasoby App Engine

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