Как получить набор queryset с данными для 2 таблиц?

Я совсем новичок в Django. Я пишу приложение, используя Vue3 во фронтенде и Django5 в бекенде. Я уже реализовал вход в систему, но хочу, чтобы возвращаемый пользователь содержал роль, которая находится в другой таблице, а я просто получаю id.

Мои модели Django:

class Role(models.Model):
    id_role = models.AutoField(primary_key=True)
    name = models.TextField(max_length=100)
    permissions = models.ManyToManyField(Permission)

    def __str__(self):
        return self.name

class User(models.Model):
    id_user = models.AutoField(primary_key=True)
    email = models.TextField(max_length=100)
    password = models.CharField(max_length=255)
    role_id = models.ForeignKey(Role, on_delete=models.PROTECT)
    locale = models.TextField(max_length=5)

    def __str__(self):
        return self.email

Сериализаторы:

class RoleSerializer(ModelSerializer):
    class Meta:
        model = Role
        fields = (
            'id_role', 'name', 'permissions'
        )

class UserSerializer(ModelSerializer):
    class Meta:
        model = User
        fields = (
            'id_user', 'email', 'password', 'role_id', 'locale'
        )

Это мой набор представлений, где у меня есть метод login, и я хочу, чтобы он возвращал имя роли вместо role_id:

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.all()

    @action(detail=False, methods=['post'])
    def login(self, request, pk=None):
        user = serializers.serialize('json', self.queryset.filter(email=request.data["email"], password=request.data["password"]))
        if (user and user != "[]"):
            return Response(user)
        else:
            return Response({"error": "The user does not exist"},
                            status=status.HTTP_400_BAD_REQUEST)

Я уже изучил вопросы SO (Как сделать Inner Join в django? ) и попробовал использовать этот код:

@action(detail=False, methods=['post'])
    def login(self, request, pk=None):
        queryset = User.objects.select_related('roles')
        user = serializers.serialize('json', queryset.filter(email=request.data["email"], password=request.data["password"]))
        if (user and user != "[]"):
            return Response(user)
        else:
            return Response({"error": "The user does not exist"},
                            status=status.HTTP_400_BAD_REQUEST)

но я просто получаю эту ошибку:

File "/usr/local/lib/python3.12/site-packages/django/db/models/sql/compiler.py", line 1367, in get_related_selections
     raise FieldError(
     django.core.exceptions.FieldError: Invalid field name(s) given in select_related: 'roles'. Choices are: role_id

Когда я пытаюсь использовать role_id, я просто получаю тот же ответ, что и в начале. Я не знаю, что я делаю неправильно.


EDIT: Я только что попробовал этот ответ (https://stackoverflow.com/a/48341567/4639580), но получил:

FieldError at /api/users/login/
Cannot resolve keyword 'role' into field. Choices are: email, id_user, locale, password, person, role_id, role_id_id
Request Method: POST
Request URL:    http://localhost:8000/api/users/login/
Django Version: 5.0.2
Exception Type: FieldError
Exception Value:    
Cannot resolve keyword 'role' into field. Choices are: email, id_user, locale, password, person, role_id, role_id_id
Exception Location: /usr/local/lib/python3.12/site-packages/django/db/models/sql/query.py, line 1772, in names_to_path

Вы можете использовать метод select_related для выполнения внутреннего соединения между таблицами.

from django.shortcuts import get_object_or_404

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.select_related('role_id')

    @action(detail=False, methods=['post'])
    def login(self, request, pk=None):
        email = request.data["email"]
        password = request.data["password"]

        user = get_object_or_404(self.queryset, email=email, password=password)
        serializer = self.get_serializer(user)
        return Response(serializer.data)

В приведенном выше коде select_related('role_id') используется для получения связанного объекта Role для каждого объекта User в наборе запросов. Это устраняет необходимость в дополнительном запросе к базе данных при обращении к полю role_id.

Обратите внимание, что вам нужно использовать имя поля role_id вместо roles, потому что поле внешнего ключа в модели User определено как role_id.

тогда объект пользователя сериализуется с помощью сериализатора, определенного для модели User, и сериализованные данные возвращаются в ответе.

Вы можете использовать метод select_related для выполнения внутреннего соединения между таблицами.

from django.shortcuts import get_object_or_404

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.select_related('role_id')

    @action(detail=False, methods=['post'])
    def login(self, request, pk=None):
        email = request.data["email"]
        password = request.data["password"]

        user = get_object_or_404(self.queryset, email=email, password=password)
        serializer = self.get_serializer(user)
        return Response(serializer.data)

В приведенном выше коде select_related('role_id') используется для получения связанного объекта Role для каждого объекта User в наборе запросов. Это устраняет необходимость в дополнительном запросе к базе данных при обращении к полю role_id.

Обратите внимание, что вам нужно использовать имя поля role_id вместо roles, потому что поле внешнего ключа в модели User определено как role_id.

тогда объект пользователя сериализуется с помощью сериализатора, определенного для модели User, и сериализованные данные возвращаются в ответе.

Для тех, у кого такая же проблема, и кто не находит решения в select_related. Пожалуйста, убедитесь, что у вас есть необходимые вложенные сериализаторы. В моем случае ошибка заключалась в том, что я пропустил вложенный сериализатор для ролевой модели.

Вот что я сделал:

Мои модели:

class RoleSerializer(ModelSerializer):
    class Meta:
        model = Role
        fields = (
            'id_role', 'name', 'permissions'
        )

class UserSerializer(ModelSerializer):
    role_id = RoleSerializer()

    class Meta:
        model = User
        fields = (
            'id_user', 'email', 'password', 'role_id', 'locale'
        )

Мое мнение:

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.select_related('role_id').all()

    @action(detail=False, methods=['post'])
    def login(self, request, pk=None):
        email=request.data["email"]
        password=request.data["password"]

        user = get_object_or_404(self.queryset, email=email, password=password)
        serializer = self.get_serializer(user)
        return Response(serializer.data)

И теперь все работает. Спасибо вам всем.

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