יצירת קשר בין קלאסים וטבלאות במסד הנתונים באמצעות relationships

מחבר:
בתאריך:

טבלאות נתונים קשורות לעיתים קרובות אחת לשנייה בקשר שמכונה relationship. לדוגמא, ספר יכול להיות שייך לסדרת ספרים או מכונית למשתמש. FastApi משתמש ב- ORM כדי להפוך את כתיבת וניהול היחסים בין טבלאות לפשוט למדי.

האפליקציה שאנו מפתחים בסדרת המדריכים המוקדשת ל-FastApi כוללת טבלת משתמשים ומכוניות ובמדריך זה נראה כיצד לבטא את היחס relationship של בעלות כאשר משתמש יכול להיות הבעלים של מכונית אחת או יותר.

הסרטון הקצר הבא מדגים את האפליקציה בפעולה:

המדריך משתייך לסדרה בנושא FastApi שהיא המערכת הטובה ביותר של פייתון להקמת אפליקציות אינטרנט. מדריכים נוספים בסדרה:

  1. FastAPI - היכרות עם ספרית הקוד הטובה ביותר של פיתון להקמת אפליקציות אינטרנט
  2. אפליקצית אינטרנט עם FastApi - הקמת מסד הנתונים
  3. FastApi - לעבוד עם מסד נתונים - ORM ואלכימיה

 

להורדת הקוד המלא אותו נפתח במדריך

 

נוסיף את ה-relationships מתוך ספריית SQLAlchemy:

models.py

from sqlalchemy import Column, Integer, String, DateTime, ForeignKey

from sqlalchemy.orm import relationship
נוסיף לכל אחד מהמודלים את ה- relationship למודל האחר:

models.py

class Car(Base):
  __tablename__ = "cars"
  id = Column(Integer, primary_key=True, index=True)
  model = Column(String(80), unique=False, index=False)
  price = Column(Integer)
  created_at = Column(DateTime(timezone=True), server_default=func.now())
  updated_at = Column(DateTime(timezone=True), onupdate=func.now())
  active = Column(Integer, default=0)

  owner_id = Column(Integer, ForeignKey("users.id"))

  owner = relationship("User", back_populates="cars")


class User(Base):
  __tablename__ = "users"
  id = Column(Integer, primary_key=True, index=True)
  email = Column(String(80), unique=True)
  name = Column(String(80))
  password = Column(String(60))
  picture = Column(String(80))
  created_at = Column(DateTime(timezone=True), server_default=func.now())
  updated_at = Column(DateTime(timezone=True), onupdate=func.now())
  active = Column(Integer, default=0)

  cars = relationship("Car", back_populates="owner")
  • התכונה owner שהוספנו לקלאס Car יוצר את ה-relationship ל-User, מה שמאפשר לנו לגשת למידע על המשתמש שהמכונית שייכת לו באמצעות car.owner.
  • מצד שני, התכונה cars שהוספנו לקלאס User מאפשר לנו לשלוף את רשימת המכוניות השייכות למשתמש.
  • המפתח הזר, Foreign Key, מתווסף כשדה למסד הנתונים.

השינוי במודלים חייב להתבטא מסד הנתונים. כדי לשנות את מסד הנתונים אנחנו יכולים למחוק את הטבלאות, ואז כשנריץ את האפליקציה היא תבנה בשבילנו את מסד הנתונים עם היחסים והטור החדש.

מידע נוסף בתיעוד של FastApi

בנוסף, עלינו לציין בסכימות את הקשרים שיצרנו בין הטבלאות במסד הנתונים.

ראשית, נוסיף לתלויות את List כי ייתכן משתמש שלו יותר ממכונית אחת:

from typing import List, Optional

נוסיף לקלאס UserOut את השדה cars - כשם השדה במודל של מסד הנתונים - ע"מ שיכלול את רשימת המכוניות השייכות למשתמש:

class CarBase(BaseModel):
   model: str
   price: int

   class Config:
       orm_mode = True

class Car(CarBase):
   active: Optional[bool]
   owner_id: int


class UserOut(User):
   id: int
   cars: List[Car] = []

הסדר חשוב. כיוון ש-UserOut תלוי ב-Car צריך למקם את הקלאס Car לפניו כי פייתון קורא את הסקריפט שורה שורה מלמעלה למטה.

בנוסף, כדי שהסכימה CarOut תציג מידע על הבעלים נוסיף לה שדה owner - כשם השדה במודל - אשר יכיל מידע על משתמש:

class CarOut(CarBase):
   owner: User

הקוד המלא של הסכימות:

scheme.py

from pydantic import BaseModel, EmailStr, constr
from typing import List, Optional

class CarBase(BaseModel):
   model: str
   price: int

   class Config:
       orm_mode = True

class Car(CarBase):
   active: Optional[bool]
   owner_id: int


class User(BaseModel):
   name: constr(strip_whitespace=True, min_length=2, max_length=80)
   picture: Optional[str]

   class Config:
       orm_mode = True


class UserIn(User):
   email: EmailStr
   password: constr(strip_whitespace=True, min_length=5, max_length=16, regex=r'^\w+$')
   active: Optional[bool]

class UserOut(User):
   id: int
   cars: List[Car] = []


class CarOut(CarBase):
   owner: User

נגדיר את התוצא של המתודות ברוטרים בהתאם:

routers/users.py

@router.get('/', response_model=List[schemas.UserOut])
def get_users(limit = 10,
             q: str = Query(None, min_length=3, max_length=50, regex="^\w[a-zA-Z א-ת]+$"),
             db: Session = Depends(database.get_db)):
  
   users = db.query(models.User)
   # users = db.with_entities(models.User.name, models.User.picture)
   if q:
       search = "%{}%".format(q)
       users = users.filter(models.User.name.like(search))
   users = users.limit(limit).all()
   return users




@router.get('/{id}', status_code = 200, response_model=schemas.UserOut)
def get_user(id: int, db: Session = Depends(database.get_db)):
   user = db.query(models.User).filter(models.User.id == id).first()
   if not user:
       raise HTTPException(status_code=404, detail=f"User #{id} not found")
   return user
  • המתודות לשליפת משתמשים מחזירות response_model מסוג UseOut הכולל את רשימת המכוניות השייכות למשתמשים.

routers/cars.py

@router.get('/', response_model=List[schemas.CarOut])
def get_cars(limit = 10, active: bool = True, sort_by: Optional[str] = None, sort_dir: Optional[str] = "desc", db: Session = Depends(database.get_db)):
   #return db.query(models.Car).all()
   cars = db.query(models.Car).filter(models.Car.active == active)
   if sort_by:
      direction = desc if sort_dir == 'desc' else asc
      cars = cars.order_by(direction(getattr(models.Car, sort_by)))
   cars = cars.limit(limit).all()
 
   return cars



@router.get('/{id}', status_code = 200, response_model=schemas.CarOut)
def get_car(id: int, db: Session = Depends(database.get_db)):
  car = db.query(models.Car).filter(models.Car.id == id).first()
  if not car:
     raise HTTPException(status_code=404, detail=f"Item #{id} not found")
  return car
  • המתודות לשליפת מכוניות מחזירות response_model מסוג CarOut הכולל מידע על הבעלים של המכונית.

במדריכים הבאים בסדרה ללימוד FastApi

  1. נאפשר ניהול משתמשים.
  2. נוסיף תבניות.
  3. נעבוד עם ה-api דרך אפליקצית צד משתמש הכתובה ב-JavaScript.

לכל המדריכים בסדרה ללימוד פייתון

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

הוסף תגובה חדשה

 

= 5 + 4