Menjadi Developer Web dengan Python dan Flask IV: Database

Bagus Aji Santoso 18 Januari 2018

 Menjadi Developer Web dengan Python dan Flask IV: Database

*Artikel ini merupakan terjemahan The Flask Mega-Tutorial Part IV: Database dari seri The Flask Mega-Tutorial karya Miguel Grinberg yang akan dirilis secara gratis bab per bab sampai bulan Mei 2018. Dukung penulis aslinya dengan membeli buku/video tutorial lengkapnya di sini. *

Topik pembahasan bab ini sangat penting. Sebagian besar aplikasi akan membutuhkan suatu cara untuk menyimpan data yang dapat diambil secara efisien dan inilah kenapa database diciptakan.

The GitHub links for this chapter are: Browse, Zip, Diff.

Database di Flask

Mungkin pembaca sudah tahu sebelumnya bahwa Flask tidak memiliki fitur database bawaan. Database merupakan salah satu area dimana kita diberikan kebebasan untuk memilih database apa yang cocok dengan aplikasi kita daripada dipaksa untuk menggunakan satu solusi saja.

Ada banyak pilihan database untuk Python yang bagus, kebanyaka mereka memiliki ekstensi Flask sehingga membuat proses integrasi ke aplikasi menjadi lebih baik. Database dapat dibagi menjadi dua grup yaitu grup yang mengikuti model relasi dan grup yang tidak mengikuti model relasi. Grup yang dikedua sering disebut NoSQL yang mengindikasikan bahwa mereka tidak mengimplementasi SQL. Meski ada banyak produk database yang bagus di kedua grup tersebut, penulis merasa bahwa database relasiional lebih cocok untuk aplikasi yang memiliki struktur data seperti daftar user, blog, post, dll. sementara NoSQL cocok untuk data yang kurang terstruktur. Aplikasi yang akan kita buat dapat diimplementasi menggunakan database manapun, namun karena alasan yang sebelumnya sudah penulis kemukakan, maka kita hanya akan menggunakan database relasional.

Di artikel sebelumnya penulis sudah mengenalkan ekstensi Flask. Di bab ini kita akan kembai menggunakan dua ekstensi yaitu Flask-SQLAlchemy, ekstensi Flask untuk menggunakan paket SQLAlchemy yang sudah dibungkus sedemikian rupa sehingga lebih ramah terhadap kode Flask. SQLAlchemy adalah sebuah Object Relational Mapper atau ORM. ORM memungkinkan aplikasi untuk menggunakan database dengan data high-level seperti kelas, objek dan method daripada menggunakan tabel dan kode SQL secara langsung. Tugas ORM adalah menerjemahkan operasi high-level menjadi perintah database.

Hal yang menarik tentang SQLAlchemy adalah bahwa ia bukan ORM hanya untuk satu sistem database. SQLAlchemy mendukung banyak sistem database diantaranya MySQL, PostgreSQL dan SQLite. Fitur ini sangat berguna karena kita bisa memakai sistem SQLite sederhana di lingkungan development lalu menggunakan sistem yang lebih terjamin untuk production menggunakan MySQL atau PostgreSQL tanpa mengubah aplikasi.

Untuk memasang Flask-SQLAlchemy, pastikan sudah mengaktifkan virtual environment (baca lagi artikel pertama jika lupa) dan jalankan:

(venv) $ pip install flask-sqlalchemy

Database Migrations

Sebagian besar tutorial yang penulis temui membahas pembuatan dan penggunaan database namun tidak menunjukkan bagaimana menangani data yang sudah ada saat aplikasi semakin berkembang. Hal ini cukup berat karena database relasional fokus pada data yang terstruktur sehingga saat strukturnya berubah maka data yang sudah ada di database harus di migrasikan ke struktur yang baru.

Ekstensi kedua yang akan kita pakai di bab ini adalah Flask-Migrate. Ekstensi ini merupakan pembungkus Flask untuk Alembic, sebuah database migration framework-nya SQLAlchemy. Bekerja dengan database migrations akan menambah pekerjaan dalam menyiapkan database, namun tambahan kerjaan ini merupakan harga yang murah dibandingkan keuntungan yang akan kita dapatkan di masa mendatang.

