fastapi/docs/tr/docs/tutorial/bigger-applications.md

20 KiB
Raw Blame History

Daha Büyük Uygulamalar - Birden Fazla Dosya

Bir uygulama veya web API geliştirirken, her şeyi tek bir dosyaya sığdırabilmek nadirdir.

FastAPI, tüm esnekliği korurken uygulamanızı yapılandırmanıza yardımcı olan pratik bir araç sunar.

/// info | Bilgi

Flask'ten geliyorsanız, bu yapı Flask'in Blueprints'ine denk gelir.

///

Örnek Bir Dosya Yapısı

Diyelim ki şöyle bir dosya yapınız var:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   ├── dependencies.py
│   └── routers
│   │   ├── __init__.py
│   │   ├── items.py
│   │   └── users.py
│   └── internal
│       ├── __init__.py
│       └── admin.py

/// tip | İpucu

Birden fazla __init__.py dosyası var: her dizinde veya alt dizinde bir tane.

Bu sayede bir dosyadaki kodu diğerine import edebilirsiniz.

Örneğin app/main.py içinde şöyle bir satırınız olabilir:

from app.routers import items

///

  • app dizini her şeyi içerir. Ayrıca boş bir app/__init__.py dosyası olduğu için bir "Python package" (bir "Python module" koleksiyonu) olur: app.
  • İçinde bir app/main.py dosyası vardır. Bir Python package'in (içinde __init__.py dosyası olan bir dizinin) içinde olduğundan, o package'in bir "module"üdür: app.main.
  • Benzer şekilde app/dependencies.py dosyası da bir "module"dür: app.dependencies.
  • app/routers/ adında bir alt dizin vardır ve içinde başka bir __init__.py dosyası bulunur; dolayısıyla bu bir "Python subpackage"dir: app.routers.
  • app/routers/items.py dosyası app/routers/ packagei içinde olduğundan bir submoduledür: app.routers.items.
  • app/routers/users.py için de aynı şekilde, başka bir submoduledür: app.routers.users.
  • app/internal/ adında bir alt dizin daha vardır ve içinde başka bir __init__.py dosyası bulunur; dolayısıyla bu da bir "Python subpackage"dir: app.internal.
  • Ve app/internal/admin.py dosyası başka bir submoduledür: app.internal.admin.

Aynı dosya yapısı, yorumlarla birlikte:

.
├── app                  # "app" bir Python package'idir
│   ├── __init__.py      # bu dosya, "app"i bir "Python package" yapar
│   ├── main.py          # "main" module'ü, örn. import app.main
│   ├── dependencies.py  # "dependencies" module'ü, örn. import app.dependencies
│   └── routers          # "routers" bir "Python subpackage"idir
│   │   ├── __init__.py  # "routers"ı bir "Python subpackage" yapar
│   │   ├── items.py     # "items" submodule'ü, örn. import app.routers.items
│   │   └── users.py     # "users" submodule'ü, örn. import app.routers.users
│   └── internal         # "internal" bir "Python subpackage"idir
│       ├── __init__.py  # "internal"ı bir "Python subpackage" yapar
│       └── admin.py     # "admin" submodule'ü, örn. import app.internal.admin

APIRouter

Diyelim ki sadece kullanıcıları yönetmeye ayrılmış dosyanız /app/routers/users.py içindeki submodule olsun.

Kullanıcılarla ilgili path operationları, kodun geri kalanından ayrı tutmak istiyorsunuz; böylece düzenli kalır.

Ancak bu hâlâ aynı FastAPI uygulaması/web APIsinin bir parçasıdır (aynı "Python Package" içinde).

Bu module için path operationları APIRouter kullanarak oluşturabilirsiniz.

APIRouter Import Edin

FastAPI classında yaptığınız gibi import edip bir "instance" oluşturursunuz:

{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[1,3] title["app/routers/users.py"] *}

APIRouter ile Path Operations

Sonra bunu kullanarak path operationlarınızı tanımlarsınız.

FastAPI classını nasıl kullanıyorsanız aynı şekilde kullanın:

{* ../../docs_src/bigger_applications/app_an_py39/routers/users.py hl[6,11,16] title["app/routers/users.py"] *}

APIRouterı "mini bir FastAPI" classı gibi düşünebilirsiniz.

Aynı seçeneklerin hepsi desteklenir.

Aynı parameters, responses, dependencies, tags, vb.

/// tip | İpucu

Bu örnekte değişkenin adı router. Ancak istediğiniz gibi adlandırabilirsiniz.

///

Bu APIRouterı ana FastAPI uygulamasına ekleyeceğiz; ama önce dependencylere ve bir diğer APIRoutera bakalım.

Dependencies

Uygulamanın birden fazla yerinde kullanılacak bazı dependencylere ihtiyacımız olacağını görüyoruz.

