Initial project commit
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
from datetime import datetime
|
||||
|
||||
from geoalchemy2 import Geometry
|
||||
from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, Numeric, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class Spot(Base):
|
||||
__tablename__ = "spots"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
title: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
|
||||
city: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
||||
location: Mapped[str] = mapped_column(
|
||||
Geometry("POINT", srid=4326), nullable=False
|
||||
)
|
||||
description: Mapped[str | None] = mapped_column(Text)
|
||||
transport: Mapped[str | None] = mapped_column(Text)
|
||||
best_time: Mapped[str | None] = mapped_column(String(200))
|
||||
difficulty: Mapped[str | None] = mapped_column(Text)
|
||||
is_free: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
price_min: Mapped[float | None] = mapped_column(Numeric(10, 2), nullable=True)
|
||||
price_max: Mapped[float | None] = mapped_column(Numeric(10, 2), nullable=True)
|
||||
audit_status: Mapped[str] = mapped_column(String(20), default="pending")
|
||||
reject_reason: Mapped[str | None] = mapped_column(String(500))
|
||||
avg_rating: Mapped[float | None] = mapped_column(Float, default=None)
|
||||
rating_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
favorite_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
creator_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("users.id"), nullable=False
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
creator = relationship("User", lazy="selectin")
|
||||
images = relationship(
|
||||
"SpotImage", back_populates="spot", lazy="selectin", order_by="SpotImage.sort_order"
|
||||
)
|
||||
tags = relationship("Tag", secondary="spot_tags", lazy="selectin")
|
||||
|
||||
@property
|
||||
def longitude(self) -> float | None:
|
||||
if self.location is None:
|
||||
return None
|
||||
# WKBElement → use ST_X via a query; for serialisation we parse the WKT
|
||||
from geoalchemy2.shape import to_shape
|
||||
|
||||
point = to_shape(self.location)
|
||||
return point.x
|
||||
|
||||
@property
|
||||
def latitude(self) -> float | None:
|
||||
if self.location is None:
|
||||
return None
|
||||
from geoalchemy2.shape import to_shape
|
||||
|
||||
point = to_shape(self.location)
|
||||
return point.y
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Spot {self.id} {self.title}>"
|
||||
|
||||
|
||||
class SpotImage(Base):
|
||||
__tablename__ = "spot_images"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
spot_id: Mapped[int] = mapped_column(
|
||||
Integer, ForeignKey("spots.id"), nullable=False
|
||||
)
|
||||
image_url: Mapped[str] = mapped_column(String(500), nullable=False)
|
||||
is_cover: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
audit_status: Mapped[str] = mapped_column(String(20), default="pending")
|
||||
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
|
||||
spot = relationship("Spot", back_populates="images")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<SpotImage {self.id} spot={self.spot_id}>"
|
||||
Reference in New Issue
Block a user