update schema

This commit is contained in:
Nicola Malizia
2025-10-10 19:30:59 +02:00
parent 5fc75397e4
commit 7c4feddfa5
4 changed files with 245 additions and 33 deletions

View File

@@ -1,12 +1,15 @@
import enum
from sqlalchemy import (Column, Integer, String, Date, Enum, Boolean,
DECIMAL, Text, TIMESTAMP, ForeignKey)
DECIMAL, Text, TIMESTAMP, ForeignKey, Table)
from sqlalchemy.sql import func
from sqlalchemy.orm import relationship # Aggiungi questo import
from sqlalchemy.orm import relationship
from .database import Base
# Definiamo gli Enum Python corrispondenti ai tipi custom di PostgreSQL
# =============================================================================
# DEFINIZIONE DEGLI ENUM
# =============================================================================
class GenderType(str, enum.Enum):
male = "male"
female = "female"
@@ -25,20 +28,43 @@ class BraSystemType(str, enum.Enum):
AU = "AU"
IT = "IT"
JP = "JP"
# NUOVO ENUM: Tipo di intervento estetico
class SurgeryType(str, enum.Enum):
breast_reduction = "breast_reduction"
breast_augmentation = "breast_augmentation"
breast_lift = "breast_lift"
rhinoplasty = "rhinoplasty"
other = "other"
class Image(Base):
__tablename__ = "images"
# =============================================================================
# TABELLE DI ASSOCIAZIONE (PER RELAZIONI MOLTI-A-MOLTI)
# =============================================================================
id = Column(Integer, primary_key=True, index=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id"), nullable=False)
file_path = Column(String, nullable=False)
caption = Column(Text, nullable=True)
uploaded_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
celebrity_professions = Table(
"celebrity_professions",
Base.metadata,
Column("celebrity_id", Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), primary_key=True),
Column("profession_id", Integer, ForeignKey("professions.id", ondelete="CASCADE"), primary_key=True),
)
# Relazione inversa: "questa immagine appartiene a UNA celebrità".
# Specifichiamo che deve usare la colonna `celebrity_id` di QUESTA tabella.
celebrity = relationship("Celebrity", back_populates="images", foreign_keys=[celebrity_id])
celebrity_studios = Table(
"celebrity_studios",
Base.metadata,
Column("celebrity_id", Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), primary_key=True),
Column("studio_id", Integer, ForeignKey("studios.id", ondelete="CASCADE"), primary_key=True),
)
video_performers = Table(
"video_performers",
Base.metadata,
Column("video_id", Integer, ForeignKey("videos.id", ondelete="CASCADE"), primary_key=True),
Column("celebrity_id", Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), primary_key=True),
)
# =============================================================================
# MODELLI PRINCIPALI
# =============================================================================
class Celebrity(Base):
__tablename__ = "celebrities"
@@ -51,41 +77,140 @@ class Celebrity(Base):
nationality = Column(String, nullable=True)
ethnicity = Column(String, nullable=True)
sexuality = Column(String, nullable=True)
hair_color = Column(String, nullable=True)
eye_color = Column(String, nullable=True)
height_cm = Column(Integer, nullable=True)
weight_kg = Column(Integer, nullable=True)
body_type = Column(String, nullable=True)
bust_cm = Column(Integer, nullable=True)
waist_cm = Column(Integer, nullable=True)
hips_cm = Column(Integer, nullable=True)
chest_circumference_cm = Column(Integer, nullable=True)
bra_band_size = Column(Integer, nullable=True)
bra_cup_size = Column(String, nullable=True)
bra_size_system = Column(Enum(BraSystemType), nullable=True)
boobs_are_natural = Column(Boolean, nullable=True)
shoe_size = Column(DECIMAL(4, 1), nullable=True)
shoe_size_system = Column(Enum(ShoeSystemType), nullable=True)
biography = Column(Text, nullable=True)
official_website = Column(String, nullable=True)
# 1. Relazione "una celebrità ha MOLTE immagini".
# SQLAlchemy deve usare la FK che si trova nella tabella Image.
images = relationship("Image", back_populates="celebrity", foreign_keys="[Image.celebrity_id]")
# Chiave esterna per l'immagine del profilo
profile_image_id = Column(Integer, ForeignKey("images.id", ondelete="SET NULL"), nullable=True)
# 2. Relazione "una celebrità ha UNA immagine del profilo".
# SQLAlchemy deve usare la FK che si trova in QUESTA tabella (celebrities).
profile_image = relationship("Image", foreign_keys=[profile_image_id])
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
updated_at = Column(TIMESTAMP(timezone=True), default=func.now(), onupdate=func.now())
# Nota: le altre tabelle (images, tattoos, etc.) andrebbero modellate qui
# se si volessero gestire le relazioni in modo completo con SQLAlchemy.
# Per questo esempio, ci concentriamo solo sulla tabella `celebrities`.
# RELAZIONI
profile_image = relationship("Image", foreign_keys=[profile_image_id])
# Relazioni Uno-a-Molti (Una celebrità ha molti...)
images = relationship("Image", back_populates="celebrity", foreign_keys="Image.celebrity_id", cascade="all, delete-orphan")
tattoos = relationship("Tattoo", back_populates="celebrity", cascade="all, delete-orphan")
aliases = relationship("CelebrityAlias", back_populates="celebrity", cascade="all, delete-orphan")
activity_periods = relationship("ActivityPeriod", back_populates="celebrity", cascade="all, delete-orphan")
cosmetic_surgeries = relationship("CosmeticSurgery", back_populates="celebrity", cascade="all, delete-orphan")
social_media = relationship("SocialMediaAccount", back_populates="celebrity", cascade="all, delete-orphan")
external_links = relationship("ExternalLink", back_populates="celebrity", cascade="all, delete-orphan")
# Relazioni Molti-a-Molti
professions = relationship("Profession", secondary=celebrity_professions, back_populates="celebrities")
studios = relationship("Studio", secondary=celebrity_studios, back_populates="celebrities")
videos = relationship("Video", secondary=video_performers, back_populates="performers")
# =============================================================================
# MODELLI "LOOKUP" (PROFESSIONI, STUDIOS)
# =============================================================================
class Profession(Base):
__tablename__ = "professions"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True, nullable=False)
celebrities = relationship("Celebrity", secondary=celebrity_professions, back_populates="professions")
class Studio(Base):
__tablename__ = "studios"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True, nullable=False)
celebrities = relationship("Celebrity", secondary=celebrity_studios, back_populates="studios")
videos = relationship("Video", back_populates="studio")
class Video(Base):
__tablename__ = "videos"
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
release_date = Column(Date)
studio_id = Column(Integer, ForeignKey("studios.id"))
description = Column(Text)
url_preview = Column(String)
studio = relationship("Studio", back_populates="videos")
performers = relationship("Celebrity", secondary=video_performers, back_populates="videos")
# =============================================================================
# MODELLI SATELLITE (LEGATI A CELEBRITY CON RELAZIONE 1-A-MOLTI)
# =============================================================================
class Image(Base):
__tablename__ = "images"
id = Column(Integer, primary_key=True, index=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
file_path = Column(String, nullable=False)
caption = Column(Text, nullable=True)
uploaded_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
celebrity = relationship("Celebrity", back_populates="images", foreign_keys=[celebrity_id])
class Tattoo(Base):
__tablename__ = "tattoos"
id = Column(Integer, primary_key=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
description = Column(String, nullable=False)
body_location = Column(String)
celebrity = relationship("Celebrity", back_populates="tattoos")
class CelebrityAlias(Base):
__tablename__ = "celebrity_aliases"
id = Column(Integer, primary_key=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
alias_name = Column(String, nullable=False)
celebrity = relationship("Celebrity", back_populates="aliases")
class ActivityPeriod(Base):
__tablename__ = "activity_periods"
id = Column(Integer, primary_key=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
start_year = Column(Integer, nullable=False)
end_year = Column(Integer, nullable=True)
notes = Column(Text)
celebrity = relationship("Celebrity", back_populates="activity_periods")
class CosmeticSurgery(Base):
__tablename__ = "cosmetic_surgeries"
id = Column(Integer, primary_key=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
surgery_type = Column(Enum(SurgeryType), nullable=False)
surgery_date = Column(Date)
new_bra_band_size = Column(Integer)
new_bra_cup_size = Column(String)
new_bra_size_system = Column(Enum(BraSystemType))
notes = Column(Text)
created_at = Column(TIMESTAMP(timezone=True), server_default=func.now())
celebrity = relationship("Celebrity", back_populates="cosmetic_surgeries")
class SocialMediaAccount(Base):
__tablename__ = "social_media_accounts"
id = Column(Integer, primary_key=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
platform = Column(String, nullable=False)
url = Column(String, unique=True, nullable=False)
follower_count = Column(Integer)
last_checked_date = Column(Date)
celebrity = relationship("Celebrity", back_populates="social_media")
class ExternalLink(Base):
__tablename__ = "external_links"
id = Column(Integer, primary_key=True)
celebrity_id = Column(Integer, ForeignKey("celebrities.id", ondelete="CASCADE"), nullable=False)
url = Column(String, nullable=False)
title = Column(String)
source_website = Column(String)
publication_date = Column(Date)
celebrity = relationship("Celebrity", back_populates="external_links")

View File

@@ -1,7 +1,83 @@
from pydantic import BaseModel
from typing import Optional
from typing import Optional, List
from datetime import date, datetime
from .models import GenderType, ShoeSystemType, BraSystemType
from .models import GenderType, ShoeSystemType, BraSystemType, SurgeryType
# =============================================================================
# SCHEMI PER I MODELLI CORRELATI (LOOKUP E SATELLITE)
# =============================================================================
# --- Profession ---
class ProfessionBase(BaseModel):
name: str
class ProfessionCreate(ProfessionBase):
pass
class Profession(ProfessionBase):
id: int
class Config: from_attributes = True
# --- Studio ---
class StudioBase(BaseModel):
name: str
class StudioCreate(StudioBase):
pass
class Studio(StudioBase):
id: int
class Config: from_attributes = True
# --- Tattoo ---
class TattooBase(BaseModel):
description: str
body_location: Optional[str] = None
class TattooCreate(TattooBase):
pass
class Tattoo(TattooBase):
id: int
celebrity_id: int
class Config: from_attributes = True
# --- Alias ---
class CelebrityAliasBase(BaseModel):
alias_name: str
class CelebrityAliasCreate(CelebrityAliasBase):
pass
class CelebrityAlias(CelebrityAliasBase):
id: int
celebrity_id: int
class Config: from_attributes = True
# --- Cosmetic Surgery ---
class CosmeticSurgeryBase(BaseModel):
surgery_type: SurgeryType
surgery_date: Optional[date] = None
new_bra_band_size: Optional[int] = None
new_bra_cup_size: Optional[str] = None
new_bra_size_system: Optional[BraSystemType] = None
notes: Optional[str] = None
class CosmeticSurgeryCreate(CosmeticSurgeryBase):
pass
class CosmeticSurgery(CosmeticSurgeryBase):
id: int
celebrity_id: int
created_at: datetime
class Config: from_attributes = True
# --- Image ---
class ImageBase(BaseModel):
file_path: str
caption: Optional[str] = None
class ImageCreate(ImageBase):
pass
class Image(ImageBase):
id: int
celebrity_id: int
uploaded_at: datetime
class Config: from_attributes = True
# =============================================================================
# SCHEMI PER CELEBRITY
# =============================================================================
# Schema di base con i campi comuni
class CelebrityBase(BaseModel):
@@ -63,11 +139,22 @@ class CelebrityUpdate(BaseModel):
official_website: Optional[str] = None
profile_image_id: Optional[int] = None
# Schema per la lettura dei dati (include campi generati dal DB)
# Schema per la lettura dei dati (include campi generati dal DB e le relazioni)
class Celebrity(CelebrityBase):
id: int
created_at: datetime
updated_at: datetime
# Campi relazionali che verranno popolati automaticamente da SQLAlchemy
images: List[Image] = []
tattoos: List[Tattoo] = []
aliases: List[CelebrityAlias] = []
# Per semplicità, possiamo usare qui gli schemi di base,
# ma in un'app reale potresti volere schemi specifici per la "lettura".
professions: List[Profession] = []
studios: List[Studio] = []
cosmetic_surgeries: List[CosmeticSurgery] = []
class Config:
from_attributes = True # Permette a Pydantic di leggere dati da un modello ORM