Bu yüzden onları ayrı bir dependencies moduleüne koyuyoruz (app/dependencies.py).

Şimdi, özel bir X-Token header'ını okumak için basit bir dependency kullanalım:

{* ../../docs_src/bigger_applications/app_an_py39/dependencies.py hl[3,6:8] title["app/dependencies.py"] *}

/// tip | İpucu

Örneği basit tutmak için uydurma bir header kullanıyoruz.

Ancak gerçek senaryolarda, entegre Security yardımcı araçlarını{.internal-link target=_blank} kullanarak daha iyi sonuç alırsınız.

///

APIRouter ile Başka Bir Module

Diyelim ki uygulamanızdaki "items" ile ilgili endpoint'ler de app/routers/items.py moduleünde olsun.

Şunlar için path operationlarınız var:

  • /items/
  • /items/{item_id}

Bu, app/routers/users.py ile aynı yapıdadır.

Ancak biraz daha akıllı davranıp kodu sadeleştirmek istiyoruz.

Bu moduledeki tüm path operationların şu ortak özelliklere sahip olduğunu biliyoruz:

  • Path prefix: /items.
  • tags: (tek bir tag: items).
  • Ek responses.
  • dependencies: hepsinin, oluşturduğumuz X-Token dependencysine ihtiyacı var.

Dolayısıyla bunları her path operationa tek tek eklemek yerine APIRoutera ekleyebiliriz.

{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *}

Her path operationın pathi aşağıdaki gibi / ile başlamak zorunda olduğundan:

@router.get("/{item_id}")
async def read_item(item_id: str):
    ...

...prefixin sonunda / olmamalıdır.

Yani bu örnekte prefix /items olur.

Ayrıca, bu router içindeki tüm path operationlara uygulanacak bir tags listesi ve ek responses da ekleyebiliriz.

Ve routerdaki tüm path operationlara eklenecek, her request için çalıştırılıp çözülecek bir dependencies listesi de ekleyebiliriz.

/// tip | İpucu

path operation decoratorlarındaki dependencylerde{.internal-link target=_blank} olduğu gibi, path operation functionınıza herhangi bir değer aktarılmayacağını unutmayın.

///

Sonuç olarak item pathleri artık:

  • /items/
  • /items/{item_id}

...tam da istediğimiz gibi olur.

  • Hepsi, içinde tek bir string "items" bulunan bir tag listesiyle işaretlenir.
    • Bu "tags", özellikle otomatik interaktif dokümantasyon sistemleri (OpenAPI) için çok faydalıdır.
  • Hepsi önceden tanımlı responsesları içerir.
  • Bu path operationların hepsinde, öncesinde dependencies listesi değerlendirilip çalıştırılır.
    • Ayrıca belirli bir path operation içinde dependency tanımlarsanız, onlar da çalıştırılır.
    • Önce router dependencyleri, sonra decoratordaki dependencies{.internal-link target=_blank}, sonra da normal parametre dependencyleri çalışır.
    • Ayrıca scopes ile Security dependencyleri{.internal-link target=_blank} de ekleyebilirsiniz.

/// tip | İpucu

APIRouter içinde dependencies kullanmak, örneğin bir grup path operation için kimlik doğrulamayı zorunlu kılmakta kullanılabilir. Dependencyleri tek tek her birine eklemeseniz bile.

///

/// check | Ek bilgi

prefix, tags, responses ve dependencies parametreleri (çoğu başka örnekte olduğu gibi) kod tekrarını önlemenize yardımcı olan, FastAPInin bir özelliğidir.

///

Dependency'leri Import Edin

Bu kod app.routers.items moduleünde, yani app/routers/items.py dosyasında duruyor.

Dependency functionını ise app.dependencies moduleünden, yani app/dependencies.py dosyasından almamız gerekiyor.

Bu yüzden dependencyler için .. ile relative import kullanıyoruz:

{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[3] title["app/routers/items.py"] *}

Relative Import Nasıl Çalışır

/// tip | İpucu

Importların nasıl çalıştığını çok iyi biliyorsanız, bir sonraki bölüme geçin.

///

Tek bir nokta ., örneğin:

from .dependencies import get_token_header

şu anlama gelir:

  • Bu moduleün (yani app/routers/items.py dosyasının) bulunduğu package içinden başla ( app/routers/ dizini)...
  • dependencies moduleünü bul (app/routers/dependencies.py gibi hayali bir dosya)...
  • ve oradan get_token_header functionını import et.

Ama o dosya yok; bizim dependencylerimiz app/dependencies.py dosyasında.

Uygulama/dosya yapımızın nasıl göründüğünü hatırlayın:


İki nokta .., örneğin:

from ..dependencies import get_token_header

