-
Notifications
You must be signed in to change notification settings - Fork 140
feat: Realtime Scalable chat among brands and creators #56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
65d9ac3
f0545c0
35068b5
cf6305c
ab0349e
799285a
a76b22b
f14dbb6
e8e169c
fbb0fcd
6c3d2b1
6656b85
c2b502e
f3551e0
4879b4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| from datetime import datetime, timezone | ||
| from db.db import AsyncSessionLocal | ||
| from models.models import User | ||
|
|
||
|
|
||
| async def seed_db(): | ||
| users = [ | ||
| { | ||
| "id": "aabb1fd8-ba93-4e8c-976e-35e5c40b809c", | ||
| "username": "creator1", | ||
| "email": "[email protected]", | ||
| "password": "password123", | ||
| "role": "creator", | ||
| "bio": "Lifestyle and travel content creator", | ||
| }, | ||
| { | ||
| "id": "6dbfcdd5-795f-49c1-8f7a-a5538b8c6f6f", | ||
| "username": "brand1", | ||
| "email": "[email protected]", | ||
| "password": "password123", | ||
| "role": "brand", | ||
| "bio": "Sustainable fashion brand looking for influencers", | ||
| }, | ||
| ] | ||
|
|
||
| # Insert or update the users | ||
| async with AsyncSessionLocal() as session: | ||
| for user_data in users: | ||
| # Check if user exists | ||
| existing_user = await session.execute( | ||
| User.__table__.select().where(User.email == user_data["email"]) | ||
| ) | ||
| existing_user = existing_user.scalar_one_or_none() | ||
|
|
||
| if existing_user: | ||
| continue | ||
| else: | ||
| # Create new user | ||
| user = User( | ||
| id=user_data["id"], | ||
| username=user_data["username"], | ||
| email=user_data["email"], | ||
| password_hash=user_data[ | ||
| "password" | ||
| ], # Using plain password directly | ||
| role=user_data["role"], | ||
| bio=user_data["bio"], | ||
| ) | ||
| session.add(user) | ||
| print(f"Created user: {user_data['email']}") | ||
|
|
||
| # Commit the session | ||
| await session.commit() | ||
| print("β Users seeded successfully.") | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,10 @@ | ||||||||||||||||||||||||||||
| from fastapi import FastAPI | ||||||||||||||||||||||||||||
| from fastapi.middleware.cors import CORSMiddleware | ||||||||||||||||||||||||||||
| from db.db import engine | ||||||||||||||||||||||||||||
| from models import models | ||||||||||||||||||||||||||||
| from db.seed import seed_db | ||||||||||||||||||||||||||||
| from models import models, chat | ||||||||||||||||||||||||||||
| from routes.post import router as post_router | ||||||||||||||||||||||||||||
| from routes.chat import router as chat_router | ||||||||||||||||||||||||||||
| from sqlalchemy.exc import SQLAlchemyError | ||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||
|
|
@@ -11,28 +14,44 @@ | |||||||||||||||||||||||||||
| # Load environment variables | ||||||||||||||||||||||||||||
| load_dotenv() | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # Async function to create database tables with exception handling | ||||||||||||||||||||||||||||
| async def create_tables(): | ||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||
| async with engine.begin() as conn: | ||||||||||||||||||||||||||||
| await conn.run_sync(models.Base.metadata.create_all) | ||||||||||||||||||||||||||||
| await conn.run_sync(chat.Base.metadata.create_all) | ||||||||||||||||||||||||||||
| print("β Tables created successfully or already exist.") | ||||||||||||||||||||||||||||
| except SQLAlchemyError as e: | ||||||||||||||||||||||||||||
| print(f"β Error creating tables: {e}") | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| #Lifespan context manager for startup and shutdown events | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # Lifespan context manager for startup and shutdown events | ||||||||||||||||||||||||||||
| @asynccontextmanager | ||||||||||||||||||||||||||||
| async def lifespan(app: FastAPI): | ||||||||||||||||||||||||||||
| print("App is starting...") | ||||||||||||||||||||||||||||
| await create_tables() | ||||||||||||||||||||||||||||
| await seed_db() | ||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π οΈ Refactor suggestion Add error handling for database seeding. The @asynccontextmanager
async def lifespan(app: FastAPI):
print("App is starting...")
await create_tables()
- await seed_db()
+ try:
+ await seed_db()
+ print("β
Database seeded successfully.")
+ except Exception as e:
+ print(f"β οΈ Error seeding database: {e}")
+ # Consider whether to fail startup or continue based on your requirements
yield
print("App is shutting down...")π Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||
| yield | ||||||||||||||||||||||||||||
| print("App is shutting down...") | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # Initialize FastAPI | ||||||||||||||||||||||||||||
| app = FastAPI(lifespan=lifespan) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # Add CORS middleware | ||||||||||||||||||||||||||||
| app.add_middleware( | ||||||||||||||||||||||||||||
| CORSMiddleware, | ||||||||||||||||||||||||||||
| allow_origins=["http://localhost:5173"], | ||||||||||||||||||||||||||||
| allow_credentials=True, | ||||||||||||||||||||||||||||
| allow_methods=["*"], | ||||||||||||||||||||||||||||
| allow_headers=["*"], | ||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # Include the routes | ||||||||||||||||||||||||||||
| app.include_router(post_router) | ||||||||||||||||||||||||||||
| app.include_router(chat_router) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @app.get("/") | ||||||||||||||||||||||||||||
| async def home(): | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,54 @@ | ||||||||
| from sqlalchemy import Column, String, ForeignKey, DateTime, Enum, UniqueConstraint | ||||||||
| from sqlalchemy.orm import relationship | ||||||||
| from datetime import datetime, timezone | ||||||||
| from db.db import Base | ||||||||
| import uuid | ||||||||
| import enum | ||||||||
|
|
||||||||
|
|
||||||||
| def generate_uuid(): | ||||||||
| return str(uuid.uuid4()) | ||||||||
|
Comment on lines
+9
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π οΈ Refactor suggestion Remove duplicate function - use the one from models.py. This -def generate_uuid():
- return str(uuid.uuid4())
+from models.models import generate_uuidπ Committable suggestion
Suggested change
|
||||||||
|
|
||||||||
|
|
||||||||
| class MessageStatus(enum.Enum): | ||||||||
| SENT = "sent" | ||||||||
| DELIVERED = "delivered" | ||||||||
| SEEN = "seen" | ||||||||
|
|
||||||||
|
|
||||||||
| class ChatList(Base): | ||||||||
| __tablename__ = "chat_list" | ||||||||
|
|
||||||||
| id = Column(String, primary_key=True, default=generate_uuid) | ||||||||
| user1_id = Column(String, ForeignKey("users.id"), nullable=False) | ||||||||
| user2_id = Column(String, ForeignKey("users.id"), nullable=False) | ||||||||
| last_message_time = Column( | ||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||
| ) | ||||||||
|
|
||||||||
| user1 = relationship("User", foreign_keys=[user1_id], backref="chatlist_user1") | ||||||||
| user2 = relationship("User", foreign_keys=[user2_id], backref="chatlist_user2") | ||||||||
|
|
||||||||
| __table_args__ = (UniqueConstraint("user1_id", "user2_id", name="unique_chat"),) | ||||||||
|
|
||||||||
|
|
||||||||
| class ChatMessage(Base): | ||||||||
| __tablename__ = "chat_messages" | ||||||||
|
|
||||||||
| id = Column(String, primary_key=True, default=generate_uuid) | ||||||||
| sender_id = Column(String, ForeignKey("users.id"), nullable=False) | ||||||||
| receiver_id = Column(String, ForeignKey("users.id"), nullable=False) | ||||||||
| message = Column(String, nullable=False) | ||||||||
| status = Column( | ||||||||
| Enum(MessageStatus), default=MessageStatus.SENT | ||||||||
| ) # Using the enum class | ||||||||
| created_at = Column( | ||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||
| ) | ||||||||
|
|
||||||||
| sender = relationship("User", foreign_keys=[sender_id], backref="sent_messages") | ||||||||
| receiver = relationship( | ||||||||
| "User", foreign_keys=[receiver_id], backref="received_messages" | ||||||||
| ) | ||||||||
| chat_list_id = Column(String, ForeignKey("chat_list.id"), nullable=False) | ||||||||
| chat = relationship("ChatList", backref="messages") | ||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,12 +1,25 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| from sqlalchemy import Column, String, Integer, ForeignKey, Float, Text, JSON, DECIMAL, TIMESTAMP | ||||||||||||||||||||||||||||||||||||||||||||||
| from sqlalchemy import ( | ||||||||||||||||||||||||||||||||||||||||||||||
| Column, | ||||||||||||||||||||||||||||||||||||||||||||||
| String, | ||||||||||||||||||||||||||||||||||||||||||||||
| Integer, | ||||||||||||||||||||||||||||||||||||||||||||||
| ForeignKey, | ||||||||||||||||||||||||||||||||||||||||||||||
| Float, | ||||||||||||||||||||||||||||||||||||||||||||||
| Text, | ||||||||||||||||||||||||||||||||||||||||||||||
| JSON, | ||||||||||||||||||||||||||||||||||||||||||||||
| DECIMAL, | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime, | ||||||||||||||||||||||||||||||||||||||||||||||
| Boolean, | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
| from sqlalchemy.orm import relationship | ||||||||||||||||||||||||||||||||||||||||||||||
| from datetime import datetime, timezone | ||||||||||||||||||||||||||||||||||||||||||||||
| from db.db import Base | ||||||||||||||||||||||||||||||||||||||||||||||
| import uuid | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| def generate_uuid(): | ||||||||||||||||||||||||||||||||||||||||||||||
| return str(uuid.uuid4()) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # User Table (Creators & Brands) | ||||||||||||||||||||||||||||||||||||||||||||||
| class User(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
| __tablename__ = "users" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -18,13 +31,30 @@ class User(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| role = Column(String, nullable=False) # 'creator' or 'brand' | ||||||||||||||||||||||||||||||||||||||||||||||
| profile_image = Column(Text, nullable=True) | ||||||||||||||||||||||||||||||||||||||||||||||
| bio = Column(Text, nullable=True) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc)) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| is_online = Column(Boolean, default=False) # β Track if user is online | ||||||||||||||||||||||||||||||||||||||||||||||
| last_seen = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| audience = relationship("AudienceInsights", back_populates="user", uselist=False) | ||||||||||||||||||||||||||||||||||||||||||||||
| sponsorships = relationship("Sponsorship", back_populates="brand") | ||||||||||||||||||||||||||||||||||||||||||||||
| posts = relationship("UserPost", back_populates="user") | ||||||||||||||||||||||||||||||||||||||||||||||
| applications = relationship("SponsorshipApplication", back_populates="creator") | ||||||||||||||||||||||||||||||||||||||||||||||
| payments = relationship("SponsorshipPayment", back_populates="creator") | ||||||||||||||||||||||||||||||||||||||||||||||
| payments = relationship( | ||||||||||||||||||||||||||||||||||||||||||||||
| "SponsorshipPayment", | ||||||||||||||||||||||||||||||||||||||||||||||
| foreign_keys="[SponsorshipPayment.creator_id]", | ||||||||||||||||||||||||||||||||||||||||||||||
| back_populates="creator", | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
| brand_payments = relationship( | ||||||||||||||||||||||||||||||||||||||||||||||
| "SponsorshipPayment", | ||||||||||||||||||||||||||||||||||||||||||||||
| foreign_keys="[SponsorshipPayment.brand_id]", | ||||||||||||||||||||||||||||||||||||||||||||||
| back_populates="brand", | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π οΈ Refactor suggestion Eliminate SQLAlchemy overlap warnings by declaring
- payments = relationship(
+ payments = relationship(
"SponsorshipPayment",
foreign_keys="[SponsorshipPayment.creator_id]",
- back_populates="creator",
+ back_populates="creator",
+ overlaps="brand_payments",
)
- brand_payments = relationship(
+ brand_payments = relationship(
"SponsorshipPayment",
foreign_keys="[SponsorshipPayment.brand_id]",
- back_populates="brand",
+ back_populates="brand",
+ overlaps="payments",
)This keeps the mapper quiet and avoids subtle loader issues. π Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Audience Insights Table | ||||||||||||||||||||||||||||||||||||||||||||||
| class AudienceInsights(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -38,10 +68,13 @@ class AudienceInsights(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| average_views = Column(Integer) | ||||||||||||||||||||||||||||||||||||||||||||||
| time_of_attention = Column(Integer) # in seconds | ||||||||||||||||||||||||||||||||||||||||||||||
| price_expectation = Column(DECIMAL(10, 2)) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc)) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| user = relationship("User", back_populates="audience") | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Sponsorship Table (For Brands) | ||||||||||||||||||||||||||||||||||||||||||||||
| class Sponsorship(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
| __tablename__ = "sponsorships" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -54,11 +87,14 @@ class Sponsorship(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| budget = Column(DECIMAL(10, 2)) | ||||||||||||||||||||||||||||||||||||||||||||||
| engagement_minimum = Column(Float) | ||||||||||||||||||||||||||||||||||||||||||||||
| status = Column(String, default="open") | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc)) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| brand = relationship("User", back_populates="sponsorships") | ||||||||||||||||||||||||||||||||||||||||||||||
| applications = relationship("SponsorshipApplication", back_populates="sponsorship") | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # User Posts Table | ||||||||||||||||||||||||||||||||||||||||||||||
| class UserPost(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
| __tablename__ = "user_posts" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -70,10 +106,13 @@ class UserPost(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| post_url = Column(Text, nullable=True) | ||||||||||||||||||||||||||||||||||||||||||||||
| category = Column(String, nullable=True) | ||||||||||||||||||||||||||||||||||||||||||||||
| engagement_metrics = Column(JSON) # {"likes": 500, "comments": 100, "shares": 50} | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column(TIMESTAMP, default=lambda: datetime.now(timezone.utc)) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| user = relationship("User", back_populates="posts") | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Sponsorship Applications Table | ||||||||||||||||||||||||||||||||||||||||||||||
| class SponsorshipApplication(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
| __tablename__ = "sponsorship_applications" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -84,11 +123,14 @@ class SponsorshipApplication(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| post_id = Column(String, ForeignKey("user_posts.id"), nullable=True) | ||||||||||||||||||||||||||||||||||||||||||||||
| proposal = Column(Text, nullable=False) | ||||||||||||||||||||||||||||||||||||||||||||||
| status = Column(String, default="pending") | ||||||||||||||||||||||||||||||||||||||||||||||
| applied_at = Column(TIMESTAMP, default=datetime.utcnow) | ||||||||||||||||||||||||||||||||||||||||||||||
| applied_at = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| creator = relationship("User", back_populates="applications") | ||||||||||||||||||||||||||||||||||||||||||||||
| sponsorship = relationship("Sponsorship", back_populates="applications") | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Collaborations Table | ||||||||||||||||||||||||||||||||||||||||||||||
| class Collaboration(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
| __tablename__ = "collaborations" | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -98,7 +140,10 @@ class Collaboration(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| creator_2_id = Column(String, ForeignKey("users.id"), nullable=False) | ||||||||||||||||||||||||||||||||||||||||||||||
| collaboration_details = Column(Text, nullable=False) | ||||||||||||||||||||||||||||||||||||||||||||||
| status = Column(String, default="pending") | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column(TIMESTAMP,default=lambda: datetime.now(timezone.utc)) | ||||||||||||||||||||||||||||||||||||||||||||||
| created_at = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| # Sponsorship Payments Table | ||||||||||||||||||||||||||||||||||||||||||||||
| class SponsorshipPayment(Base): | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -110,6 +155,11 @@ class SponsorshipPayment(Base): | |||||||||||||||||||||||||||||||||||||||||||||
| sponsorship_id = Column(String, ForeignKey("sponsorships.id"), nullable=False) | ||||||||||||||||||||||||||||||||||||||||||||||
| amount = Column(DECIMAL(10, 2), nullable=False) | ||||||||||||||||||||||||||||||||||||||||||||||
| status = Column(String, default="pending") | ||||||||||||||||||||||||||||||||||||||||||||||
| transaction_date = Column(TIMESTAMP, default=datetime.utcnow) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| creator = relationship("User", back_populates="payments") | ||||||||||||||||||||||||||||||||||||||||||||||
| transaction_date = Column( | ||||||||||||||||||||||||||||||||||||||||||||||
| DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| creator = relationship("User", foreign_keys=[creator_id], back_populates="payments") | ||||||||||||||||||||||||||||||||||||||||||||||
| brand = relationship( | ||||||||||||||||||||||||||||||||||||||||||||||
| "User", foreign_keys=[brand_id], back_populates="brand_payments" | ||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Store hashed passwords instead of plaintext.
Storing plaintext passwords is a serious security risk. Even in seed data, you should hash passwords to simulate real-world scenarios and maintain secure practices.
π Committable suggestion