Building Post Management System with Python

Database Schema Design

**community/models.py**
class Thread(BaseModel):
    author = ForeignKeyField(User, verbose_name="Author")
    headline = CharField(max_length=200, verbose_name="Headline", null=True)
    community = ForeignKeyField(CommunityGroup, verbose_name="Community")
    reply_count = IntegerField(default=0, verbose_name="Reply Count")

    is_featured = BooleanField(default=0, verbose_name="Featured")
    is_trending = BooleanField(default=0, verbose_name="Trending")

    body = TextField(verbose_name="Content")

    @classmethod
    def with_author(cls):
        return cls.select(cls, User.id, User.display_name).join(User)

class ThreadReply(BaseModel):
    commenter = ForeignKeyField(User, verbose_name="Commenter", related_name="comments_authored")
    thread = ForeignKeyField(Thread, verbose_name="Thread")
    parent = ForeignKeyField('self', null=True, verbose_name="Parent Reply", related_name="child_replies")
    target_user = ForeignKeyField(User, verbose_name="Target User", related_name="replies_received", null=True)
    message = CharField(max_length=1000, verbose_name="Message")
    response_count = IntegerField(default=0, verbose_name="Response Count")
    vote_count = IntegerField(default=0, verbose_name="Vote Count")

    @classmethod
    def with_details(cls):
        commenter_alias = User.alias()
        target_alias = User.alias()
        return cls.select(
            cls, Thread, 
            target_alias.id.alias('target_id'), target_alias.display_name.alias('target_name'),
            commenter_alias.id.alias('commenter_id'), commenter_alias.display_name.alias('commenter_name')
        ).join(
            Thread, join_type=JOIN.LEFT_OUTER, on=cls.thread
        ).switch(cls).join(
            commenter_alias, join_type=JOIN.LEFT_OUTER, on=cls.commenter
        ).switch(cls).join(
            target_alias, join_type=JOIN.LEFT_OUTER, on=cls.target_user
        )

class ReplyVote(BaseModel):
    voter = ForeignKeyField(User, verbose_name="Voter")
    reply = ForeignKeyField(ThreadReply, verbose_name="Reply")

Creating New Threads

**community/forms.py** Parameter Validation
class ThreadForm(Form):
    headline = StringField("Headline", validators=[DataRequired("Headline required")])
    body = StringField("Body", validators=[DataRequired("Content required")])
**community/handlers.py** Thread Creation Handler
class ThreadHandler(RedisHandler):
    @authenticated_async
    async def post(self, community_id, *args, **kwargs):
        response = {}
        try:
            community = await self.application.objects.get(CommunityGroup, id=int(community_id))
            membership = await self.application.objects.get(
                CommunityGroupMember, 
                user=self.current_user,
                community=community, 
                status="active"
            )
            
            payload = json.loads(self.request.body.decode("utf8"))
            form = ThreadForm.from_json(payload)
            
            if form.validate():
                thread = await self.application.objects.create(
                    Thread,
                    author=self.current_user,
                    headline=form.headline.data,
                    body=form.body.data,
                    community=community
                )
                response["thread_id"] = thread.id
            else:
                self.set_status(400)
                for field in form.errors:
                    response[field] = form.errors[field][0]
                    
        except CommunityGroup.DoesNotExist:
            self.set_status(404)
        except CommunityGroupMember.DoesNotExist:
            self.set_status(403)
            
        self.finish(response)
**community/urls.py** Routing
urlpattern = (
    url("/community/([0-9]+)/threads/", ThreadHandler),
)

Retrieving Threads

**community/handlers.py** Thread Retrieval Handler
class ThreadHandler(RedisHandler):
    @authenticated_async
    async def get(self, community_id, *args, **kwargs):
        threads = []
        try:
            community = await self.application.objects.get(CommunityGroup, id=int(community_id))
            membership = await self.application.objects.get(
                CommunityGroupMember, 
                user=self.current_user,
                community=community, 
                status="active"
            )
            
            query = Thread.with_author()
            filter_type = self.get_argument("filter", None)
            
            if filter_type == "trending":
                query = query.filter(Thread.is_trending == True)
            elif filter_type == "featured":
                query = query.filter(Thread.is_featured == True)
                
            thread_list = await self.application.objects.execute(query)
            
            for thread in thread_list:
                threads.append({
                    "author": {
                        "id": thread.author.id,
                        "name": thread.author.display_name
                    },
                    "id": thread.id,
                    "headline": thread.headline,
                    "body": thread.body,
                    "reply_count": thread.reply_count
                })
                
        except CommunityGroupMember.DoesNotExist:
            self.set_status(403)
        except CommunityGroup.DoesNotExist:
            self.set_status(404)
            
        self.finish(json.dumps(threads))

Thread Details

**community/handlers.py** Thread Detail Handler
class ThreadDetailHandler(RedisHandler):
    @authenticated_async
    async def get(self, thread_id, *args, **kwargs):
        response = {}
        try:
            thread_query = Thread.with_author().where(Thread.id == int(thread_id))
            thread_results = await self.application.objects.execute(thread_query)
            
            for thread in thread_results:
                response = {
                    "author": model_to_dict(thread.author),
                    "headline": thread.headline,
                    "body": thread.body,
                    "reply_count": thread.reply_count,
                    "created": thread.created.strftime("%Y-%m-%d")
                }
                break
            else:
                self.set_status(404)
                
        except Exception:
            self.set_status(500)
            
        self.finish(response)
**community/urls.py** Routing
urlpattern = (
    url("/thread/([0-9]+)/", ThreadDetailHandler),
)

Thread Comments System

