{"id":40948,"date":"2026-03-02T12:41:00","date_gmt":"2026-03-02T09:41:00","guid":{"rendered":"https:\/\/fatihsoysal.com\/blog\/?p=40948"},"modified":"2026-03-02T12:41:00","modified_gmt":"2026-03-02T09:41:00","slug":"flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma","status":"publish","type":"post","link":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/","title":{"rendered":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma"},"content":{"rendered":"<p><body><\/p>\n<h2>Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma<\/h2>\n<p>Flask-SQLAlchemy, Flask uygulamalar\u0131 i\u00e7in SQLAlchemy ORM&#8217;ini entegre eden g\u00fc\u00e7l\u00fc bir uzant\u0131d\u0131r. Modern web uygulamalar\u0131n\u0131n karma\u015f\u0131k veri yap\u0131lar\u0131n\u0131 y\u00f6netmek i\u00e7in veritaban\u0131 ili\u015fkileri kritik \u00f6neme sahiptir. Bu ili\u015fkiler aras\u0131nda en esnek ve genellikle en yanl\u0131\u015f anla\u015f\u0131lanlardan biri \u00e7oka-\u00e7ok (many-to-many) ili\u015fkileridir. Bu makalede, \u00e7oka-\u00e7ok ili\u015fkilerinin ne oldu\u011funu, neden gerekli olduklar\u0131n\u0131 ve Flask-SQLAlchemy kullanarak bu ili\u015fkileri nas\u0131l etkili bir \u015fekilde tan\u0131mlayaca\u011f\u0131n\u0131z\u0131, y\u00f6netece\u011finizi ve optimize edece\u011finizi ayr\u0131nt\u0131l\u0131 olarak inceleyece\u011fiz. Amac\u0131m\u0131z, hem teorik bilgiyi peki\u015ftirmek hem de pratik kod \u00f6rnekleriyle Flask-SQLAlchemy&#8217;nin bu alandaki g\u00fcc\u00fcn\u00fc g\u00f6stermektir.<\/p>\n<h2>Veritaban\u0131 \u0130li\u015fkilerine Genel Bak\u0131\u015f<\/h2>\n<p>Veritabanlar\u0131, ger\u00e7ek d\u00fcnyadaki varl\u0131klar\u0131 ve bu varl\u0131klar aras\u0131ndaki ba\u011flant\u0131lar\u0131 modellemek i\u00e7in tasarlanm\u0131\u015ft\u0131r. Bu ba\u011flant\u0131lar, ili\u015fkisel veritabanlar\u0131n\u0131n temelini olu\u015fturur ve genellikle \u00fc\u00e7 ana t\u00fcrde s\u0131n\u0131fland\u0131r\u0131l\u0131r: bire-bir, bire-\u00e7ok (veya \u00e7oka-bir) ve \u00e7oka-\u00e7ok.<\/p>\n<h3>\u0130li\u015fki \u00c7e\u015fitleri ve \u00c7oka-\u00c7ok \u0130li\u015fkisinin \u00d6nemi<\/h3>\n<p>*   <strong>Bire-Bir (One-to-One):<\/strong> Bir tablodaki her kayd\u0131n, di\u011fer tablodaki yaln\u0131zca bir kayda kar\u015f\u0131l\u0131k geldi\u011fi ve bunun tersinin de ge\u00e7erli oldu\u011fu ili\u015fkidir. \u00d6rne\u011fin, bir <code>Kullan\u0131c\u0131<\/code> tablosu ile <code>Kullan\u0131c\u0131Profili<\/code> tablosu aras\u0131nda bire-bir ili\u015fki olabilir; her kullan\u0131c\u0131n\u0131n bir profili ve her profilin bir kullan\u0131c\u0131s\u0131 vard\u0131r. Genellikle, b\u00fcy\u00fck bir tablonun belirli s\u00fctunlar\u0131n\u0131 ay\u0131rarak performans\u0131 art\u0131rmak veya gizlili\u011fi y\u00f6netmek i\u00e7in kullan\u0131l\u0131r.<\/p>\n<p>*   <strong>Bire-\u00c7ok \/ \u00c7oka-Bir (One-to-Many \/ Many-to-One):<\/strong> En yayg\u0131n ili\u015fki t\u00fcr\u00fcd\u00fcr. Bir tablodaki bir kayd\u0131n di\u011fer tablodaki birden \u00e7ok kayda kar\u015f\u0131l\u0131k geldi\u011fi, ancak di\u011fer tablodaki her kayd\u0131n ilk tablodaki yaln\u0131zca bir kayda kar\u015f\u0131l\u0131k geldi\u011fi ili\u015fkidir. \u00d6rne\u011fin, bir <code>Yazar<\/code> bir\u00e7ok <code>Kitap<\/code> yazabilir, ancak bir <code>Kitap<\/code> genellikle tek bir <code>Yazar<\/code> taraf\u0131ndan yaz\u0131l\u0131r (bu basitle\u015ftirilmi\u015f bir \u00f6rnektir, \u00e7oka-\u00e7ok ili\u015fkisiyle kar\u0131\u015ft\u0131r\u0131lmamal\u0131d\u0131r). Bu ili\u015fki, genellikle &#8220;\u00e7ok&#8221; taraf\u0131ndaki tabloda, &#8220;bir&#8221; taraf\u0131ndaki tablonun birincil anahtar\u0131n\u0131 yabanc\u0131 anahtar (foreign key) olarak tutarak modellenir.<\/p>\n<p>*   <strong>\u00c7oka-\u00c7ok (Many-to-Many):<\/strong> Bir tablodaki bir kayd\u0131n di\u011fer tablodaki birden \u00e7ok kayda kar\u015f\u0131l\u0131k geldi\u011fi ve di\u011fer tablodaki bir kayd\u0131n da ilk tablodaki birden \u00e7ok kayda kar\u015f\u0131l\u0131k geldi\u011fi ili\u015fkidir. \u00d6rne\u011fin, bir <code>\u00d6\u011frenci<\/code> birden \u00e7ok <code>Ders<\/code> alabilir ve bir <code>Ders<\/code> de birden \u00e7ok <code>\u00d6\u011frenci<\/code> taraf\u0131ndan al\u0131nabilir. Bu ili\u015fki t\u00fcr\u00fc, do\u011frudan iki tablo aras\u0131nda bir yabanc\u0131 anahtar ile modellenemez. \u0130li\u015fkisel veritaban\u0131 teorisine g\u00f6re, \u00e7oka-\u00e7ok ili\u015fkilerini modellemek i\u00e7in \u00fc\u00e7\u00fcnc\u00fc bir tabloya, yani bir <strong>ara tabloya (association table)<\/strong> ihtiya\u00e7 duyulur. Bu ara tablo, iki ana tablonun birincil anahtarlar\u0131n\u0131 yabanc\u0131 anahtar olarak i\u00e7erir ve bu iki ana tablo aras\u0131ndaki ba\u011flant\u0131y\u0131 sa\u011flar. \u00c7oka-\u00e7ok ili\u015fkileri, ger\u00e7ek d\u00fcnya senaryolar\u0131nda olduk\u00e7a yayg\u0131n oldu\u011fundan ve karma\u015f\u0131k veri yap\u0131lar\u0131n\u0131 esnek bir \u015fekilde temsil etmeye olanak tan\u0131d\u0131\u011f\u0131ndan, veritaban\u0131 tasar\u0131m\u0131nda kritik bir role sahiptir.<\/p>\n<h2>\u00c7oka-\u00c7ok \u0130li\u015fkilerinin Temeli: Ara Tablo<\/h2>\n<p>\u00c7oka-\u00e7ok ili\u015fkilerini anlaman\u0131n anahtar\u0131, ara tablo veya di\u011fer ad\u0131yla birle\u015fim tablosu (join table) veya ba\u011flant\u0131 tablosu (linking table) kavram\u0131n\u0131 kavramakt\u0131r. Bu tablo, iki ana varl\u0131k aras\u0131ndaki do\u011frudan \u00e7oka-\u00e7ok ili\u015fkisini, iki ayr\u0131 bire-\u00e7ok ili\u015fkisine d\u00f6n\u00fc\u015ft\u00fcrerek \u00e7\u00f6zer.<\/p>\n<h3>Kavram ve Yap\u0131<\/h3>\n<p>Ara tablo, ili\u015fkilendirmek istedi\u011fimiz iki ana tablonun birincil anahtarlar\u0131n\u0131 yabanc\u0131 anahtar olarak i\u00e7eren \u00f6zel bir tablodur. Bu tablonun kendisinin genellikle bir birincil anahtar\u0131 olmaz, ancak iki yabanc\u0131 anahtar\u0131n birle\u015fimi genellikle bir bile\u015fik birincil anahtar (composite primary key) g\u00f6revi g\u00f6r\u00fcr ve her bir ili\u015fkinin benzersiz olmas\u0131n\u0131 sa\u011flar.<\/p>\n<p>\u00d6rne\u011fin, bir <code>Yazar<\/code> ve <code>Kitap<\/code> aras\u0131nda \u00e7oka-\u00e7ok ili\u015fkisi oldu\u011funu d\u00fc\u015f\u00fcnelim (bir kitap birden fazla yazar taraf\u0131ndan yaz\u0131labilir ve bir yazar birden fazla kitap yazabilir). Bu durumda, <code>yazar_kitap<\/code> ad\u0131nda bir ara tablo olu\u015ftururuz. Bu tablo a\u015fa\u011f\u0131daki s\u00fctunlara sahip olabilir:<\/p>\n<p>*   <code>yazar_id<\/code>: <code>Yazar<\/code> tablosunun birincil anahtar\u0131na referans veren bir yabanc\u0131 anahtar.<br \/>\n*   <code>kitap_id<\/code>: <code>Kitap<\/code> tablosunun birincil anahtar\u0131na referans veren bir yabanc\u0131 anahtar.<\/p>\n<p>Bu iki s\u00fctun, birlikte <code>yazar_kitap<\/code> tablosunun bile\u015fik birincil anahtar\u0131n\u0131 olu\u015fturur. Bu sayede, ayn\u0131 yazar\u0131n ayn\u0131 kitaba birden fazla kez atanmas\u0131 engellenir. Bu ara tablo, <code>Yazar<\/code> ile <code>yazar_kitap<\/code> aras\u0131nda bire-\u00e7ok, ve <code>Kitap<\/code> ile <code>yazar_kitap<\/code> aras\u0131nda da bire-\u00e7ok ili\u015fki kurar. B\u00f6ylece, orijinal \u00e7oka-\u00e7ok ili\u015fkisi iki bire-\u00e7ok ili\u015fkisine ayr\u0131\u015ft\u0131r\u0131lm\u0131\u015f olur.<\/p>\n<p>Ara tablolar sadece bu iki yabanc\u0131 anahtar\u0131 i\u00e7ermek zorunda de\u011fildir. \u0130li\u015fkinin kendisine ait ek nitelikler (payload attributes) de i\u00e7erebilirler. \u00d6rne\u011fin, bir <code>\u00d6\u011frenci<\/code> ve <code>Ders<\/code> ili\u015fkisinde, ara tablo <code>\u00f6\u011frenci_ders<\/code> ayn\u0131 zamanda \u00f6\u011frencinin dersten ald\u0131\u011f\u0131 <code>not<\/code> veya <code>kay\u0131t_tarihi<\/code> gibi bilgileri de tutabilir. Bu senaryo, Flask-SQLAlchemy&#8217;de &#8220;Association Object Pattern&#8221; olarak bilinen daha geli\u015fmi\u015f bir yakla\u015f\u0131mla ele al\u0131n\u0131r ve makalenin ilerleyen b\u00f6l\u00fcmlerinde ayr\u0131nt\u0131l\u0131 olarak ele al\u0131nacakt\u0131r.<\/p>\n<h2>Flask-SQLAlchemy ile \u00c7oka-\u00c7ok \u0130li\u015fkisi Tan\u0131mlama<\/h2>\n<p>Flask-SQLAlchemy, \u00e7oka-\u00e7ok ili\u015fkilerini tan\u0131mlamak i\u00e7in sezgisel ve g\u00fc\u00e7l\u00fc bir API sunar. \u0130lk olarak temel kurulumu ve modelleri ele alal\u0131m, ard\u0131ndan <code>db.relationship()<\/code> metodunun parametrelerini inceleyelim.<\/p>\n<h3>Temel Kurulum ve Modeller<\/h3>\n<p>\u00d6ncelikle bir Flask uygulamas\u0131 ve Flask-SQLAlchemy uzant\u0131s\u0131 kurulumunu yapal\u0131m. Daha sonra, bir <code>Yazar<\/code> (Author) ve <code>Kitap<\/code> (Book) modeli \u00fczerinden \u00e7oka-\u00e7ok ili\u015fkisini \u00f6rnekleyelim.<\/p>\n<pre><code class=\"language-python\">from flask import Flask\nfrom flask_sqlalchemy import SQLAlchemy\n\napp = Flask(__name__)\napp.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:\/\/\/many_to_many_example.db'\napp.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False\ndb = SQLAlchemy(app)\n\n<h2>Ara tabloyu tan\u0131mlama<\/h2>\n<h2>Bu, db.Model'den t\u00fcremeyen, sadece bir tablo nesnesidir.<\/h2>\n<h2>\u0130ki yabanc\u0131 anahtar ve bile\u015fik birincil anahtar i\u00e7erir.<\/h2>\nkitap_yazarlari = db.Table('kitap_yazarlari',\n    db.Column('kitap_id', db.Integer, db.ForeignKey('kitap.id'), primary_key=True),\n    db.Column('yazar_id', db.Integer, db.ForeignKey('yazar.id'), primary_key=True)\n)\n\nclass Yazar(db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    ad = db.Column(db.String(100), nullable=False)\n\n    # Bu yazar\u0131n yazd\u0131\u011f\u0131 kitaplar\n    # secondary parametresi, ara tabloyu i\u015faret eder.\n    # backref, Kitap modelinde 'yazarlar' ad\u0131nda otomatik bir ili\u015fki olu\u015fturur.\n    kitaplar = db.relationship('Kitap', secondary=kitap_yazarlari,\n                               backref=db.backref('yazarlar', lazy=True))\n\n    def __repr__(self):\n        return f'<Yazar {self.ad}>'\n\nclass Kitap(db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    baslik = db.Column(db.String(200), nullable=False)\n    yayin_yili = db.Column(db.Integer)\n\n    # Yazarlar ili\u015fkisi backref taraf\u0131ndan olu\u015fturuldu\u011fu i\u00e7in burada explicit olarak tan\u0131mlamaya gerek yok.\n    # Ancak tan\u0131mlansayd\u0131 \u015f\u00f6yle olurdu:\n    # yazarlar = db.relationship('Yazar', secondary=kitap_yazarlari,\n    #                            back_populates='kitaplar')\n\n    def __repr__(self):\n        return f'<Kitap {self.baslik}>'\n\n<h2>Veritaban\u0131n\u0131 olu\u015fturma<\/h2>\nwith app.app_context():\n    db.create_all()<\/code><\/pre>\n<p>Yukar\u0131daki kodda dikkat edilmesi gerekenler:<\/p>\n<p>1.  <strong><code>db.Table()<\/code>:<\/strong> Bu, Flask-SQLAlchemy&#8217;nin ara tabloyu tan\u0131mlamak i\u00e7in kulland\u0131\u011f\u0131 \u00f6zel bir yap\u0131d\u0131r. <code>db.Model<\/code>&#8216;den t\u00fcremeyen, sadece bir tablo nesnesi olarak var olan bir yap\u0131d\u0131r. \u0130lk arg\u00fcman tablonun ad\u0131d\u0131r (<code>'kitap_yazarlari'<\/code>). Di\u011fer arg\u00fcmanlar ise s\u00fctun tan\u0131mlamalar\u0131d\u0131r. Her iki s\u00fctun da ilgili ana tablolar\u0131n birincil anahtarlar\u0131na referans veren <code>db.ForeignKey<\/code>&#8216;ler olmal\u0131d\u0131r. Ayr\u0131ca, her iki s\u00fctun da <code>primary_key=True<\/code> olarak i\u015faretlenerek bile\u015fik birincil anahtar olu\u015fturulur.<br \/>\n2.  <strong><code>Yazar<\/code> ve <code>Kitap<\/code> Modelleri:<\/strong> Her iki model de standart <code>db.Model<\/code>&#8216;den t\u00fcretilmi\u015ftir ve kendi birincil anahtarlar\u0131na ve di\u011fer niteliklerine sahiptir.<\/p>\n<h3><code>db.relationship()<\/code> Kullan\u0131m\u0131<\/h3>\n<p>\u00c7oka-\u00e7ok ili\u015fkisini kurman\u0131n kalbi <code>db.relationship()<\/code> metodunda yatar. Bu metot, ORM&#8217;in ili\u015fkili nesneleri nas\u0131l y\u00fckleyece\u011fini ve y\u00f6netece\u011fini tan\u0131mlar.<\/p>\n<p><code>Yazar<\/code> modelindeki <code>kitaplar<\/code> ili\u015fkisini inceleyelim:<\/p>\n<pre><code class=\"language-python\">kitaplar = db.relationship('Kitap', secondary=kitap_yazarlari,\n                           backref=db.backref('yazarlar', lazy=True))<\/code><\/pre>\n<p>*   <strong><code>'Kitap'<\/code>:<\/strong> \u0130li\u015fkinin di\u011fer taraf\u0131ndaki modelin ad\u0131d\u0131r. Yani, bir <code>Yazar<\/code> nesnesi \u00fczerinden hangi t\u00fcrden nesnelere eri\u015fece\u011fimizi belirtir (bu durumda <code>Kitap<\/code> nesnelerine).<br \/>\n*   <strong><code>secondary=kitap_yazarlari<\/code>:<\/strong> Bu parametre, bu ili\u015fkinin bir \u00e7oka-\u00e7ok ili\u015fkisi oldu\u011funu ve <code>kitap_yazarlari<\/code> ara tablosunu kullanarak kuruldu\u011funu belirtir. Bu, <code>db.Table()<\/code> ile tan\u0131mlad\u0131\u011f\u0131m\u0131z tablo nesnesidir.<br \/>\n*   <strong><code>backref=db.backref('yazarlar', lazy=True)<\/code>:<\/strong> <code>backref<\/code> parametresi, ili\u015fkinin di\u011fer taraf\u0131nda (bu durumda <code>Kitap<\/code> modelinde) otomatik olarak bir ters ili\u015fki olu\u015fturur. Yani, <code>Kitap<\/code> modeli \u00fczerinde <code>yazarlar<\/code> ad\u0131nda bir \u00f6zellik olu\u015fturur. Bu \u00f6zellik, ilgili kitaba ait yazarlar\u0131 sorgulamak i\u00e7in kullan\u0131labilir. <code>lazy=True<\/code> ise bu ters ili\u015fkinin nas\u0131l y\u00fcklenece\u011fini belirtir. <code>lazy<\/code> parametresi, performans a\u00e7\u0131s\u0131ndan \u00f6nemlidir ve farkl\u0131 de\u011ferler alabilir:<br \/>\n    *   <strong><code>select<\/code> (varsay\u0131lan):<\/strong> \u0130li\u015fkili nesneler, eri\u015fildi\u011finde ayr\u0131 bir SELECT sorgusu ile y\u00fcklenir. \u00c7o\u011fu zaman iyi bir dengedir.<br \/>\n    *   <strong><code>joined<\/code>:<\/strong> Ana sorgu ile ayn\u0131 anda, JOIN kullanarak y\u00fcklenir. N+1 sorgu problemini \u00f6nlemek i\u00e7in iyidir ancak ana sorguyu daha karma\u015f\u0131k hale getirebilir.<br \/>\n    *   <strong><code>subquery<\/code>:<\/strong> \u0130li\u015fkili nesneler, ana sorgu ile birlikte bir alt sorgu kullan\u0131larak y\u00fcklenir. <code>joined<\/code>&#8216;a benzer ancak baz\u0131 durumlarda daha iyi performans g\u00f6sterebilir.<br \/>\n    *   <strong><code>dynamic<\/code>:<\/strong> \u0130li\u015fkili nesneler hemen y\u00fcklenmez, bunun yerine bir sorgu nesnesi d\u00f6nd\u00fcr\u00fcl\u00fcr. Bu, ili\u015fkili nesneler \u00fczerinde daha fazla filtreleme veya s\u0131ralama yapmak istedi\u011finizde kullan\u0131\u015fl\u0131d\u0131r.<br \/>\n    *   <strong><code>False<\/code> \/ <code>True<\/code>:<\/strong> <code>False<\/code> hemen y\u00fckler (eager loading), <code>True<\/code> ise <code>select<\/code> ile ayn\u0131d\u0131r.<\/p>\n<p><code>backref<\/code> yerine <code>back_populates<\/code> de kullan\u0131labilir. <code>back_populates<\/code>, her iki modelde de ili\u015fkinin a\u00e7\u0131k\u00e7a tan\u0131mlanmas\u0131n\u0131 gerektirir ve daha net bir kod okunabilirli\u011fi sa\u011flayabilir, ancak <code>backref<\/code> daha k\u0131sad\u0131r ve otomatik olarak ters ili\u015fkiyi olu\u015fturur. \u00c7oka-\u00e7ok ili\u015fkilerinde genellikle <code>backref<\/code> tercih edilir.<\/p>\n<h2>\u0130li\u015fkili Verilerle \u00c7al\u0131\u015fma: CRUD \u0130\u015flemleri<\/h2>\n<p>Flask-SQLAlchemy ile \u00e7oka-\u00e7ok ili\u015fkilerini tan\u0131mlad\u0131ktan sonra, s\u0131ra bu ili\u015fkiler \u00fczerinde temel CRUD (Create, Read, Update, Delete) i\u015flemlerini ger\u00e7ekle\u015ftirmeye gelir.<\/p>\n<h3>Veri Ekleme ve Sorgulama<\/h3>\n<p>Yeni yazarlar ve kitaplar olu\u015fturup bunlar\u0131 ili\u015fkilendirelim.<\/p>\n<pre><code class=\"language-python\">with app.app_context():\n    # Yeni yazarlar olu\u015fturma\n    yazar1 = Yazar(ad='George Orwell')\n    yazar2 = Yazar(ad='Aldous Huxley')\n    yazar3 = Yazar(ad='J.K. Rowling')\n\n    db.session.add_all([yazar1, yazar2, yazar3])\n    db.session.commit()\n\n    # Yeni kitaplar olu\u015fturma\n    kitap1 = Kitap(baslik='1984', yayin_yili=1949)\n    kitap2 = Kitap(baslik='Hayvan \u00c7iftli\u011fi', yayin_yili=1945)\n    kitap3 = Kitap(baslik='Cesur Yeni D\u00fcnya', yayin_yili=1932)\n    kitap4 = Kitap(baslik='Harry Potter ve Felsefe Ta\u015f\u0131', yayin_yili=1997)\n\n    db.session.add_all([kitap1, kitap2, kitap3, kitap4])\n    db.session.commit()\n\n    # Kitaplar\u0131 yazarlarla ili\u015fkilendirme\n    # Yazar1 (George Orwell) 1984 ve Hayvan \u00c7iftli\u011fi'ni yazd\u0131\n    yazar1.kitaplar.append(kitap1)\n    yazar1.kitaplar.append(kitap2)\n\n    # Yazar2 (Aldous Huxley) Cesur Yeni D\u00fcnya'y\u0131 yazd\u0131\n    yazar2.kitaplar.append(kitap3)\n\n    # Yazar3 (J.K. Rowling) Harry Potter'\u0131 yazd\u0131\n    yazar3.kitaplar.append(kitap4)\n\n    # Bir kitap birden fazla yazar taraf\u0131ndan yaz\u0131lm\u0131\u015fsa\n    # \u00d6rne\u011fin, kitap1'e (1984) ba\u015fka bir yazar ekleyelim (sadece \u00f6rnek ama\u00e7l\u0131)\n    # yazar2.kitaplar.append(kitap1) # Bu durumda kitap1 hem Orwell hem Huxley'e ait olurdu.\n\n    db.session.commit()\n\n    print(\"--- Veri Ekleme Ba\u015far\u0131l\u0131 ---\")\n\n    # Veri Sorgulama\n    print(\"\\n--- Kitaplar\u0131 ve Yazarlar\u0131n\u0131 Sorgulama ---\")\n    all_books = Kitap.query.all()\n    for book in all_books:\n        print(f\"Kitap: {book.baslik} ({book.yayin_yili})\")\n        print(f\"  Yazarlar\u0131:\")\n        for author in book.yazarlar: # backref ile olu\u015fturulan yazarlar \u00f6zelli\u011fi\n            print(f\"    - {author.ad}\")\n\n    print(\"\\n--- Yazarlar\u0131 ve Kitaplar\u0131n\u0131 Sorgulama ---\")\n    all_authors = Yazar.query.all()\n    for author in all_authors:\n        print(f\"Yazar: {author.ad}\")\n        print(f\"  Kitaplar\u0131:\")\n        for book in author.kitaplar:\n            print(f\"    - {book.baslik} ({book.yayin_yili})\")\n\n    # Belirli bir yazar\u0131n kitaplar\u0131n\u0131 sorgulama\n    orwell = Yazar.query.filter_by(ad='George Orwell').first()\n    if orwell:\n        print(f\"\\n{orwell.ad}'\u0131n kitaplar\u0131:\")\n        for book in orwell.kitaplar:\n            print(f\"  - {book.baslik}\")\n\n    # Belirli bir kitab\u0131n yazarlar\u0131n\u0131 sorgulama\n    kitap_1984 = Kitap.query.filter_by(baslik='1984').first()\n    if kitap_1984:\n        print(f\"\\n'{kitap_1984.baslik}' kitab\u0131n\u0131n yazarlar\u0131:\")\n        for author in kitap_1984.yazarlar:\n            print(f\"  - {author.ad}\")<\/code><\/pre>\n<p>Veri ekleme i\u015flemi olduk\u00e7a basittir: \u0130lgili nesneleri olu\u015fturduktan sonra, ili\u015fkinin bir taraf\u0131ndaki liste benzeri \u00f6zelli\u011fe (<code>yazar1.kitaplar<\/code>) di\u011fer taraf\u0131n nesnesini (<code>kitap1<\/code>) <code>append()<\/code> metodu ile eklersiniz. SQLAlchemy, <code>db.session.commit()<\/code> \u00e7a\u011fr\u0131ld\u0131\u011f\u0131nda ara tabloya gerekli giri\u015fleri otomatik olarak ekler.<\/p>\n<p>Sorgulama da ayn\u0131 \u015fekilde kolayd\u0131r. \u0130li\u015fki \u00f6zelli\u011fi (<code>yazar.kitaplar<\/code> veya <code>kitap.yazarlar<\/code>) \u00fczerinden do\u011frudan ilgili nesnelerin bir listesine eri\u015febilirsiniz.<\/p>\n<h3>Veri G\u00fcncelleme ve Silme<\/h3>\n<p>\u0130li\u015fkileri g\u00fcncellemek veya kald\u0131rmak da benzer \u015fekilde liste manip\u00fclasyonu ile yap\u0131l\u0131r.<\/p>\n<pre><code class=\"language-python\">with app.app_context():\n    # \u00d6rnek veri ekleme (yukar\u0131daki koddan sonra \u00e7al\u0131\u015ft\u0131rmak i\u00e7in)\n    # yazar1 = Yazar(ad='George Orwell')\n    # yazar2 = Yazar(ad='Aldous Huxley')\n    # kitap1 = Kitap(baslik='1984')\n    # kitap3 = Kitap(baslik='Cesur Yeni D\u00fcnya')\n    # db.session.add_all([yazar1, yazar2, kitap1, kitap3])\n    # yazar1.kitaplar.append(kitap1)\n    # yazar2.kitaplar.append(kitap3)\n    # db.session.commit()\n\n    # Mevcut bir yazar ve kitap alal\u0131m\n    orwell = Yazar.query.filter_by(ad='George Orwell').first()\n    brave_new_world = Kitap.query.filter_by(baslik='Cesur Yeni D\u00fcnya').first()\n    huxley = Yazar.query.filter_by(ad='Aldous Huxley').first()\n\n    if orwell and brave_new_world:\n        print(f\"\\n--- \u0130li\u015fki G\u00fcncelleme ---\")\n        print(f\"'{brave_new_world.baslik}' kitab\u0131n\u0131n mevcut yazarlar\u0131: {[a.ad for a in brave_new_world.yazarlar]}\")\n        \n        # Orwell'i Cesur Yeni D\u00fcnya'n\u0131n yazar\u0131 olarak ekleyelim (\u00f6rnek)\n        orwell.kitaplar.append(brave_new_world)\n        db.session.commit()\n        print(f\"Orwell, '{brave_new_world.baslik}' kitab\u0131na yazar olarak eklendi.\")\n        print(f\"'{brave_new_world.baslik}' kitab\u0131n\u0131n yeni yazarlar\u0131: {[a.ad for a in brave_new_world.yazarlar]}\")\n\n    if orwell and brave_new_world:\n        print(f\"\\n--- \u0130li\u015fki Silme ---\")\n        print(f\"'{brave_new_world.baslik}' kitab\u0131n\u0131n mevcut yazarlar\u0131: {[a.ad for a in brave_new_world.yazarlar]}\")\n\n        # Orwell'i Cesur Yeni D\u00fcnya'n\u0131n yazarlar\u0131ndan \u00e7\u0131karal\u0131m\n        orwell.kitaplar.remove(brave_new_world)\n        db.session.commit()\n        print(f\"Orwell, '{brave_new_world.baslik}' kitab\u0131ndan yazar olarak \u00e7\u0131kar\u0131ld\u0131.\")\n        print(f\"'{brave_new_world.baslik}' kitab\u0131n\u0131n yeni yazarlar\u0131: {[a.ad for a in brave_new_world.yazarlar]}\")\n\n    # Bir yazar\u0131n veya kitab\u0131n tamamen silinmesi\n    # Cascade opsiyonlar\u0131 olmadan, sadece ana kay\u0131t silinir, ara tablodaki referanslar kal\u0131r\n    # Ancak, ForeignKey'ler genellikle ON DELETE CASCADE ile tan\u0131mlan\u0131rsa, ilgili ara tablo kay\u0131tlar\u0131 da silinir.\n    # Flask-SQLAlchemy'de db.relationship'te cascade=\"all, delete-orphan\" kullan\u0131larak ORM seviyesinde de bu kontrol sa\u011flanabilir.\n\n    # \u00d6rne\u011fin, Aldous Huxley'i silelim. Onunla ili\u015fkili kitaplar kal\u0131r, sadece ara tablodaki ba\u011flant\u0131 silinir.\n    if huxley:\n        print(f\"\\n--- Yazar Silme ---\")\n        print(f\"Silinmeden \u00f6nce Aldous Huxley'in kitaplar\u0131: {[b.baslik for b in huxley.kitaplar]}\")\n        db.session.delete(huxley)\n        db.session.commit()\n        print(f\"Aldous Huxley silindi.\")\n        \n        # Kontrol edelim, Brave New World hala var m\u0131 ve yazarlar\u0131 bo\u015f mu?\n        brave_new_world_after_delete = Kitap.query.filter_by(baslik='Cesur Yeni D\u00fcnya').first()\n        if brave_new_world_after_delete:\n            print(f\"'{brave_new_world_after_delete.baslik}' kitab\u0131 hala mevcut.\")\n            print(f\"Yazarlar\u0131: {[a.ad for a in brave_new_world_after_delete.yazarlar]}\")<\/code><\/pre>\n<p>\u0130li\u015fkileri g\u00fcncellemek i\u00e7in <code>append()<\/code> veya <code>remove()<\/code> metotlar\u0131n\u0131 kullanabilirsiniz. Bir nesneyi bir ili\u015fkiden \u00e7\u0131karmak, ara tablodaki ilgili kayd\u0131 siler.<\/p>\n<p>Ana bir nesneyi (\u00f6rne\u011fin bir <code>Yazar<\/code> veya <code>Kitap<\/code>) sildi\u011finizde, varsay\u0131lan olarak sadece o ana nesne silinir. Ara tablodaki ilgili kay\u0131tlar, e\u011fer yabanc\u0131 anahtarlar\u0131n\u0131z <code>ON DELETE CASCADE<\/code> ile tan\u0131mlanmam\u0131\u015fsa, &#8220;yetim&#8221; kalabilir ve veri tutars\u0131zl\u0131\u011f\u0131na yol a\u00e7abilir. Bu nedenle, veritaban\u0131 \u015femas\u0131nda <code>ON DELETE CASCADE<\/code> k\u0131s\u0131tlamalar\u0131n\u0131 kullanmak veya Flask-SQLAlchemy&#8217;nin <code>db.relationship<\/code> tan\u0131m\u0131nda <code>cascade=\"all, delete-orphan\"<\/code> gibi se\u00e7enekleri kullanmak \u00f6nemlidir. Bu se\u00e7enekler, bir ana nesne silindi\u011finde ili\u015fkili ara tablo kay\u0131tlar\u0131n\u0131n da otomatik olarak silinmesini sa\u011flar.<\/p>\n<h3>Ara Tabloya Ek Nitelikler Ekleme (Association Object Pattern)<\/h3>\n<p>Baz\u0131 durumlarda, \u00e7oka-\u00e7ok ili\u015fkisinin kendisi hakk\u0131nda bilgi saklamam\u0131z gerekebilir. \u00d6rne\u011fin, bir <code>\u00d6\u011frenci<\/code> bir <code>Ders<\/code>e kaydoldu\u011funda, bu kaydolma olay\u0131n\u0131n bir <code>kay\u0131t_tarihi<\/code> veya \u00f6\u011frencinin o dersten ald\u0131\u011f\u0131 bir <code>not<\/code>u olabilir. Bu t\u00fcr ek nitelikler, ara tabloya do\u011frudan eklenebilir. Ancak, <code>db.Table()<\/code> ile tan\u0131mlanan basit ara tablolar bu ek niteliklere ORM seviyesinde do\u011frudan eri\u015fim sa\u011flamaz. Bu durumu ele almak i\u00e7in <strong>Association Object Pattern<\/strong> kullan\u0131l\u0131r.<\/p>\n<h3>Neden ve Nas\u0131l<\/h3>\n<p>Association Object Pattern, ara tabloyu ayr\u0131 bir <code>db.Model<\/code> s\u0131n\u0131f\u0131 olarak tan\u0131mlayarak \u00e7al\u0131\u015f\u0131r. Bu yeni model, her iki ana modele de bire-\u00e7ok ili\u015fkisi kurar ve \u00e7oka-\u00e7ok ili\u015fkisinin kendisine ait ek nitelikleri (payload attributes) i\u00e7erir.<\/p>\n<p>\u00d6rnek olarak, <code>\u00d6\u011frenci<\/code> ve <code>Ders<\/code> modelleri aras\u0131nda bir <code>DersKaydi<\/code> (Enrollment) association object&#8217;i olu\u015ftural\u0131m:<\/p>\n<pre><code class=\"language-python\">from datetime import datetime\n\n<h2>... (Flask ve SQLAlchemy kurulumu ayn\u0131 kal\u0131r) ...<\/h2>\n\n<h2>Eski kitap_yazarlari tablosunu kald\u0131r\u0131n veya yeni bir veritaban\u0131 kullan\u0131n<\/h2>\n<h2>db.drop_all() # Geli\u015ftirme ortam\u0131nda kullan\u0131\u015fl\u0131 olabilir<\/h2>\n\nclass DersKaydi(db.Model):\n    __tablename__ = 'ders_kaydi'\n    ogrenci_id = db.Column(db.Integer, db.ForeignKey('ogrenci.id'), primary_key=True)\n    ders_id = db.Column(db.Integer, db.ForeignKey('ders.id'), primary_key=True)\n    kayit_tarihi = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)\n    notu = db.Column(db.String(2)) # \u00d6rne\u011fin, 'AA', 'BB', 'FF'\n\n    # Bu ili\u015fki nesnesinden \u00d6\u011frenci ve Ders nesnelerine eri\u015fim\n    ogrenci = db.relationship('Ogrenci', back_populates='ders_kayitlari')\n    ders = db.relationship('Ders', back_populates='ogrenci_kayitlari')\n\n    def __repr__(self):\n        return f'<DersKaydi Ogrenci:{self.ogrenci_id} Ders:{self.ders_id} Not:{self.notu}>'\n\nclass Ogrenci(db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    ad = db.Column(db.String(100), nullable=False)\n    soyad = db.Column(db.String(100), nullable=False)\n\n    # Bir \u00f6\u011frencinin birden \u00e7ok ders kayd\u0131 olabilir (One-to-Many)\n    ders_kayitlari = db.relationship('DersKaydi', back_populates='ogrenci',\n                                    cascade=\"all, delete-orphan\") # \u00d6\u011frenci silinince kay\u0131tlar\u0131 da silinsin\n\n    # \u00d6\u011frencinin ald\u0131\u011f\u0131 derslere do\u011frudan eri\u015fim i\u00e7in\n    # Bu, association object'i atlayarak derslere do\u011frudan eri\u015fim sa\u011flar\n    dersler = db.relationship('Ders', secondary='ders_kaydi', viewonly=True,\n                              overlaps=\"ders_kayitlari,ders\") # SQLAlchemy 2.0+ i\u00e7in overlaps ipucu\n\n    def __repr__(self):\n        return f'<Ogrenci {self.ad} {self.soyad}>'\n\nclass Ders(db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    kod = db.Column(db.String(10), unique=True, nullable=False)\n    ad = db.Column(db.String(200), nullable=False)\n\n    # Bir dersin birden \u00e7ok \u00f6\u011frenci kayd\u0131 olabilir (One-to-Many)\n    ogrenci_kayitlari = db.relationship('DersKaydi', back_populates='ders',\n                                       cascade=\"all, delete-orphan\") # Ders silinince kay\u0131tlar\u0131 da silinsin\n\n    # Derse kay\u0131tl\u0131 \u00f6\u011frencilere do\u011frudan eri\u015fim i\u00e7in\n    ogrenciler = db.relationship('Ogrenci', secondary='ders_kaydi', viewonly=True,\n                                 overlaps=\"ogrenci_kayitlari,ogrenci\") # SQLAlchemy 2.0+ i\u00e7in overlaps ipucu\n\n    def __repr__(self):\n        return f'<Ders {self.kod} - {self.ad}>'\n\nwith app.app_context():\n    db.create_all()<\/code><\/pre>\n<p>Bu yap\u0131da:<\/p>\n<p>1.  <strong><code>DersKaydi<\/code> Modeli:<\/strong> Bu, art\u0131k bir <code>db.Model<\/code>&#8216;dir ve <code>ogrenci_id<\/code> ile <code>ders_id<\/code>&#8216;yi yabanc\u0131 anahtar olarak i\u00e7erir. Ayr\u0131ca <code>kayit_tarihi<\/code> ve <code>notu<\/code> gibi ek niteliklere sahiptir. <code>ogrenci<\/code> ve <code>ders<\/code> ili\u015fkileri, bu kay\u0131t nesnesinden ilgili <code>Ogrenci<\/code> ve <code>Ders<\/code> nesnelerine eri\u015fim sa\u011flar. <code>back_populates<\/code> kullan\u0131larak her iki taraftaki ili\u015fki a\u00e7\u0131k\u00e7a tan\u0131mlan\u0131r.<br \/>\n2.  <strong><code>Ogrenci<\/code> ve <code>Ders<\/code> Modelleri:<\/strong> Bu modeller art\u0131k do\u011frudan \u00e7oka-\u00e7ok ili\u015fkisi kurmak yerine, <code>DersKaydi<\/code> modeliyle bire-\u00e7ok ili\u015fkisi kurar. \u00d6rne\u011fin, <code>Ogrenci<\/code> modelindeki <code>ders_kayitlari<\/code> \u00f6zelli\u011fi, bu \u00f6\u011frencinin t\u00fcm <code>DersKaydi<\/code> nesnelerini d\u00f6nd\u00fcr\u00fcr.<br \/>\n3.  <strong><code>viewonly=True<\/code> ve <code>secondary='ders_kaydi'<\/code>:<\/strong> <code>Ogrenci<\/code> modelindeki <code>dersler<\/code> ve <code>Ders<\/code> modelindeki <code>ogrenciler<\/code> ili\u015fkileri, <code>DersKaydi<\/code> association object&#8217;ini atlayarak do\u011frudan ilgili derslere veya \u00f6\u011frencilere eri\u015fim sa\u011flamak i\u00e7in kullan\u0131l\u0131r. <code>secondary<\/code> parametresi burada ara tablo ad\u0131n\u0131 al\u0131r (modelin kendisi de\u011fil, tablonun ad\u0131). <code>viewonly=True<\/code> bu ili\u015fkilerin sadece sorgulama ama\u00e7l\u0131 oldu\u011funu, bu ili\u015fkiler \u00fczerinden ekleme\/silme yap\u0131lmad\u0131\u011f\u0131n\u0131 belirtir. Ekleme\/silme i\u015flemleri <code>DersKaydi<\/code> nesneleri \u00fczerinden yap\u0131l\u0131r. <code>overlaps<\/code> parametresi, SQLAlchemy 2.0&#8217;da gerekli hale gelen bir ipucudur ve ili\u015fkilerin \u00e7ak\u0131\u015fmas\u0131n\u0131 \u00f6nlemek i\u00e7in kullan\u0131l\u0131r.<\/p>\n<h3>\u0130li\u015fkili Verilerle \u00c7al\u0131\u015fma (Association Object)<\/h3>\n<p>Association Object Pattern ile CRUD i\u015flemleri biraz farkl\u0131l\u0131k g\u00f6sterir:<\/p>\n<pre><code class=\"language-python\">with app.app_context():\n    # \u00d6\u011frenci ve dersler olu\u015ftural\u0131m\n    ogrenci1 = Ogrenci(ad='Ay\u015fe', soyad='Y\u0131lmaz')\n    ogrenci2 = Ogrenci(ad='Can', soyad='Demir')\n\n    ders1 = Ders(kod='CS101', ad='Giri\u015f Bilgisayar Bilimi')\n    ders2 = Ders(kod='MATH201', ad='Lineer Cebir')\n    ders3 = Ders(kod='PHY101', ad='Fizik I')\n\n    db.session.add_all([ogrenci1, ogrenci2, ders1, ders2, ders3])\n    db.session.commit()\n\n    # Kay\u0131tlar olu\u015fturma (ili\u015fkiyi ek niteliklerle birlikte kurma)\n    kayit1 = DersKaydi(ogrenci=ogrenci1, ders=ders1, notu='AA')\n    kayit2 = DersKaydi(ogrenci=ogrenci1, ders=ders2, notu='BA')\n    kayit3 = DersKaydi(ogrenci=ogrenci2, ders=ders1, notu='BB')\n    kayit4 = DersKaydi(ogrenci=ogrenci2, ders=ders3, notu='CC')\n\n    db.session.add_all([kayit1, kayit2, kayit3, kayit4])\n    db.session.commit()\n\n    print(\"--- Association Object ile Veri Ekleme Ba\u015far\u0131l\u0131 ---\")\n\n    # Veri Sorgulama\n    print(\"\\n--- \u00d6\u011frencilerin Ald\u0131\u011f\u0131 Dersler ve Notlar\u0131 ---\")\n    all_students = Ogrenci.query.all()\n    for student in all_students:\n        print(f\"\u00d6\u011frenci: {student.ad} {student.soyad}\")\n        for enrollment in student.ders_kayitlari:\n            print(f\"  - Ders: {enrollment.ders.ad} ({enrollment.ders.kod}), Not: {enrollment.notu}, Kay\u0131t Tarihi: {enrollment.kayit_tarihi.strftime('%Y-%m-%d')}\")\n\n    print(\"\\n--- Bir Derse Kay\u0131tl\u0131 \u00d6\u011frenciler ve Notlar\u0131 ---\")\n    cs101_ders = Ders.query.filter_by(kod='CS101').first()\n    if cs101_ders:\n        print(f\"Ders: {cs101_ders.ad} ({cs101_ders.kod})\")\n        for enrollment in cs101_ders.ogrenci_kayitlari:\n            print(f\"  - \u00d6\u011frenci: {enrollment.ogrenci.ad} {enrollment.ogrenci.soyad}, Not: {enrollment.notu}\")\n\n    # Do\u011frudan derslere eri\u015fim (viewonly=True sayesinde)\n    ayse = Ogrenci.query.filter_by(ad='Ay\u015fe').first()\n    if ayse:\n        print(f\"\\nAy\u015fe'nin do\u011frudan eri\u015filebilen dersleri: {[d.ad for d in ayse.dersler]}\")\n\n    # Veri G\u00fcncelleme\n    print(\"\\n--- Kay\u0131t G\u00fcncelleme ---\")\n    ayse_cs101_kayit = DersKaydi.query.filter_by(ogrenci=ayse, ders=cs101_ders).first()\n    if ayse_cs101_kayit:\n        print(f\"Ay\u015fe'nin CS101 dersindeki mevcut notu: {ayse_cs101_kayit.notu}\")\n        ayse_cs101_kayit.notu = 'AA+'\n        db.session.commit()\n        print(f\"Ay\u015fe'nin CS101 dersindeki yeni notu: {ayse_cs101_kayit.notu}\")\n\n    # Veri Silme\n    print(\"\\n--- Kay\u0131t Silme ---\")\n    can = Ogrenci.query.filter_by(ad='Can').first()\n    fizik_ders = Ders.query.filter_by(kod='PHY101').first()\n\n    if can and fizik_ders:\n        can_fizik_kayit = DersKaydi.query.filter_by(ogrenci=can, ders=fizik_ders).first()\n        if can_fizik_kayit:\n            print(f\"Can'\u0131n Fizik I dersindeki kayd\u0131 siliniyor: {can_fizik_kayit}\")\n            db.session.delete(can_fizik_kayit)\n            db.session.commit()\n            print(\"Kay\u0131t silindi.\")\n\n        # Kontrol edelim\n        print(f\"Can'\u0131n kalan dersleri: {[d.ad for d in can.dersler]}\")<\/code><\/pre>\n<p>Association Object Pattern, \u00e7oka-\u00e7ok ili\u015fkisinin kendisine ait ek verileri saklaman\u0131z gerekti\u011finde daha esnek ve g\u00fc\u00e7l\u00fc bir \u00e7\u00f6z\u00fcm sunar. \u0130\u015flemler, do\u011frudan ana modelin ili\u015fki listesine nesne eklemek yerine, <code>DersKaydi<\/code> gibi association object&#8217;leri olu\u015fturup y\u00f6netmek \u00fczerine kuruludur.<\/p>\n<h2>Performans ve Optimizasyon \u0130pu\u00e7lar\u0131<\/h2>\n<p>\u00c7oka-\u00e7ok ili\u015fkileri, \u00f6zellikle b\u00fcy\u00fck veri k\u00fcmeleriyle \u00e7al\u0131\u015f\u0131rken performans sorunlar\u0131na yol a\u00e7abilir. N+1 sorgu problemi ve yanl\u0131\u015f y\u00fckleme stratejileri yayg\u0131n tuzaklard\u0131r.<\/p>\n<h3>N+1 Sorgu Problemi ve \u00c7\u00f6z\u00fcmleri<\/h3>\n<p>N+1 sorgu problemi, bir ana sorgunun N adet ili\u015fkili nesne d\u00f6nd\u00fcrmesi ve her bir ili\u015fkili nesnenin detaylar\u0131na eri\u015fildi\u011finde ayr\u0131 bir sorgu daha tetiklenmesiyle ortaya \u00e7\u0131kar. Bu, toplamda N+1 sorgu anlam\u0131na gelir ve veritaban\u0131 performans\u0131n\u0131 ciddi \u015fekilde d\u00fc\u015f\u00fcrebilir.<\/p>\n<p>\u00d6rne\u011fin, t\u00fcm yazarlar\u0131 ve her yazar\u0131n kitaplar\u0131n\u0131 listelemek istedi\u011fimizi varsayal\u0131m:<\/p>\n<pre><code class=\"language-python\"># K\u00f6t\u00fc \u00f6rnek: N+1 sorgu problemi\nwith app.app_context():\n    yazarlar = Yazar.query.all() # 1 sorgu\n    for yazar in yazarlar:\n        print(f\"Yazar: {yazar.ad}\")\n        for kitap in yazar.kitaplar: # Her yazar i\u00e7in ayr\u0131 bir sorgu (N sorgu)\n            print(f\"  - {kitap.baslik}\")<\/code><\/pre>\n<p>Bu durumda, <code>Yazar.query.all()<\/code> bir sorgu \u00e7al\u0131\u015ft\u0131r\u0131r. Ard\u0131ndan, her bir yazar i\u00e7in <code>yazar.kitaplar<\/code>&#8216;a eri\u015fildi\u011finde, ili\u015fkili kitaplar\u0131 y\u00fcklemek i\u00e7in veritaban\u0131na ayr\u0131 bir sorgu daha g\u00f6nderilir. E\u011fer 100 yazar varsa, bu 1 (yazarlar) + 100 (kitaplar) = 101 sorgu demektir.<\/p>\n<p>Bu problemi \u00e7\u00f6zmek i\u00e7in <strong>eager loading (hevesli y\u00fckleme)<\/strong> kullan\u0131l\u0131r. Flask-SQLAlchemy, <code>db.relationship()<\/code> tan\u0131m\u0131ndaki <code>lazy<\/code> parametresi veya sorgulama an\u0131nda <code>joinedload()<\/code> \/ <code>subqueryload()<\/code> gibi y\u00f6ntemlerle eager loading&#8217;i destekler.<\/p>\n<p>*   <strong><code>joinedload()<\/code>:<\/strong> \u0130li\u015fkili nesneleri ana sorguyla birlikte bir <code>LEFT OUTER JOIN<\/code> kullanarak y\u00fckler.<\/p>\n<pre><code class=\"language-python\">with app.app_context():\n        # joinedload ile N+1 problemini \u00e7\u00f6zme\n        # Tek bir sorgu ile t\u00fcm yazarlar\u0131 ve ilgili kitaplar\u0131n\u0131 getirir\n        yazarlar = Yazar.query.options(db.joinedload(Yazar.kitaplar)).all()\n        for yazar in yazarlar:\n            print(f\"Yazar: {yazar.ad}\")\n            for kitap in yazar.kitaplar: # Bu eri\u015fim ek sorgu tetiklemez\n                print(f\"  - {kitap.baslik}\")<\/code><\/pre>\n<p>*   <strong><code>subqueryload()<\/code>:<\/strong> \u0130li\u015fkili nesneleri ayr\u0131 bir alt sorgu kullanarak y\u00fckler. Bu, ana sorgunun karma\u015f\u0131kl\u0131\u011f\u0131n\u0131 azalt\u0131rken N+1 problemini \u00e7\u00f6zer.<\/p>\n<pre><code class=\"language-python\">with app.app_context():\n        # subqueryload ile N+1 problemini \u00e7\u00f6zme\n        # \u0130ki sorgu \u00e7al\u0131\u015ft\u0131r\u0131r: biri yazarlar i\u00e7in, di\u011feri t\u00fcm kitaplar i\u00e7in (tek alt sorgu)\n        yazarlar = Yazar.query.options(db.subqueryload(Yazar.kitaplar)).all()\n        for yazar in yazarlar:\n            print(f\"Yazar: {yazar.ad}\")\n            for kitap in yazar.kitaplar: # Bu eri\u015fim ek sorgu tetiklemez\n                print(f\"  - {kitap.baslik}\")<\/code><\/pre>\n<p>Hangi eager loading stratejisinin kullan\u0131laca\u011f\u0131, uygulaman\u0131z\u0131n ihtiya\u00e7lar\u0131na ve veritaban\u0131 yap\u0131s\u0131na ba\u011fl\u0131d\u0131r. <code>joinedload<\/code> genellikle daha az sorgu say\u0131s\u0131 nedeniyle tercih edilirken, \u00e7ok say\u0131da ili\u015fki veya b\u00fcy\u00fck veri setleri i\u00e7in <code>subqueryload<\/code> daha iyi olabilir.<\/p>\n<p>Ek olarak, veritaban\u0131 seviyesinde do\u011fru indeksleme yapmak (\u00f6zellikle yabanc\u0131 anahtar s\u00fctunlar\u0131 \u00fczerinde) sorgu performans\u0131n\u0131 \u00f6nemli \u00f6l\u00e7\u00fcde art\u0131r\u0131r. Flask-SQLAlchemy, <code>db.Column<\/code> tan\u0131mlarken <code>index=True<\/code> parametresini kullanarak indeks olu\u015fturmay\u0131 kolayla\u015ft\u0131r\u0131r.<\/p>\n<h2>Sonu\u00e7<\/h2>\n<p>Flask-SQLAlchemy ile \u00e7oka-\u00e7ok veritaban\u0131 ili\u015fkilerini y\u00f6netmek, modern web uygulamalar\u0131 geli\u015ftiren herkes i\u00e7in temel bir beceridir. Bu makalede, \u00e7oka-\u00e7ok ili\u015fkilerinin alt\u0131nda yatan ara tablo kavram\u0131n\u0131, Flask-SQLAlchemy&#8217;nin bu ili\u015fkileri <code>db.Table()<\/code> ve <code>db.relationship()<\/code> kullanarak nas\u0131l tan\u0131mlad\u0131\u011f\u0131n\u0131 detayl\u0131ca inceledik. Basit \u00e7oka-\u00e7ok ili\u015fkilerinden, ili\u015fkinin kendisine ait ek nitelikleri bar\u0131nd\u0131ran Association Object Pattern&#8217;e kadar \u00e7e\u015fitli senaryolar\u0131 ele ald\u0131k.<\/p>\n<p>Veri ekleme, sorgulama, g\u00fcncelleme ve silme gibi temel CRUD i\u015flemlerini kod \u00f6rnekleriyle g\u00f6sterdik. Ayr\u0131ca, N+1 sorgu problemi gibi yayg\u0131n performans tuzaklar\u0131na ve <code>joinedload()<\/code> ile <code>subqueryload()<\/code> gibi eager loading teknikleriyle bu sorunlar\u0131n nas\u0131l \u00e7\u00f6z\u00fclebilece\u011fine de\u011findik.<\/p>\n<p>Flask-SQLAlchemy&#8217;nin sa\u011flad\u0131\u011f\u0131 g\u00fc\u00e7l\u00fc ORM yetenekleri sayesinde, karma\u015f\u0131k veritaban\u0131 ili\u015fkilerini Python kodunuzda nesne tabanl\u0131 bir yakla\u015f\u0131mla ele alabilir, veritaban\u0131 i\u015flemlerini soyutlayarak daha temiz ve s\u00fcrd\u00fcr\u00fclebilir uygulamalar geli\u015ftirebilirsiniz. Bu bilgileri kullanarak, Flask uygulamalar\u0131n\u0131zda daha sa\u011flam ve performansl\u0131 veri modelleri olu\u015fturmak i\u00e7in iyi bir ba\u015flang\u0131\u00e7 noktas\u0131na sahipsiniz.<\/body><\/p>\n","protected":false},"excerpt":{"rendered":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma Flask-SQLAlchemy, Flask uygulamalar\u0131 i\u00e7in SQLAlchemy ORM&#8217;ini entegre eden g\u00fc\u00e7l\u00fc bir uzant\u0131d\u0131r.","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_page_header_type":"","csco_page_load_nextpost":"","csco_page_subscribe_form":"","csco_page_contact_form":"","footnotes":""},"categories":[1505],"tags":[],"class_list":{"0":"post-40948","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-sql-2","7":"cs-entry","8":"cs-video-wrap"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.5 (Yoast SEO v25.3.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma - Kodlar\u0131n Gizemli D\u00fcnyas\u0131<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma\" \/>\n<meta property=\"og:description\" content=\"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma Flask-SQLAlchemy, Flask uygulamalar\u0131 i\u00e7in SQLAlchemy ORM&#039;ini entegre eden g\u00fc\u00e7l\u00fc bir uzant\u0131d\u0131r.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\" \/>\n<meta property=\"og:site_name\" content=\"Kodlar\u0131n Gizemli D\u00fcnyas\u0131\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-02T09:41:00+00:00\" \/>\n<meta name=\"author\" content=\"Fatih Soysal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"Fatih Soysal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"23 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\"},\"author\":{\"name\":\"Fatih Soysal\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"headline\":\"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma\",\"datePublished\":\"2026-03-02T09:41:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\"},\"wordCount\":2433,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"articleSection\":[\"SQL\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#respond\"]}],\"copyrightYear\":\"2026\",\"copyrightHolder\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\",\"name\":\"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma - Kodlar\u0131n Gizemli D\u00fcnyas\u0131\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\"},\"datePublished\":\"2026-03-02T09:41:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Anasayfa\",\"item\":\"https:\/\/fatihsoysal.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/\",\"name\":\"Fatihsoysal.com\",\"description\":\"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim\",\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\",\"name\":\"Fatih Soysal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"contentUrl\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"width\":512,\"height\":512,\"caption\":\"Fatih Soysal\"},\"logo\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\"},\"description\":\"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma - Kodlar\u0131n Gizemli D\u00fcnyas\u0131","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/","og_locale":"tr_TR","og_type":"article","og_title":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma","og_description":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma Flask-SQLAlchemy, Flask uygulamalar\u0131 i\u00e7in SQLAlchemy ORM'ini entegre eden g\u00fc\u00e7l\u00fc bir uzant\u0131d\u0131r.","og_url":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/","og_site_name":"Kodlar\u0131n Gizemli D\u00fcnyas\u0131","article_published_time":"2026-03-02T09:41:00+00:00","author":"Fatih Soysal","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"Fatih Soysal","Tahmini okuma s\u00fcresi":"23 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#article","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/"},"author":{"name":"Fatih Soysal","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"headline":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma","datePublished":"2026-03-02T09:41:00+00:00","mainEntityOfPage":{"@id":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/"},"wordCount":2433,"commentCount":0,"publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"articleSection":["SQL"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#respond"]}],"copyrightYear":"2026","copyrightHolder":{"@id":"https:\/\/fatihsoysal.com\/blog\/#organization"}},{"@type":"WebPage","@id":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/","url":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/","name":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma - Kodlar\u0131n Gizemli D\u00fcnyas\u0131","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/#website"},"datePublished":"2026-03-02T09:41:00+00:00","breadcrumb":{"@id":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fatihsoysal.com\/blog\/flask-sqlalchemy-ile-coka-cok-veritabani-iliskilerini-kullanma\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Anasayfa","item":"https:\/\/fatihsoysal.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Flask-SQLAlchemy ile \u00c7oka-\u00c7ok Veritaban\u0131 \u0130li\u015fkilerini Kullanma"}]},{"@type":"WebSite","@id":"https:\/\/fatihsoysal.com\/blog\/#website","url":"https:\/\/fatihsoysal.com\/blog\/","name":"Fatihsoysal.com","description":"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim","publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1","name":"Fatih Soysal","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","contentUrl":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","width":512,"height":512,"caption":"Fatih Soysal"},"logo":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/"},"description":"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.","url":"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/"}]}},"yoast_meta":{"yoast_wpseo_title":"","yoast_wpseo_metadesc":"","yoast_wpseo_canonical":""},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/40948","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/comments?post=40948"}],"version-history":[{"count":1,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/40948\/revisions"}],"predecessor-version":[{"id":40949,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/40948\/revisions\/40949"}],"wp:attachment":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/media?parent=40948"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/categories?post=40948"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/tags?post=40948"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}