Как передать параметры связанных объектов в сериализатор основных объектов

У меня есть объект 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 the User model [Django-doc] directly. For more information you can see the referencing the User 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.

Вернуться на верх