Proses pemasangan Flask-Migrate mirip dengan proses yang sudah kita lakukan sebelumnya:

(venv) $ pip install flask-migrate

Konfigurasi Flask-SQLAlchemy

Saat proses pembuatan aplikasi, penulis akan menggunakan SQLite database. SQLite database adalah pilihan paling nyaman untuk dipakai membuat aplikasi kecil, kadang aplikasi yang tidak begitu kecil pun masih bisa. Karena database disimpan di sebuah file maka kita tidak perlu menjalankan server database seperti MySQL dan PostgreSQL.

Kita akan menambah dua configuration baru kedalam config file:

import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config(object):
    # ...
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'app.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

Ekstensi Flask-SQLAlchemy mengambil lokasi file database melalui variabel SQLALCHEMY_DATABASE_URI . Seperti yang dapat pembaca ingat dari bab sebelumnya, mengambil konfigurasi dari environment variable dan menyiapkan nilai bawaan apabila environment variable tidak tersedia merupakan kebiasaan yang baik . Disini penulis mengambil alamat database dari environment variable DATABASE_URL , dan apabila tidak tidak ada maka penulis akan membuat file app.db di direktori utama aplikasi ayng ditentukan oleh basedir.

Konfigurasi SQLALCHEMY_TRACK_MODIFICATIONS diatur nilainya menjadi False untuk mendisable fitur Flask-SQLAlchemy yang tidak dibutuhkan (fitur untuk memberitahu aplikasi tiap kali terjadi perubahan di database)

Database dapat digunakan didalam aplikasi dalam bentuk database instance. Database migration engine uga akan memiliki sebuah instance. Berikut ini objek-objek yang perlu dibuat di file app/init.py :

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)

from app import routes, models

Ada tiga perubahan yang kita buat. Pertama, kita membuat objek db yang merepresentasikan database. Lalu kita juga menambah objek lain sebagai migration enginenya. Semoga pembaca sudah mulai bisa melihat pola penggunaan ekstensi Flask. Sebagian besar ekstensi diinisialisasikan seperti dua objek tersebut. Terakhir kita mengimpor modul baru bernama model di bagian bawah. Modul ini akan mendefinisikan struktur database.

Database Models

Data yang akan disimpan didalam database diwakilkan oleh kumpulan kelas, biasanya disebut dengan database model. Lapiran ORM didalam SQLAlchem akan menerjemahkan objek dari model menjadi tabel-tabel SQL yang diperlukan.

Mari kita mulai membuat model yang mewakilkan user. Dengan memakai WWW SQL Designer tool, kita dapat membuat diagram seperti pada gambar:

users table

Field id biasanya akan selalu ada disetiap model, dipakai sebagai primary key. Setiap user di database akan diberikan nilai id yang berbeda satu sama lain. Primary key pada umumnya akan selalu diisi secara otomatis oleh database, jadi kita cukup menandai id sebagai primary key.

Field username, email dan password_hash diatur sebagai strings (atau VARCHAR dalam istilah database), juga diberikan batas panjang agar database bisa mengoptimalkan penggunaan ruang yang dipakai. Field username dan email mungkin sudah tidak perlu dijelaskan lagi fungsinya, namun password_hash perlu mendapatkan perhatian lebih. Diaplikasi ini kita ingin agar saat database bocor, hacker tidak mendapatkan password dalam bentuk mentahnya melainkan dalam bentuk yang sudah diacak sehingga hacker tersebut akan mendapat kesulitan untuk menggunakannya. Untuk saat ini tidak perlu pusing memikirkannya, akan kita bahas lagi di masa mendatang.

Jadi sementara ini berikut tabel user yang akan kita pakai dan ditulis di file app/models.py:

from app import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def __repr__(self):
        return '<User {}>'.format(self.username)    

Kelas User diturunkan dari db.Model, base class untuk semua model Flask-SQLAlchemy. Kelas ini memiliki beberapa field sebagai variabel kelas. Field diisi dengan instance dari db.Column, dengan mengambil field sebagai salah satu argumennya, ditambah dengan argumen lain yang diperlukan misalnya menentukan field mana yang harus memiliki isi yang berbeda dan terindeks sehingga proses pencarian menadi lebih efisien.

