06/15 Phase 3 codes

This commit is contained in:
2026-06-15 14:19:14 -04:00
parent c2064b84b4
commit 60526262f0
20 changed files with 802 additions and 7 deletions
+76
View File
@@ -0,0 +1,76 @@
"""Messaging models: Conversation (per listing, per buyer/seller pair) + Message.
One conversation per (listing, buyer) pair — enforced by unique constraint.
The seller is always listing.user; buyer is any other authenticated user.
Messages are append-only; soft-delete not needed at Phase 3.
"""
from datetime import datetime
from app.extensions import db
class Conversation(db.Model):
__tablename__ = "conversations"
__table_args__ = (
db.UniqueConstraint("listing_id", "buyer_id", name="uq_conv_listing_buyer"),
)
id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
primary_key=True, autoincrement=True)
listing_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
db.ForeignKey("listings.id"), nullable=False, index=True)
buyer_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
db.ForeignKey("users.id"), nullable=False, index=True)
seller_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
db.ForeignKey("users.id"), nullable=False, index=True)
last_message_at = db.Column(db.DateTime, nullable=True, index=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
listing = db.relationship("Listing",
backref=db.backref("conversations", lazy="dynamic"))
buyer = db.relationship("User", foreign_keys=[buyer_id],
backref=db.backref("bought_conversations", lazy="dynamic"))
seller = db.relationship("User", foreign_keys=[seller_id],
backref=db.backref("sold_conversations", lazy="dynamic"))
messages = db.relationship("Message", back_populates="conversation",
order_by="Message.created_at",
cascade="all, delete-orphan", lazy="selectin")
def other_party(self, user):
return self.seller if user.id == self.buyer_id else self.buyer
def unread_count(self, user):
return sum(1 for m in self.messages
if m.sender_id != user.id and m.read_at is None)
def __repr__(self):
return f"<Conversation {self.id} L{self.listing_id}>"
class Message(db.Model):
__tablename__ = "messages"
id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
primary_key=True, autoincrement=True)
conversation_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
db.ForeignKey("conversations.id"),
nullable=False, index=True)
sender_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"),
db.ForeignKey("users.id"), nullable=False)
body = db.Column(db.Text, nullable=False)
read_at = db.Column(db.DateTime, nullable=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
conversation = db.relationship("Conversation", back_populates="messages")
sender = db.relationship("User",
backref=db.backref("sent_messages", lazy="dynamic"))
@property
def is_read(self):
return self.read_at is not None
def mark_read(self):
if self.read_at is None:
self.read_at = datetime.utcnow()
def __repr__(self):
return f"<Message {self.id} C{self.conversation_id}>"