şu anlama gelir:

  • Bu moduleün bulunduğu package içinden başla (app/routers/ dizini)...
  • üst (parent) packagee çık (app/ dizini)...
  • burada dependencies moduleünü bul (app/dependencies.py dosyası)...
  • ve oradan get_token_header functionını import et.

Bu doğru şekilde çalışır! 🎉


Aynı şekilde, üç nokta ... kullansaydık:

from ...dependencies import get_token_header

şu anlama gelirdi:

  • Bu moduleün bulunduğu package içinden başla (app/routers/ dizini)...
  • üst packagee çık (app/ dizini)...
  • sonra bir üstüne daha çık (orada bir üst package yok; app en üst seviye 😱)...
  • ve orada dependencies moduleünü bul (app/dependencies.py dosyası)...
  • ve oradan get_token_header functionını import et.

Bu, app/ dizininin üstünde, kendi __init__.py dosyası olan başka bir packagee işaret ederdi. Ama bizde böyle bir şey yok. Dolayısıyla bu örnekte hata verirdi. 🚨

Artık nasıl çalıştığını bildiğinize göre, uygulamalarınız ne kadar karmaşık olursa olsun relative importları kullanabilirsiniz. 🤓

Özel tags, responses ve dependencies Ekleyin

/items prefixini ya da tags=["items"] değerini her path operationa tek tek eklemiyoruz; çünkü bunları APIRoutera ekledik.

Ama yine de belirli bir path operationa uygulanacak ek tags tanımlayabilir, ayrıca o path operationa özel responses ekleyebiliriz:

{* ../../docs_src/bigger_applications/app_an_py39/routers/items.py hl[30:31] title["app/routers/items.py"] *}

/// tip | İpucu

Bu son path operationda tag kombinasyonu şöyle olur: ["items", "custom"].

Ayrıca dokümantasyonda iki response da görünür: biri 404, diğeri 403.

///

Ana FastAPI

Şimdi app/main.py moduleüne bakalım.

Burada FastAPI classını import edip kullanırsınız.

Bu dosya, uygulamanızda her şeyi bir araya getiren ana dosya olacak.

Mantığın büyük kısmı artık kendi modulelerinde yaşayacağı için ana dosya oldukça basit kalır.

FastAPI Import Edin

Normal şekilde bir FastAPI classı oluşturursunuz.

Hatta her APIRouter için olan dependencylerle birleştirilecek global dependencies{.internal-link target=_blank} bile tanımlayabilirsiniz:

{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[1,3,7] title["app/main.py"] *}

APIRouter Import Edin

Şimdi APIRouter içeren diğer submoduleleri import ediyoruz:

{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[4:5] title["app/main.py"] *}

app/routers/users.py ve app/routers/items.py dosyaları aynı Python packagei olan appin parçası olan submoduleler olduğu için, onları "relative import" ile tek bir nokta . kullanarak import edebiliriz.

Import Nasıl Çalışır

Şu bölüm:

from .routers import items, users

şu anlama gelir:

  • Bu moduleün (yani app/main.py dosyasının) bulunduğu package içinden başla (app/ dizini)...
  • routers subpackageini bul (app/routers/ dizini)...
  • ve buradan items submoduleünü (app/routers/items.py) ve users submoduleünü (app/routers/users.py) import et...

items moduleünün içinde router adında bir değişken vardır (items.router). Bu, app/routers/items.py dosyasında oluşturduğumuz aynı değişkendir; bir APIRouter nesnesidir.

Sonra aynı işlemi users moduleü için de yaparız.

Ayrıca şöyle de import edebilirdik:

from app.routers import items, users

/// info | Bilgi

İlk sürüm "relative import"tur:

from .routers import items, users

İkinci sürüm "absolute import"tur:

from app.routers import items, users

Python Packages ve Modules hakkında daha fazlası için, Python'ın Modules ile ilgili resmi dokümantasyonunu okuyun.

///

İsim Çakışmalarını Önleyin

items submoduleünü doğrudan import ediyoruz; sadece içindeki router değişkenini import etmiyoruz.

Çünkü users submoduleünde de router adlı başka bir değişken var.

Eğer şöyle sırayla import etseydik:

from .routers.items import router
from .routers.users import router

users içindeki router, items içindeki routerın üstüne yazardı ve ikisini aynı anda kullanamazdık.

Bu yüzden ikisini de aynı dosyada kullanabilmek için submoduleleri doğrudan import ediyoruz:

{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[5] title["app/main.py"] *}

users ve items için APIRouterları Dahil Edin

Şimdi users ve items submodulelerindeki routerları dahil edelim:

{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[10:11] title["app/main.py"] *}

/// info | Bilgi

users.router, app/routers/users.py dosyasının içindeki APIRouterı içerir.

items.router ise app/routers/items.py dosyasının içindeki APIRouterı içerir.