Method __repr__ memberitahuu Python bagaimana mencetak objek kelas ini, berguna nanti untuk debugging. Pembaca dapat melihat cara kera __repr__() di interpreter Python di bawah:

>>> from app.models import User
>>> u = User(username='susan', email='susan@example.com')
>>> u
<User susan>

Membuat Migration Repository

Kelas model yang dibuat di pembahasan sebelumnya menentukan struktur awal sebuah databas (atau dikenal juga dengan istilah schema). Saat aplikasi menjadi semakin besar, mungkin kita akan mengubah isinnya untuk menambah data baru juga mngubah serta menghapus data yang sudah ada. Alembic (migration framework yang dipakai Flask-Migrate) akan membuat perubahan schema sedemikian rupa sehingga database tidak perlu dibuat ulang dari awal.

Untuk mencapai tujuan ini Alembic menyimpan sebuah migration repository, yang merupakan direktori penyimpanan skrip migration. Setiap kali perubahan terjadi di schema database, sebuah skrip migration akan dieksekusi sesuai urutan dibuat.

Flask-Migrate menampilkan prosesnya dari perintah flask. Pembaca sudah melihat flask run, yang merupakan salah satu perintah asli Flask. Perintah flask db ditambah oleh Flask-Migrate untuk melakukan semua hal yang berhubungan dengan database migration. Jadi sekarang mari kita buat ulang migration repository untuk microblog dengan menajlankan flask db init:

(venv) $ flask db init
  Creating directory /home/miguel/microblog/migrations ... done
  Creating directory /home/miguel/microblog/migrations/versions ... done
  Generating /home/miguel/microblog/migrations/alembic.ini ... done
  Generating /home/miguel/microblog/migrations/env.py ... done
  Generating /home/miguel/microblog/migrations/README ... done
  Generating /home/miguel/microblog/migrations/script.py.mako ... done
  Please edit configuration/connection/logging settings in
  '/home/miguel/microblog/migrations/alembic.ini' before proceeding.

Ingat bahwa perintah flask bergantung pada environment variable FLASK_APP untuk mengetahui di mana Flask berada. Untuk aplikasi ini pembaca perlu mengatur FLASK_APP=microblog.py, seperti yang sudah didiskusikan di bab 1.

Setelah menjalankan perintah ini pembaca akan menemukan direktori migrations yang baru dibuat dengan beberapa file dan direktori lainnya. Semua file ini harus menadi bagian dari aplikasi ini, dengan kata lain harus ditambahkan ke source control (contohnya git).

Database Migration yang Pertama

Dengan adanya migration repository sekarang saatnya kita melakukan database migration. Ada dua cara untuk membuat database migration yaitu secara manual atau otomatis. Untuk membuat secara otomatis, Alembic membandingkan schema database yang ada di model dengan schema database yang sudah ada. Ia lalu membuat skrip migration untuk membuat perubahan yang diperlukan. Sekarang karena belum ada database yang pernah dibuat, maka migrasi otomatis akan menambah objek model User ke dalam skrip migration. Perintah flask db migrate akan membuat migrasi otomatis ini:

(venv) $ flask db migrate -m "users table"
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'user'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username']'
  Generating /home/miguel/microblog/migrations/versions/e517276bb1c2_users_table.py ... done

Teks yang dihasilkan dari perintah di atas mungkin bisa menggambarkan apa yang ditambahkan olehh Alembic ke sebuah migration. Dua baris pertama biasanya bisa diabaikan. Alembic lalu mengatakan bahwa ia menemukan sebuah tabel user dan dua indeks. Ia lalu memberitahu dimana ia membuat skrip migrationnya. Kode e517276bb1c2 dibuat secara otomatis dan akan selalu berbeda untuk tiap orang. Opsi -m pada perintah di atas tidak wajib dipakai, ia hanya menambah deskripsi singkat disebelah baris migration.

