Django中实现User直接获取所有关联Conversation的方案问询
Hey there! Let's work through this problem to get your Django freelancer platform's conversation system working smoothly. Here's a step-by-step solution to meet your requirements:
1. Fix Core Model Relationships
First, let's address the missing connections in your models and build the proper links for conversations:
Update models.py with Corrected Associations
from django.db import models from django.contrib.auth.models import User from django.db.models import Q class Worker(models.Model): user = models.OneToOneField( User, on_delete=models.CASCADE, related_name="worker_profile" # Makes it easy to access worker from User ) class Conversation(models.Model): # Link to the job this conversation is tied to job = models.ForeignKey( "Job", on_delete=models.CASCADE, related_name="conversations" ) # Link to the worker involved in the conversation worker = models.ForeignKey( Worker, on_delete=models.CASCADE, related_name="conversations" ) class Meta: # Ensure one unique conversation per job-worker pair unique_together = ("job", "worker") # Add a custom property to User to fetch all related conversations def get_user_conversations(self): # Get conversations where user is the job publisher publisher_conversations = Conversation.objects.filter(job__user=self) # Get conversations where user is the worker worker_conversations = Conversation.objects.filter(worker__user=self) # Combine and deduplicate the querysets return publisher_conversations.union(worker_conversations) # Attach the property to the User model User.add_to_class("conversations", property(get_user_conversations)) class Message(models.Model): body = models.TextField(max_length=500, blank=False) # Use ForeignKeys instead of IntegerFields for proper ORM relationships sender = models.ForeignKey( User, on_delete=models.CASCADE, related_name="sent_messages" ) receiver = models.ForeignKey( User, on_delete=models.CASCADE, related_name="received_messages" ) conversation = models.ForeignKey( Conversation, on_delete=models.CASCADE, related_name="messages" ) created_at = models.DateTimeField(auto_now_add=True) # Optional: Track message time class Job(models.Model): title = models.CharField(max_length=100, blank=False) description = models.TextField(max_length=500, blank=False) # Add this critical link: associate jobs with their publishing user user = models.ForeignKey( User, on_delete=models.CASCADE, related_name="jobs" ) workers = models.ManyToManyField(Worker, through="JobHasWorker") # Optional: Explicit intermediate table for job-worker links (matches your original logic) class JobHasWorker(models.Model): job = models.ForeignKey(Job, on_delete=models.CASCADE) worker = models.ForeignKey(Worker, on_delete=models.CASCADE) # Link each job-worker pair to its conversation conversation = models.OneToOneField( Conversation, on_delete=models.CASCADE, related_name="job_worker_link" ) class Meta: unique_together = ("job", "worker")
2. Use the New Conversation Access
Now you can easily fetch all conversations for a logged-in user with:
# In your view current_user = request.user my_conversations = current_user.conversations # Returns a QuerySet you can filter further
This property handles both cases:
- Conversations for jobs the user published
- Conversations where the user is the worker assigned to a job
3. Implement Message Permission Validation
For Regular Django Views
Add this check in any view that accesses a single message:
from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404 def view_message(request, message_id): message = get_object_or_404(Message, id=message_id) # Verify user is either sender or receiver if request.user not in [message.sender, message.receiver]: return HttpResponseForbidden("You don't have permission to view this message.") # Proceed with rendering the message ...
For Django REST Framework (if using APIs)
Create a custom permission class:
from rest_framework.permissions import BasePermission class IsMessageParticipant(BasePermission): def has_object_permission(self, request, view, obj): # Allow access only if user is sender or receiver return obj.sender == request.user or obj.receiver == request.user
Then use it in your view:
from rest_framework.views import APIView class MessageDetailView(APIView): permission_classes = [IsMessageParticipant] def get(self, request, message_id): message = get_object_or_404(Message, id=message_id) # Return message data ...
Key Notes
- Migration Required: After updating models, run
python manage.py makemigrationsandpython manage.py migrateto apply changes to your database. - ORM Best Practices: Using
ForeignKeyinstead ofIntegerFieldforsender/receiverensures referential integrity and makes queries cleaner. - Unique Conversations: The
unique_togetherconstraint inConversationprevents duplicate conversations for the same job-worker pair.
内容的提问来源于stack exchange,提问作者GRS
相关产品推荐
相关产品推荐

