Как я могу проверить уникальность экземпляра в коллекции в фреймворке django rest

У меня есть следующие модели.

class ServerGroup(models.Model):
    name = models.SlugField(unique=True)
    factor = models.IntegerField()

class ServerGroupMember(models.Model):
    class Meta:
        unique_together = (
            ("server_group", "position"),
            ("server_group", "server"),
        )

    position = models.IntegerField()
    server_group = models.ForeignKey(
        "ServerGroup", related_name="servers", on_delete=models.CASCADE
    )
    server = models.ForeignKey("Server", on_delete=models.CASCADE)

Группа серверов имеет пару свойств, name и factor, а также набор объектов ServerGroupMember. Каждый объект ServerGroupMember содержит целое число position и ссылку на объект Server. Для данного ServerGroup position должно быть уникальным, а для данного ServerGroup server должно быть уникальным. Однако в глобальном масштабе позиция и объекты сервера не обязательно должны быть уникальными, так как в 2 группах серверов может содержаться сервер в позиции 1, и один и тот же сервер может появляться в нескольких группах серверов, но не несколько раз в одной и той же группе серверов.

Учитывая, что у меня есть следующие сериализаторы, как я могу проверить вышеизложенное? В настоящее время модель проверяет условие на уровне базы данных, но вызовет уникальную ошибку ограничения, если я попытаюсь его нарушить. Я хочу иметь возможность обнаруживать это в своих представлениях, чтобы я мог вернуть соответствующий ответ на сообщение об ошибке проверки, прежде чем он сможет попасть в БД и вызвать это исключение.

class ServerGroupMemberSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.ServerGroupMember
        fields = ("position", "server")
    server = serializers.SlugRelatedField(
        slug_name="name", queryset=models.Server.objects.all()
    )

class SrvereGroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.ServerrGroup
        fields = ("name", "factor", "servers")
    servers = ServerGroupMemberSerializer(many=True, required=False)

    def create(self, validated_data): ...
    def update(self, validated_data): ...

person Anthony Oteri    schedule 28.04.2020    source источник


Ответы (2)


Вы всегда можете проверить свои проверки при переопределении метода clean() в вашей модели или при проверке в serializers.py. Кроме того, проверка касается только ваших not ForeignKey полей, поскольку вы не можете дважды добавить ServerGroupMember member_one в ServerGroup server_test.

class ServerGroupMember(models.Model):
    class Meta:
        unique_together = (
            ("server_group", "position"),
            ("server_group", "server"),
        )

    position = models.IntegerField()
    server_group = models.ForeignKey(
        "ServerGroup", related_name="servers", on_delete=models.CASCADE
    )
    server = models.ForeignKey("Server", on_delete=models.CASCADE)

   def clean(self):
       super().clean()
       if self.position in ServerGroup.server_group_set.all().values_list('position', flat=True):
           raise ValidationError(f"Position {self.position} already exists for ServerGroup {self.server_group.name}")
person Sadraoui Taha    schedule 28.04.2020

Я нашел один способ сделать это, но все же любопытно, лучший ли это подход. Кажется, это отлично работает для создания новых экземпляров ServerGroupMember. Хотя не уверен, что это сработает и для случая с обновлением, это еще предстоит попробовать.

В представлениях при создании экземпляра сериализатора я передал конструктору сериализатора объект контекста, содержащий имя ServerGroup. Например.

def post(self, request: Request, name: str, format=None) -> Response:
    server_group = self.get_object()  # defined elsewhere
    serializer = serializers.ServerGroupMemberSerializer(
        data=request.data, context={"server_group": server_group.name}
    )
    ...

Затем в ServerGroupMemberSerializer я добавил валидаторы на уровне поля, например.

def validate_position(self, value):
    server_group = self.context.get("server_group")
    try:
        models.ServerGroupMember.objects.get(
            server_group__name=server_group, position=position
        )
    except models.ServerGroupMember.DoesNotExist:
        return value
    raise serializers.ValidationError(
        f"A server group member with position {position} already exists"
    )

def validate_server(self, value):
    ... # Follow same pattern as above
person Anthony Oteri    schedule 28.04.2020