Skrip migration yang dibuat juga menjadi bagian dari aplikasi sehingga perlu ditambahkan juga ke source control. Pembaca bisa melihat isi skrip tersebut jika penasaran dengan isinya. Pembaca akan melihat dua fungsi bernama upgrade() dan downgrade(). Fungsi upgrade() akan mengaplikasikan migration dan downgrade() akan menghapusnya (kebalikan). Kedua fungsi ini memungkinkan Alembic untuk mengatur database untuk kembali ke schema tertentu dari versi migration sebelum-sebelumnya dengan fungsi downgrade.

Perintah flask db migrate tidak mengubah apapun ke database, ia hanya membuatkan skrip migration. Untuk melakukan perubahan di database gunakan perintah flask db upgrade.

(venv) $ flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> e517276bb1c2, users table

Karena aplikasi ini menggunakan SQLite, perintah upgrade akan mendeteksi abhwa sebuah database belum ada sehingga ia akan membuatnya (akan ada file database SQLite baru bernama app.db setelah perintah sukses). Jika menggunakan server database seperti MySQL dan PostgreSQL, kita harus membuat dulu sebuah database di server sebelum menjalankan upgrade.

Alur Kerja Database Upgrade dan Downgrade

Meski belum bisa apa-apa, tidak salah bila kita coba membahas sebentar bagaimana strategi database migration yang akan dilakukan nantinya. Bayangkan kita memiliki aplikasi di komputer development juga ada salinan di server yang sedang berjalan.

Anggap untuk rilis update berikutnya kita membuat perubahan di model, contohnya membuat sebuah tabel baru. Tanpa migrasi, kita harus menentukan sendiri bagaimana mengubah schema database, baik di komputer development juga di server sehingga ada banyak pekerjaan yang harus dilakukan.

Dengan menggunakan database migration, setelah memodifikasi model, aplikasi kita membuat skrip migration-nya (flask db migrate), lalu kita review untuk memastikan pembuatan migration otomatis sudah melakukan hal yang sesuai dan mengaplikasikan migration tadi ke database (flask db upgrade). Lalu kita tambahkan skrip migration ke source control dan meng-commit-nya.

Setelah sudah siap untuk merilis versi baru aplikasi ke server production, yang perlu kita lakukan selanjutnya adalah mengirim versi aplikasi yang baru (termasuk skrip migration-nya) lalu menjalankan perintah flask db upgrade. Alembic akan mendeteksi bahwa database di production belum menggunakan schema terbaru sehingga akan menjalankan skrip-skrip baru setelah rilis sebelumnya.

Relasi Database

Database relasional dapat menyimpan relasi antar data dengan sangat baik. Bayangkan kasus dimana seorang user menulis sebuah artikel blog. User tersebut akan memiliki data di tabel users dan artikel akan disimpan di tabel posts. Cara paling efektif untuk menyimpan data siapa yang menulis artikel tertentu ialah dengan menghubungkan dua tabel tersebut.

Setelah hubungan antara seorang user dan sebuah artikel terhubung, database dapat menjawab permintaan data yang diperlukan. Misalnya ada sebuah artikel dimana kita ingin mengetahui siapa user yang menulisnya, atau kita memiliki data seorang user dan ingin mencari semua artikel yang ditulisnya. Flask-SQLAlchemy dapat membantu kueri-kueri seperti ini.

Mari kita perbarui dataabse untuk menyimpan artikel (blog posts) untuk melihat bagaimana sebuah relasi terjadi. Berikut ini schema untuk tabel posts yang baru:

posts table

Tabel posts akan memiliki field id, body dan sebuah timestamp. Sebagai tambahan dari ketiga field tersebut, ada satu field tambahan bernama user_id yang isinya adalah salah satu id di tabel users. Field user_id disebut dengan foreign key. Hubungan seperti ini disebut dengan one-to-many karena "satu" (one) user dapat membuat "banyak" (many) posts atau artikel.

Kelas app/models.py yang sudah dimodifikasi terlihat sebagai berikut:

from datetime import datetime
from app import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    posts = db.relationship('Post', backref='author', lazy='dynamic')

    def __repr__(self):
        return '<User {}>'.format(self.username)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(140))
    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return '<Post {}>'.format(self.body)

