Как передать параметры связанных объектов в сериализатор основных объектов
У меня есть объект Room, который будет содержать некоторые настройки. Я хотел сделать Room и RoomSettings разными объектами, но связанными друг с другом. RoomSettings будет создаваться каждый раз, когда создается комната, поэтому я использую сигналы. Однако при создании комнаты мне нужно передать аргументы RoomSettings из Room в RoomSettings через сигналы, чтобы я мог установить настройки при создании.
models.py
from django.db import models
from users.models import AppUser as UserModel
from django.core.validators import MaxValueValidator, MinValueValidator
import random, string
from datetime import datetime
def generate_unique_key():
length = 10
while True:
key = ''.join(random.choices(string.ascii_uppercase, k=length))
if Room.objects.filter(key=key).count() == 0:
break
return key
class Room(models.Model):
host = models.ForeignKey(UserModel, on_delete=models.CASCADE, related_name='rooms')
key = models.CharField(max_length=10, default=generate_unique_key, unique=True, editable=False)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
def __str__(self):
return f'{self.key} - {self.host.username}'
def get_chat(self):
return self.chat
class RoomSettings(models.Model):
room = models.OneToOneField(Room, on_delete=models.CASCADE, related_name='settings')
max_users = models.PositiveIntegerField(default=8, validators=[MaxValueValidator(8), MinValueValidator(1)])
is_public = models.BooleanField(default=True)
users_can_send_message = models.BooleanField(default=True)
serializers.py
from rest_framework import serializers
from .models import Room, RoomSettings
from users.serializers import UserSerializer
from chats.serializers import RoomChatSerializer
class SettingSerializer(serializers.ModelSerializer):
class Meta:
model = RoomSettings
exclude = ['room']
class CreateRoomSerializer(serializers.ModelSerializer):
max_users = serializers.IntegerField()
is_public = serializers.BooleanField()
users_can_send_message = serializers.BooleanField()
class Meta:
model = Room
fields = ['max_users', 'is_public', 'users_can_send_message']
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Room, RoomSettings
from chats.models import Chat
@receiver(post_save, sender=Room)
def post_save_create_room(sender, instance, created, **kwargs):
if created:
RoomSettings.objects.create(room=instance, **kwargs)
Chat.objects.create(room=instance)
views.py
class CreateRoomView(CreateAPIView):
permission_classes = [IsAuthenticated]
serializer_class = CreateRoomSerializer
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
room = Room.objects.create(host=self.request.user, **serializer.data)
return JsonResponse(RoomSerializer(room).data, status=status.HTTP_201_CREATED)
Я пытался передать аргументы в конечную точку CreateRoomView, но получил ошибки. Он должен создать Room и RoomSettings с заданными данными.
Я не очень понимаю, зачем вам вообще нужна модель RoomSettings
, особенно если учесть, что вы каждый раз создаете запись RoomSettings
для Room
. Цель OneToOneField
- сделать дополнительную модель (здесь RoomSettings
) необязательной, но здесь вы, похоже, "жонглируете" двумя моделями, когда нет никакого смысла разделять их на две.
Просто соедините их вместе в одну модель:
from django.conf import settings
def generate_unique_key():
length = 10
while True:
key = ''.join(random.choices(string.ascii_uppercase, k=length))
if not Room.objects.filter(key=key).exists():
break
return key
class Room(models.Model):
host = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='rooms',
editable=False,
)
key = models.CharField(
max_length=10, default=generate_unique_key, unique=True, editable=False
)
created_at = models.DateTimeField(auto_now_add=True)
max_users = models.PositiveIntegerField(
default=8, validators=[MaxValueValidator(8), MinValueValidator(1)]
)
is_public = models.BooleanField(default=True)
users_can_send_message = models.BooleanField(default=True)
def __str__(self):
return f'{self.key} - {self.host.username}'
def get_chat(self):
return self.chat
Таким образом, сериализатор упрощается до:
class RoomSerializer(serializers.ModelSerializer):
max_users = serializers.IntegerField()
is_public = serializers.BooleanField()
users_can_send_message = serializers.BooleanField()
class Meta:
model = Room
fields = ['max_users', 'is_public', 'users_can_send_message']
и, таким образом, создавая объект для:
class CreateRoomView(CreateAPIView):
permission_classes = [IsAuthenticated]
serializer_class = RoomSerializer
def perform_create(self, serializer):
serializer.save(host=request.user)
Note: It is normally better to make use of the
settings.AUTH_USER_MODEL
[Django-doc] to refer to the user model, than to use theUser
model [Django-doc] directly. For more information you can see the referencing theUser
model section of the documentation [Django-doc].
Note: It is better to work with
.exists()
[Django-doc] to check if a record exists than to work with.count()
[Django-doc] since counting might have to keep looking for records after finding one.