**community/forms.py** Comment Validation
class ReplyForm(Form):
    message = StringField("Message", validators=[
        DataRequired("Message required"),
        Length(min=3, message="Message too short")
    ])
**community/handlers.py** Comment Handler
class ThreadReplyHandler(RedisHandler):
    @authenticated_async
    async def post(self, thread_id, *args, **kwargs):
        response = {}
        payload = json.loads(self.request.body.decode("utf8"))
        form = ReplyForm.from_json(payload)
        
        if form.validate():
            try:
                thread = await self.application.objects.get(Thread, id=int(thread_id))
                reply = await self.application.objects.create(
                    ThreadReply,
                    commenter=self.current_user,
                    thread=thread,
                    message=form.message.data
                )
                
                thread.reply_count += 1
                await self.application.objects.update(thread)
                
                response["reply_id"] = reply.id
                response["commenter"] = {
                    "name": self.current_user.display_name,
                    "id": self.current_user.id
                }
            except Thread.DoesNotExist:
                self.set_status(404)
        else:
            self.set_status(400)
            for field in form.errors:
                response[field] = form.errors[field][0]
                
        self.finish(response)
        
    @authenticated_async
    async def get(self, thread_id, *args, **kwargs):
        replies = []
        try:
            thread = await self.application.objects.get(Thread, id=int(thread_id))
            reply_query = ThreadReply.with_details().where(
                ThreadReply.thread == thread,
                ThreadReply.parent.is_null(True)
            ).order_by(ThreadReply.created.desc())
            
            for reply in await self.application.objects.execute(reply_query):
                has_voted = False
                try:
                    await self.application.objects.get(
                        ReplyVote, 
                        reply=reply, 
                        voter=self.current_user
                    )
                    has_voted = True
                except ReplyVote.DoesNotExist:
                    pass
                    
                replies.append({
                    "commenter": model_to_dict(reply.commenter),
                    "message": reply.message,
                    "response_count": reply.response_count,
                    "vote_count": reply.vote_count,
                    "has_voted": has_voted,
                    "id": reply.id
                })
                
        except Thread.DoesNotExist:
            self.set_status(404)
            
        self.finish(json.dumps(replies, default=json_serial))
**community/urls.py** Routing
urlpattern = (
    url("/thread/([0-9]+)/replies/", ThreadReplyHandler),
)

Reply to Comments

**community/forms.py** Response Validation
class ResponseForm(Form):
    target_user = IntegerField("Target User", validators=[DataRequired("Target user required")])
    message = StringField("Message", validators=[
        DataRequired("Message required"),
        Length(min=3, message="Message too short")
    ])
**community/handlers.py** Response Handler
class ReplyResponseHandler(RedisHandler):
    @authenticated_async
    async def post(self, reply_id, *args, **kwargs):
        response = {}
        payload = json.loads(self.request.body.decode("utf8"))
        form = ResponseForm.from_json(payload)
        
        if form.validate():
            try:
                parent_reply = await self.application.objects.get(ThreadReply, id=int(reply_id))
                target = await self.application.objects.get(User, id=form.target_user.data)
                thread = await self.application.objects.get(Thread, id=parent_reply.thread.id)
                
                new_response = await self.application.objects.create(
                    ThreadReply,
                    thread=thread,
                    commenter=self.current_user,
                    parent=parent_reply,
                    target_user=target,
                    message=form.message.data
                )
                
                parent_reply.response_count += 1
                await self.application.objects.update(parent_reply)
                
                response["response_id"] = new_response.id
                response["commenter"] = {
                    "id": self.current_user.id,
                    "name": self.current_user.display_name
                }
                
                # Send notification
                await self.application.objects.create(
                    Notification,
                    sender=self.current_user,
                    recipient=target,
                    notification_type=2,
                    parent_content=parent_reply.message,
                    content=form.message.data
                )
                
            except ThreadReply.DoesNotExist:
                self.set_status(404)
            except User.DoesNotExist:
                self.set_status(400)
                response["target_user"] = "User not found"
        else:
            self.set_status(400)
            for field in form.errors:
                response[field] = form.errors[field][0]
                
        self.finish(response)
        
    @authenticated_async
    async def get(self, reply_id, *args, **kwargs):
        responses = []
        query = ThreadReply.with_details().where(
            ThreadReply.parent_id == int(reply_id)
        )
        
        for response in await self.application.objects.execute(query):
            responses.append({
                "commenter": model_to_dict(response.commenter),
                "message": response.message,
                "response_count": response.response_count,
                "created": response.created.strftime("%Y-%m-%d"),
                "id": response.id
            })
            
        self.finish(json.dumps(responses, default=json_serial))
**community/urls.py** Routing
urlpattern = (
    url("/reply/([0-9]+)/responses/", ReplyResponseHandler),
)

Comment Voting System

**community/handlers.py** Vote Handler
class ReplyVoteHandler(RedisHandler):
    @authenticated_async
    async def post(self, reply_id, *args, **kwargs):
        response = {}
        try:
            reply = await self.application.objects.get(ThreadReply, id=int(reply_id))
            vote = await self.application.objects.create(
                ReplyVote,
                voter=self.current_user,
                reply=reply
            )
            reply.vote_count += 1
            await self.application.objects.update(reply)
            
            response["vote_id"] = vote.id
            
        except ThreadReply.DoesNotExist:
            self.set_status(404)
            
        self.finish(response)
**community/urls.py** Routing
urlpattern = (
    url("/reply/([0-9]+)/vote/", ReplyVoteHandler),
)

Tags: python web development Database Design API Development Community Platform

Posted on Mon, 28 Sep 2026 16:37:07 +0000 by kimbhoot