Database Operations (Veritabanı işlemleri)
- Flask aslen veritabanlarını desteklemez. Bunun pozitif yönü; Flask'i, kuracağınız üçüncü parti yazılımla istediğiniz database yaklaşımı veya tipi ile kullanabilirsiniz. İlişkisel(Relational) veya ilişkisel olmayan(non-Relational), SQL veya NoSQL database sistemleri için yazılmış extension'ları Flask'a entegre edip kullanabilirsiniz.
- Burada biz ORM (Object Relational Mapping) kullanacağız. Bunun için uygun extension da Flask-SQLAlchemydir.
- ORM sayesinde Python'da tanımladığımız modeller veritabanında tablolara karşılık gelecek.
pip install Flask-SQLAlchemydiyerek ihtiyacımız olan extension'ı kuruyoruz.- Biz SQLite veritabanını kullanacağız. SQLite hızlı, hafif, server kurulumu gerektirmeyen bir veritabanıdır.
- Ayrıca
pip install flask-migratediyerek Flask-Migrate extension'ını kuruyoruz. Bu extension bize Python tarafında modelde değişiklik yaptığımızda, mevcut veritabanını bozmadan, silmeden güncelleme imkanı verecek. Burada tablolar arası ilişkinin güncellenmesi, alanların eklenmesi-silinmesi gibi işlemlerin gerçekleşmesinden söz ediyoruz.
config.pydosyamız üzerinde bir kaç işlem yapacağız.
xxxxxxxxxximport osbasedir = os.path.abspath(os.path.dirname(__file__))class Config(object): SECRET_KEY = os.environ.get('SECRET_KEY') or 'cok-gizli-anahtar' SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'blog.db') SQLALCHEMY_TRACK_MODIFICATIONS = False- Varsayılan olarak
DATABASE_URLtanımlı ise onun değerini alacak, tanımlı değilse projemizin ana dizininde duracak şekildeblog.db'ye kaydolacak şekilde yerini ayarladık. SQLALCHEMY_TRACK_MODIFICATIONS, database'de her değişiklik için bir sinyal belirten bu değeriFalseyaparak, iptal etmiş oluyoruz.myapp/__init__.pydosyasında bir kaç değişiklik yapalım.
xxxxxxxxxx# externalfrom flask import Flaskfrom flask_sqlalchemy import SQLAlchemyfrom flask_migrate import Migrate#internalfrom config import Configapp = Flask(__name__)app.config.from_object(Config)db = SQLAlchemy(app)migrate = Migrate(app, db)from myapp import routes, modelsdbdeğişkeni SQLAlchemy sınıfından oluşturulan nesne referansıdır.Migrate()sınıfı app ve db değişkenlerini alarak, migration için bir nevi bağlantı yapıyor.
Şimdi
myapp/models.pydosyamız ileUsermodelimizi belirleyelim.
xxxxxxxxxxfrom myapp import dbclass 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)Usersınıfımızdb.Model'i extend ediyor.db.Column()parametre olarak alan tipini alır. Integer, String ..gibi. Extra parametreler indexleme ve unique olarak belirlenmesi için verilebilir.idalanımız integer ve primary_key olacak.- username ve email
String()sınıfı yardımı ile veritabanında verilen uzunluk kadar yerini alacak. __repr__()methodu, debugging için ve objeleri nasıl yazacağını göstermek için kullanıyoruz.- Daha önce de söylediğimiz gibi modellerdeki değişimden sonra veritabanındaki tabloların değişmesi, silinmesi vb. durumlar için flask migrate ile database tekrar baştan oluşturulmadan, istenilen değişiklikler gerçekleştirilebiliyor.
Terminal'den:
xxxxxxxxxx(env) kayace@kayace-K53SV ~/flask/blog $ export FLASK_APP=main.py(env) kayace@kayace-K53SV ~/flask/blog $ flask db init Creating directory /home/kayace/flask/blog/migrations ... done Creating directory /home/kayace/flask/blog/migrations/versions ... done Generating /home/kayace/flask/blog/migrations/script.py.mako ... done Generating /home/kayace/flask/blog/migrations/env.py ... done Generating /home/kayace/flask/blog/migrations/README ... done Generating /home/kayace/flask/blog/migrations/alembic.ini ... done Please edit configuration/connection/logging settings in '/home/kayace/flask/blog/migrations/alembic.ini' before proceeding.- Yukarıdaki komutları verdikten sonra projemize migrations adında bir klasör eklenecek.
xxxxxxxxxx(env) kayace@kayace-K53SV ~/flask/blog $ flask db migrate -m "User table added"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/kayace/flask/blog/migrations/versions/79a72da45c14_user_table_added.py ... doneÜstteki komut ile de tanımladığımız
Usermodelini database'e tablo olarak yerleştirmek için hazırlıyoruz. Yani flask migrate bizim için script üretiyor.
- Modelimizi database'e yazmak için
flask db upgradekomutunu veriyoruz.
xxxxxxxxxxenv) kayace@kayace-K53SV ~/flask/blog $ flask db upgradeINFO [alembic.runtime.migration] Context impl SQLiteImpl.INFO [alembic.runtime.migration] Will assume non-transactional DDL.INFO [alembic.runtime.migration] Running upgrade -> 79a72da45c14, User table added- Proje dizinimize
blog.dbdosyası da eklendi. Ancak MySQL veya PostgreSQL gibi veri tabanları ile çalışıyorsanız serverda veri tabanını sizin oluşturmanız gerekiyor. - Flask-SQLAlchemy Tablo isimlerinde snake_case yapısını kullanıyor. Örneğin;
User, veritabanındauserolarak,AddressPhoneiseaddress_phoneolarak duruyor. - Eğer elle isim vermek isiyorsanız,
__tablename__kullanabilirsiniz. Örnek kullanım:__tablename__ = 'tablo_adi' - Eğer flask migrate ile üretilen scripti silmek/geri almak istiyorsanız
flask db downgradediyebilirsiniz.
Database relationships (Veritabanı ilişkileri)