Kelas Post akan merepresentasikan artikel-artikel (blog posts) yang ditulis oleh user. Field timestamp akan di-index agar pengambilan data berdasarkan urutan menjadi lebih efisien. Kita juga menambahkan argumen default dan mengirimkan fungsi datetime.utcnow. Apa yang kita inginkan ialah mengambil waktu saat ini di zona UTC. Cara ini memastikan bahwa data waktu (timestamps) akan selalu sama dimanapun user berada. Data waktu yang disimpan dapat diubah menjadi waktu lokal saat ditampilkan.

Field user_id diinisialisasi sebagai foreign key ke user.id, ini artinya kita akan memanggil nilai id dari tabel users. Kelas User memiliki field posts dan diisi dengan memanggil fungsi db.relationship. Dalam sebuah relasi one-to-many, db.relationship biasanya dianggap sebagai bagian "one", dan dipakai untuk mengambil akses bagian yang "many". Contohnya, jika kita memiliki user yang disimpan dalam objek u, maka pemanggilan u.posts akan meminta seluruh artikel yang ditulis oleh user. Argumen backref mendefinisikan nama field yang akan diberikan objek-objek bagian "many" atau bagian "one" yang akan memiliki banyak data artikel. Dengan begitu, saat memanggil post.author kita akan mendapatkan user yang menulis artikel tersebut. Argumen lazy mendefiniskan bagaimana kueri database untuk relasi dikirim, kita akan membahasnya nanti. Jangan risau jika penjelasan tadi belum begitu jelas, kita akan melihat contoh penerapannya diakhir artikel.

Karena kita membuat perubahan di model aplikasi, sebuah database migration perlu di buat:

(venv) $ flask db migrate -m "posts table"
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'post'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_post_timestamp' on '['timestamp']'
  Generating /home/miguel/microblog/migrations/versions/780739b227a7_posts_table.py ... done

Lalu migration tadi harus diaplikasikan:

(venv) $ flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade e517276bb1c2 -> 780739b227a7, posts table

Jika menggunakan source control untuk menyimpan proyek ini, jangan lupa untuk menambahkan skrip ini kedalamnya.

Play Time

Penulis sudah menyusahkan pembaca dalam proses panjang pembuatan database, namun penulis belum menunjukkan bagaimana kode-kode kita tadi bekerja. Sekarang mari kita bermain dengan database menggunakan interpreter Python untuk lebih mengenalnya. Maka, sekarang buka Python dengan memanggil perintah python. Pastikan virtual environment sudah aktif sebelum memanggilnya.

Setelah memasuki prompt Python, impor database instance dan model-modelnya:

>>> from app import db
>>> from app.models import User, Post

Mulai dengan membuat sebuah user:

>>> u = User(username='john', email='john@example.com')
>>> db.session.add(u)
>>> db.session.commit()

Perubahan ke database dilakukan dalam konteks session yang dapat diliat di db.session. Perubahan-perubahan yang dilakukan diakumulasi disebuah session dan setelah semua perubahan didaftarkan ke sana kita bisa mamanggil satu perintah db.session.commit() yang akan mengaplikasikan perubahan-perubahan tersebut. Jika saat bekerja dengan session terjadi error, db.session.rollback() akan dipanggil secara otomatis dan membatalkan semua perubahan di session tersebut. Yang perlu diingat adalah perubahan-perubahan hanya akan ditulis ke database hanya jika db.session.commit() di panggil.

Mari kita tambahkan user lain:

>>> u = User(username='susan', email='susan@example.com')
>>> db.session.add(u)
>>> db.session.commit()

Sistem dataabse dapat menjawab kueri yang mengembalikan semua user:

>>> users = User.query.all()
>>> users
[<User john>, <User susan>]
>>> for u in users:
...     print(u.id, u.username)
...
1 john
2 susan

Semua model memiliki atribut query yang menjadi gerbang masuk ke kueri database lainnya. Kueri yang paling sederhana ialah mengambil seluruh elemen dari keals tersebut melalui method all(). Perhatikan field id yang secara otomatis diatur menjadi 1 dan 2 saat user tersebut ditambahkan.

