Python Flask-SQLAlchemy ORM
ile Veritabanı İşlemleri
ORM Nedir ?
ORM, Object Relational Mapping anlamına gelmektedir.
İlişkisel veritabanı (RDBMS) ile nesneye yönelik programların (Object Oriented Programming) arasında bir tür köprü özelliği gören ve ilişkisel veritabanındaki bilgilerimizi yönetmek için, nesne modellerimizi kullandığımız bir tekniktir/metodtur.
SQLAlchemy
- Python için geliştirilmiş SQL kütüphanesi/paketi ve Object Relational Mapper(ORM) aracıdır.
Veritabanı Modeli
Modelde gördüğünüz varlıklar (entity) arasındaki ilişki aşağıdaki gibi olacaktır:
- Teacher & Student
Many To Many
- Teacher & Lecture
One To One
- Teacher & Book
One To Many
- Teacher & Student
models.py
adında bir python dosyası oluşturalım.Flask ve SQLAlchemy sınıflarından nesneleri oluşturup konfigürasyonu yapalım.
xxxxxxxxxx
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + 'veritabani.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Projede
SQLite
veritabanı kullanıyoruz. MySQL, PostgreSQL kullanacaksanız önce veritabanını (database schema) oluşturmalısınız.- Veritabanı konfigürasyonunda daha fazlası için -> Configuration — Flask-SQLAlchemy Documentation (2.3)
SQLite Veritabanı adına
veritabani.db
adını verdik.Many To Many
ilişkileri veritabanında kullanabilmek içinOne To Many
ilişkilerine dönüştürüyoruz. Bunun için bir yardımcı tabloya ihtiyacımız var.Modelde göreceğiniz
StudentTeacher
tablosu yardımcı tablodur.
xxxxxxxxxx
# Many-To-Many icin yardimci tablo
student_teacher = db.Table('StudentTeacher',
db.Column('student_id', db.Integer, db.ForeignKey('student.id')),
db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id'))
)
db.Table()
sınıfı ile model olmayan tablo oluşturabiliyoruz.İlk parametre
StudentTeacher
tablo adı.db.Column()
ile kolon oluşturuyoruz.İlk parametre
student_id
kolon adı.db.Integer
veri tipinin integer (tamsayı) olacağını belirtiyor.db.ForeignKey()
sınıfı ile Dış Anahtar(Foreign Key) oluşturuyoruz.student.id
; Student isimli tablonunid
'sini belirtiyor.
primary_key=True
diyerek birincil anahtarı belirtiyoruz.
Student
Modeli
xxxxxxxxxx
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
teachers = db.relationship('Teacher',
secondary=student_teacher,
lazy='dynamic',
backref=db.backref('students', lazy='dynamic'))
def __repr__(self):
return '<Student %r>' % self.name
Student(db.Model)
; db.Model sınıfını miras alıyor.id
; db.Column ile kolon oluşturuyoruz.name
;db.String(50)
, 50 karakterlik bir string alabileceğini belirtir.nullable=False
diyerek, name değerinin boş bırakılamayacağını belirtiyoruz.
teachers
class attribute(sınıf niteliği);db.relationship()
ile bir ilişkinin oluşturulacağını belirtiyoruz. db.relationship() ile veritabanında kolon(attribute) oluşturulmaz.db.relationship()
; ilk parametresi'Teacher'
ile hangi model class ile ilişki kuracağımızı belirtiyoruz.secondary=student_teacher
diyerek yardımcı tablomuzu da ilişkiye dahil ediyoruz(Many To Many
için gerekli!).lazy
parametresi verinin database'den nasıl yükleneceğini belirtir.lazy='dynamic'
dediğimizde database query'yi (veri tabanı sorgusunu) parça parça yapabiliriz. Filtreleme(filter_by), sıralama(order_by) yapabiliriz.lazy='select'
ile tek seferde bütün datayı veritabanından standart select gibi almak için kullanılır. (select, True olarak da belirtilebilir.)lazy='joined'
ile veriyi tek seferdeJOIN
olarak almak için kullanılır. select ile dynamic kombinasyonu gibidir. (joined, False olarak da belirtilebilir.)lazy='subquery'
, joined gibidir.
backref
ile ilişki kurduğumuz model class'a bir tane property(özellik) ekliyoruz. Böylece ilişki kurduğumuzTeacher
sınıfından db.backref('students') içindeki'students'
diyerek öğretmenin öğrencilerini görebiliriz.
__repr__(self)
özel bir methoddur. Model class'tan nesne oluşturulduğunda sınıf adını ve methodda belirttiğimizself.name
in değerini ekrana basar. Aşağıdaki gibi.
xxxxxxxxxx
>>> Student(name='Adnan')
<Student 'Adnan'>
Teacher
Modeli
xxxxxxxxxx
class Teacher(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
lecture_id = db.Column(db.Integer, db.ForeignKey('lecture.id'))
lecture = db.relationship('Lecture',
back_populates='teacher', uselist=False)
books = db.relationship('Book', backref='author', lazy='dynamic')
def __repr__(self):
return '<Teacher %r>' % self.name
lecture_id
; Lecture model sınıfının id'sini tutacak sınıf niteliği.db.relationship(..., uselist=False, lazy=False)
;uselist=False
: One to One ilişkide kullanmak için belirtildi.lazy=False
, lazy='joined' demektir.back_populates
; backref gibidir.
backref
parametresinin vedb.backref()
methodunun kullanımına dikkat ediniz.
Lecture
ve Book
Modeli
xxxxxxxxxx
class Lecture(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
teacher = db.relationship('Teacher',
back_populates = 'lecture', uselist=False)
def __repr__(self):
return '<Lecture %r>' % self.name
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
teacher_id = db.Column(db.Integer, db.ForeignKey('teacher.id'))
def __repr__(self):
return '<Book %r>' % self.name
Shell ortamında çalışırken import ederken kolaylık olması için;
xxxxxxxxxx
shell_context_processor .
def make_shell_context():
return {
'db':db,
'Student':Student,
'Teacher':Teacher,
'Lecture':Lecture,
'Book':Book
}
$ export FLASK_APP=models.py
dedikten sonra$ flask shell
diyerek shell ortamında çalışabilirsiniz.
Shell ortamında işlemler
xxxxxxxxxx
>>> # Model sınıflar ve db referansını import ediyoruz.
>>> from models import *
>>> # Model sınıflara(class) göre veritabanı tablolarını oluşturur.
>>> db.create_all()
>>> # Veritabanı işlemleri için data ekliyoruz.
>>> student1 = Student(name='Adnan')
>>> student2 = Student(name='Hamit')
>>> student3 = Student(name='Mehmet')
>>> teacher1 = Teacher(name='Şadi Evren Şeker')
>>> teacher2 = Teacher(name='Akın Kaldıroğlu')
>>> book1 = Book(name='Weka and Data Mining')
>>> book2 = Book(name='Digital Firm')
>>> book3 = Book(name='Java and OOP')
>>> book4 = Book(name='Design Patterns and Clean Code')
>>> lecture1 = Lecture(name='Python and Data Structures')
>>> lecture2 = Lecture(name='Java ile Nesne Merkezli Programlama')
>>> lecture3 = Lecture(name='Artificial Neural Networks')
>>> # Veritabanına gönderilecek nesne referanslarını session'a ekliyoruz.
>>> db.session.add(teacher1)
>>> db.session.add(teacher2)
>>> db.session.add_all([student1, student2, student3, book1, book2, book3, book4, lecture1, lecture2, lecture3])
>>> # Session'a eklenen verileri veritabanına yazıyoruz.
>>> db.session.commit()
>>>
- Bu işlemlerden sonra veritabanı tabloları:
xxxxxxxxxx
>>> student1.teachers
<sqlalchemy.orm.dynamic.AppenderBaseQuery object at 0x7fb5aef66c18>
>>> student1.teachers.all()
[]
>>> # append() methoduna verdiğimiz nesne referansı ile ilişki bağlantısını yapmış oluyoruz. remove() ile de ilişkiyi koparabilirsiniz.
>>> student1.teachers.append(teacher1)
>>> student1.teachers.append(teacher2)
>>> student1.teachers.all()
[<Teacher 'Şadi Evren Şeker'>, <Teacher 'Akın Kaldıroğlu'>]
>>> db.session.commit()
>>> teacher1.students.all()
[<Student 'Adnan'>]
>>> teacher1.students.append(student2)
>>> teacher1.students.append(student3)
>>> teacher1.students.all()
[<Student 'Adnan'>, <Student 'Hamit'>, <Student 'Mehmet'>]
>>> db.session.commit()
xxxxxxxxxx
>>> teacher1.books.all()
[]
>>> book1
<Book 'Weka and Data Mining'>
>>> book2
<Book 'Digital Firm'>
>>> teacher1.books.append(book1)
>>> teacher1.books.append(book2)
>>> teacher1.books.append(book4)
>>> teacher1.books.all()
[<Book 'Weka and Data Mining'>, <Book 'Digital Firm'>, <Book 'Design Patterns and Clean Code'>]
>>> teacher2.books.all()
[]
>>> teacher2.books.append(book3)
>>> teacher2.books.all()
[<Book 'Java and OOP'>]
>>> teacher2.books.append(book4)
>>> teacher2.books.all()
[<Book 'Java and OOP'>, <Book 'Design Patterns and Clean Code'>]
>>> teacher1.books.all()
[<Book 'Weka and Data Mining'>, <Book 'Digital Firm'>]
>>> db.session.commit()
>>>
- Design Patterns and Clean Code kitabını Şadi Evren Şeker'e atayalım.
xxxxxxxxxx
>>>
>>> teacher1.books.all()
[<Book 'Weka and Data Mining'>, <Book 'Digital Firm'>]
>>> teacher1.books.append(book4)
>>> teacher1.books.all()
[<Book 'Weka and Data Mining'>, <Book 'Digital Firm'>, <Book 'Design Patterns and Clean Code'>]
>>> db.session.commit()
>>>
xxxxxxxxxx
>>> teacher1.lecture
>>> lecture1
<Lecture 'Python and Data Structures'>
>>> lecture2
<Lecture 'Java ile Nesne Merkezli Programlama'>
>>> lecture3
<Lecture 'Artificial Neural Networks'>
>>> lecture1.teacher
>>> lecture1.teacher = teacher1
>>> teacher1.lecture
<Lecture 'Python and Data Structures'>
>>> lecture2.teacher = teacher1
>>> teacher1.lecture
<Lecture 'Java ile Nesne Merkezli Programlama'>
>>> db.session.commit()
>>>
xxxxxxxxxx
>>> teacher2.lecture = lecture2
>>> teacher1.lecture
<Lecture 'Java ile Nesne Merkezli Programlama'>
>>> teacher2.lecture
<Lecture 'Java ile Nesne Merkezli Programlama'>
>>> db.session.commit()
- Dersi en son kime atadıysak onda kalır.
One To One
gereği!
backref
'i daha fazla açıklarsak.
backref
kullandığınızda, 2 tablo arası ilişkide, 2. tablodadb.relationship
yapmanız gerekmiyor.Student
sınıfında ufak bir değişiklik yapalım.
xxxxxxxxxx
class Student(db.Model):
#...
teachers = db.relationship('Teacher',
secondary=student_teacher,
lazy='dynamic',
backref=db.backref('ogrenciler', lazy='dynamic'))
#...
students
yerineogrenciler
yaptık sadece! Aşağıdaki kodlara dikkat edelim.
xxxxxxxxxx
>>> student1.teachers.append(teacher1)
>>> student2.teachers.append(teacher1)
>>> student3.teachers.append(teacher1)
>>> teacher1.ogrenciler.all()
[<Student 'Adnan'>, <Student 'Hamit'>, <Student 'Mehmet'>]
backref
sayesindeteacher1.ogrenciler
diyerek veya aşağıdaki gibibook1.author
diyerek ilişkili tablonun verisine erişebiliyoruz.
xxxxxxxxxx
>>> book1.author
<Teacher 'Şadi Evren Şeker'>
>>> book2.author
<Teacher 'Şadi Evren Şeker'>
>>> book4.author
<Teacher 'Şadi Evren Şeker'>
>>> book3.author
<Teacher 'Akın Kaldıroğlu'>
>>>
- Kaynak: Basic Relationship Patterns — SQLAlchemy 1.2 Documentation
- Kodların Tamamı:
xxxxxxxxxx
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + 'veritabani.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# Many-To-Many icin yardimci tablo
student_teacher = db.Table('StudentTeacher',
db.Column('student_id', db.Integer, db.ForeignKey('student.id'), primary_key=True),
db.Column('teacher_id', db.Integer, db.ForeignKey('teacher.id'), primary_key=True)
)
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
teachers = db.relationship('Teacher',
secondary=student_teacher,
lazy='dynamic',
backref=db.backref('ogrenciler', lazy='dynamic'))
def __repr__(self):
return '<Student %r>' % self.name
class Teacher(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
lecture_id = db.Column(db.Integer, db.ForeignKey('lecture.id'))
lecture = db.relationship('Lecture',
back_populates='teacher', uselist=False)
books = db.relationship('Book', backref='author', lazy='dynamic')
def __repr__(self):
return '<Teacher %r>' % self.name
class Lecture(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
# back_populates='lecture', uselist=False also can be used instead of back_populates
teacher = db.relationship('Teacher',
back_populates = 'lecture', uselist=False)
# One To One relationship can be converted to only one table
def __repr__(self):
return '<Lecture %r>' % self.name
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
teacher_id = db.Column(db.Integer, db.ForeignKey('teacher.id'))
def __repr__(self):
return '<Book %r>' % self.name
shell_context_processor .
def make_shell_context():
return {
'db':db,
'Student':Student,
'Teacher':Teacher,
'Lecture':Lecture,
'Book':Book
}
Yorumlar