///

app.include_router() ile her bir APIRouterı ana FastAPI uygulamasına ekleyebiliriz.

Böylece o router içindeki tüm routelar uygulamanın bir parçası olarak dahil edilir.

/// note | Teknik Detaylar

Aslında içeride, APIRouter içinde tanımlanan her path operation için bir path operation oluşturur.

Yani perde arkasında, her şey tek bir uygulamaymış gibi çalışır.

///

/// check | Ek bilgi

Routerları dahil ederken performans konusunda endişelenmeniz gerekmez.

Bu işlem mikrosaniyeler sürer ve sadece startup sırasında olur.

Dolayısıyla performansı etkilemez.

///

Özel prefix, tags, responses ve dependencies ile Bir APIRouter Dahil Edin

Şimdi, kurumunuzun size app/internal/admin.py dosyasını verdiğini düşünelim.

Bu dosyada, kurumunuzun birden fazla proje arasında paylaştığı bazı admin path operationlarını içeren bir APIRouter var.

Bu örnekte çok basit olacak. Ancak kurum içinde başka projelerle paylaşıldığı için, bunu değiştirip prefix, dependencies, tags vs. doğrudan APIRoutera ekleyemediğimizi varsayalım:

{* ../../docs_src/bigger_applications/app_an_py39/internal/admin.py hl[3] title["app/internal/admin.py"] *}

Yine de bu APIRouterı dahil ederken özel bir prefix ayarlamak istiyoruz ki tüm path operationları /admin ile başlasın; ayrıca bu projede hâlihazırda kullandığımız dependencies ile güvene almak, tags ve responses eklemek istiyoruz.

Orijinal APIRouterı değiştirmeden, bu parametreleri app.include_router()a vererek hepsini tanımlayabiliriz:

{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[14:17] title["app/main.py"] *}

Böylece orijinal APIRouter değişmeden kalır; yani aynı app/internal/admin.py dosyasını kurum içindeki diğer projelerle de paylaşmaya devam edebiliriz.

Sonuç olarak, uygulamamızda admin moduleündeki her bir path operation şunlara sahip olur:

  • /admin prefixi.
  • admin tagi.
  • get_token_header dependencysi.
  • 418 responseu. 🍵

Ancak bu sadece bizim uygulamamızdaki o APIRouter için geçerlidir; onu kullanan diğer kodlar için değil.

Dolayısıyla örneğin diğer projeler aynı APIRouterı farklı bir authentication yöntemiyle kullanabilir.

Bir Path Operation Dahil Edin

Path operationları doğrudan FastAPI uygulamasına da ekleyebiliriz.

Burada bunu yapıyoruz... sadece yapabildiğimizi göstermek için 🤷:

{* ../../docs_src/bigger_applications/app_an_py39/main.py hl[21:23] title["app/main.py"] *}

ve app.include_router() ile eklenen diğer tüm path operationlarla birlikte doğru şekilde çalışır.

/// info | Çok Teknik Detaylar

Not: Bu oldukça teknik bir detay; büyük ihtimalle direkt geçebilirsiniz.


APIRouterlar "mount" edilmez; uygulamanın geri kalanından izole değildir.

Çünkü path operationlarını OpenAPI şemasına ve kullanıcı arayüzlerine dahil etmek istiyoruz.

Onları tamamen izole edip bağımsız şekilde "mount" edemediğimiz için, path operationlar doğrudan eklenmek yerine "klonlanır" (yeniden oluşturulur).

///

Otomatik API Dokümanını Kontrol Edin

Şimdi uygulamanızı çalıştırın:

$ fastapi dev app/main.py

<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Ve dokümanları http://127.0.0.1:8000/docs adresinde açın.

Tüm submodulelerdeki pathleri, doğru pathler (ve prefixler) ve doğru taglerle birlikte içeren otomatik API dokümanını göreceksiniz:

Aynı Router'ı Farklı prefix ile Birden Fazla Kez Dahil Edin

.include_router() ile aynı routerı farklı prefixler kullanarak birden fazla kez de dahil edebilirsiniz.

Örneğin aynı APIyi /api/v1 ve /api/latest gibi farklı prefixler altında sunmak için faydalı olabilir.

Bu, muhtemelen ihtiyacınız olmayan ileri seviye bir kullanımdır; ancak gerekirse diye mevcut.

Bir APIRouterı Başka Birine Dahil Edin

Bir APIRouterı FastAPI uygulamasına dahil ettiğiniz gibi, bir APIRouterı başka bir APIRoutera da şu şekilde dahil edebilirsiniz:

router.include_router(other_router)

routerı FastAPI uygulamasına dahil etmeden önce bunu yaptığınızdan emin olun; böylece other_router içindeki path operationlar da dahil edilmiş olur.