Berikut ini cara lain melakukan kueri. Jika kita sudah mengetahui nilai id salah satu user, kita bisa mengambil objek user tersebut dengan perintah:

>>> u = User.query.get(1)
>>> u
<User john>

Sekarang tambahkan artikel baru:

>>> u = User.query.get(1)
>>> p = Post(body='my first post!', author=u)
>>> db.session.add(p)
>>> db.session.commit()

Penulis tidak memasukkan nilai untuk timestamp karena kita sudah mengaturnya secara otomatis di model. Lalu bagaimana dengan field user_id? Perhatikan lagi bagain db.relationship yang ada di dalam kelas User dimana menambahkan sebuah atribut posts ke user, dan author ke posts. Penulis menambahkan seorang field virtual author (penulis) dan memasukkan objek user ke dalamnya langsung tanpa perlu menggunakan ID. SQLAlchemy sudah mengetahui apa yang harus dilakukan.

Untuk menyelesaikan bagian ini, mari kita lihat beberapa kueri lain:

>>> # get all posts written by a user
>>> u = User.query.get(1)
>>> u
<User john>
>>> posts = u.posts.all()
>>> posts
[<Post my first post!>]

>>> # same, but with a user that has no posts
>>> u = User.query.get(2)
>>> u
<User susan>
>>> u.posts.all()
[]

>>> # print post author and body for all posts 
>>> posts = Post.query.all()
>>> for p in posts:
...     print(p.id, p.author.username, p.body)
...
1 john my first post!

# get all users in reverse alphabetical order
>>> User.query.order_by(User.username.desc()).all()
[<User susan>, <User john>]

Dokumentasi Flask-SQLAlchemy akan menjadi tempat terbaik untuk melihat semua opsi kuery database.

Mari kita harus seluruh data yang kita buat di atas sehingga database menjadi bersih dan siap digunakan di bab berikutnya:

>>> users = User.query.all()
>>> for u in users:
...     db.session.delete(u)
...
>>> posts = Post.query.all()
>>> for p in posts:
...     db.session.delete(p)
...
>>> db.session.commit()

Shell Context

Masih ingat apa yang kita lakukan setelah memulai interpreter Python di bagian sebelumnya? Hal pertama yang kita lakukan adalah mengimpor modul dan keals yang diperlukan:

>>> from app import db
>>> from app.models import User, Post

Saat membuat aplikasi Flask, kita mungkin perlu menguji beberapa hal melalui shell Python sehingga mengulang-ulang impor di atas mungkin dapat membosankan. Perintah flask shell adalah opsi lain dari perintah flask yang akan memulai interpreter Python dan mengimpor seluruh kelas yang berhubungan dengan aplikasi Flask kita. Perhatikan contoh di bawah:

(venv) $ python
>>> app
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'app' is not defined
>>>

(venv) $ flask shell
>>> app
<Flask 'app'>

Saat memulai sesi interpreter biasa, app tidak dikenal karena belum diimpor, tapi saat menggunakan flask shell, app langsung dikenal karena sudah di impor secara otomatis. Kita juga bisa mengimpor data lain yang dibutuhkan melalui shell context.

Berikut ini fungsi yang ada di file microblog.py untuk membuat interpreter dari flask shell mengimpor db, User, dan Post secara otomatis:

from app import app, db
from app.models import User, Post

@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User, 'Post': Post}

Decorator app.shell_context_processor mendaftarkan fungsi dibawahnya sebagai shell context function. Perintah flask shell selanjutnya akan mengimpor data yang di-return dan memberikan sebuah dictionary. Bagian yang ada di sebelah kiri tanda ":" adalah key, yang akan dipakai sebagai nama variabel saat diimpor.

Sekarang, saat memulai flask shell kita bisa langsung bekerja dengan database tanpa perlu mengimpor apapun:

(venv) $ flask shell
>>> db
<SQLAlchemy engine=sqlite:////Users/migu7781/Documents/dev/flask/microblog2/app.db>
>>> User
<class 'app.models.User'>
>>> Post
<class 'app.models.Post'>