Как получить набор 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)
И теперь все работает. Спасибо вам всем.