- Projemiz için 1(one) kullanıcı(user) 1'den fazla(many) yayın(posts) yapacak şekilde One-To-Many ilişkisini yapacağız.
myapp/models.pymodülümüzü düzenleyelim:
xxxxxxxxxxfrom datetime import datetimefrom myapp import dbclass 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)) # Relation 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)Usermodeline ilişkiyi eklememiz gerekiyor. db.relationship( ) ilk parametresi ilişki kurulacak sınıfın adı.Postmodelindeuser_idreferansınauser.idforeign key olarak verildi veUsermodeline bağlandı.Postmodelindedefault=datetime.utcnowparametresi varsayılan olarak verildi. Ayrıcautcnow()yerineutcnowatandı. Yani sonuç yerine referans atandı. UTC ile kullanıcının bulunduğu bölgeye göre tarih, zaman bilgisini alıyoruz. Böylece farklı bölgelerdeki kullanıcılar için doğru zaman bilgisini garantiye almış oluyoruz.db.relationship()ilk parametre olarak ilişki kurulacak sınıfın adını uppercase olarak alır.db.Foreignkey()ise veritabanındaki tablo adı ve ilişki kurulacak alan adını lowercase olarak alır. Birden fazla kelime için snake_case kullanılır.backref='author'; backref,manytarafındakiposts'lar için veritabanı ilişkisindekionetarafını temsil eder.posts.authordiyerek verilenpostiçin kullanıcısına erişebiliriz.lazyparametresi veritabanı ilişkisinin nasıl kurulacağını belirtiyor.
Şimdi terminalden migration işlemini yapalım.
xxxxxxxxxx(env) kayace@kayace-K53SV ~/flask/blog $ 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/kayace/flask/blog/migrations/versions/9d8ba8bdcb4e_posts_table.py ... done(env) kayace@kayace-K53SV ~/flask/blog $ flask db upgradeINFO [alembic.runtime.migration] Context impl SQLiteImpl.INFO [alembic.runtime.migration] Will assume non-transactional DDL.INFO [alembic.runtime.migration] Running upgrade 79a72da45c14 -> 9d8ba8bdcb4e, posts table(env) kayace@kayace-K53SV ~/flask/blog $ - Şimdi python shell'i açıp bir kaç database işlemi yapalım.
xxxxxxxxxx>>> from myapp import db>>> from myapp.models import User, Post>>> u = User(username="adnan", email="adnan@kaya.com")>>> db.session.add(u)>>> db.session.commit()>>> u<User adnan>- Database objemizi (
db) ve modellerimizi (User, Post) import ettik. - Yeni bir User objesi oluşturduk ve username ile email alanlarına atama yaptık.
- Değişiklikleri veritabanına uygulamak için
db.sessioncontextini kullanıyoruz. Yeni User nesnesininureferansınıdb.session.add(u)diyerek database'e yazılabilir hale getirdik. Son olarakdb.session.commit()diyerek değişiklikleri veritabanına uyguladık. db.session.rollback(), herhangi bir zamanda bir sessionda hata oluştuğunda, kaydedilmiş değişiklikleri iptal eder.- Bir kayıt daha ekleyelim veritabanımıza.
xxxxxxxxxx>>> u2 = User(username="mehmet", email="mehmet@bozan.com")>>> db.session.add(u2)>>> db.session.commit()>>> users = User.query.all()>>> users[<User adnan>, <User mehmet>]>>> for user in users:... print(user.id, user.username)... 1 adnan2 mehmet>>> u3 = User.query.get(2)>>> u3<User mehmet>- Her model
queryattribute'e sahiptir.dir(User.query)yazarsnız python shell'de iken, kullanabileceğiniz attribute listesini görebilirsiniz. - SQL'deki gibi
select * from tablo_adisorgusu yerine.all()methodu ile modele ait bütün kayıtlar geliyor. - Eğer
id'sini biliğiniz bir kayıt getirmek istiyorsanız..get(id)ile kaydı alabilirsiniz.
xxxxxxxxxx>>> u = User.query.get(1)>>> p = Post(body="Bu benim ilk yayınım", author=u)>>> db.session.add(u)>>> db.session.commit()>>> posts = Post.query.all()>>> posts[<Post Bu benim ilk yayınım>]>>> posts[0].author.username'adnan'>>> posts[0].author.email'adnan@kaya.com'>>> User.query.order_by(User.username.desc()).all()[<User mehmet>, <User adnan>] Usermodelinde tanımladığımızdb.releationship()users & posts ilişkisini sağlar. backref parametresine atadığımızauthoralanı da user ID'leri yerine kullanıldı. Buradaauthor, User sınıfının bir objesini temsil eder.- Kayıtlarımızı sıralı bir şekilde getirmek için order_by kullandık.
Kayıt Silmek (Deleting Record)
xxxxxxxxxx>>> posts = Post.query.all()>>> for p in posts:... db.session.delete(p)...>>> db.session.commit()- Kayıtları silmek için
db.session.delete(kayit_adi)kullanıyoruz.
Flask Shell
- Python shell yerine flask shell kullanmayı görelim.
xxxxxxxxxx(env) kayace@kayace-K53SV ~/flask/blog $ flask shellPython 3.5.2 (default, Nov 23 2017, 16:37:01) [GCC 5.4.0 20160609] on linuxApp: myappInstance: /home/kayace/flask/blog/instance>>> app<Flask 'myapp'>- Herhangi bir import yapmadan flask varsayılan olarak uygulama örneğini (application instance) dahil ediyor.
- Shell test yaparken çok kullanılan ortamlardan biridir.
blog/main.pymodülümüzü düzenleyelim.
xxxxxxxxxx# internalfrom myapp import app, dbfrom myapp.models import User, Post.shell_context_processordef make_shell_context(): return {'db':db, 'User':User, 'Post':Post}@app.shell_context_processormethodumuzu shell context fonksiyonu haline getirir.flask shellkomutu çalıştığı zaman tanımladığımız değerler otomatik olarak import edilmiş olur.
$ flask shellkomutunu terminalden çalıştıralım.
xxxxxxxxxx>>> User<class 'myapp.models.User'>>>> Post<class 'myapp.models.Post'>>>> db<SQLAlchemy engine=sqlite:////home/kayace/flask/blog/blog.db>@app.shell_context_processorekledikten sonra sürekli import etmemize gerek kalmadan istediğimiz modele, modüle erişebiliriz.
Yorumlar