AttributeError with User in django application

I am making an app for a clinic. I am in the finance section of my app. I want to make an income through an appointment. When I went to test this endpoint in postman, I get the following error: AttributeError: Got AttributeError when attempting to get a value for field user on serializer UserInfoSerializer. The serializer field might be named incorrectly and not match any attribute or key on the User instance. Original exception text was: 'User' object has no attribute 'user'.

I am sending you my files so that you can help me as I have had this error for a couple of days without being able to solve it.

userinfo/models.py

from django.db import models
from django.contrib.auth.models import User

class UserInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="userInfo")
address = models.CharField(max_length=255, blank=True, null=True)
phone = models.CharField(max_length=15, blank=True, null=True)
fecha_nacimiento = models.DateField(blank=True, null=True)
dni = models.CharField(max_length=10, blank=True, null=True)
postal_code = models.CharField(max_length=6, blank=True, null=True)
city = models.CharField(max_length=200, blank=True, null=True)
country = models.CharField(max_length=100, blank=True, null=True)
segundo_apellido = models.CharField(max_length=200, blank=True)

def __str__(self):
    return f"Información de {self.user.username}"`

My finanzas/models.py:

from django.db import models
from citas.models import Citas
from userinfo.models import UserInfo

class Transaccion(models.Model):
    INGRESO = 'INGRESO'
    GASTO = 'GASTO'
    INGRESO_COTIZADO = 'INGRESO_COTIZADO'

    TIPO_CHOICES = [
    (INGRESO, 'Ingreso'),
    (GASTO, 'Gasto'),
    (INGRESO_COTIZADO, 'Ingreso_Cotizado')
]

tipo = models.CharField(
    max_length=100,
    choices=TIPO_CHOICES,
    default=INGRESO,
)
monto = models.DecimalField(max_digits=10, decimal_places=2)
descripcion = models.TextField()
fecha = models.DateTimeField(auto_now_add=True)
cita = models.ForeignKey(Citas, on_delete=models.CASCADE)  # Relacionado con la cita
user = models.ForeignKey(UserInfo, on_delete=models.CASCADE)  # Relacionado con el usuario que cotiza
url = models.URLField(null=True, blank=True)

def __str__(self):
    return f"Transacción de {self.tipo} por {self.monto} para la cita {self.cita}"

class ConfiguracionFinanzas(models.Model):
precio_cita_base = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name="Precio base de la cita")
ultima_actualizacion = models.DateTimeField(auto_now=True)

def __str__(self):
    return f"Configuración de Finanzas (Precio Cita: {self.precio_cita_base})"

My views.py:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from finanzas.models import Transaccion, ConfiguracionFinanzas
from citas.models import Citas
from django.db.models import Sum
from .serializers import TransaccionSerializer, ConfiguracionFinanzasSerializer
from rest_framework.exceptions import NotFound
from datetime import datetime, timedelta
from django.utils import timezone

def obtener_transacciones_por_periodo(filtro, usuario_info):
    hoy = timezone.now()
    transacciones = Transaccion.objects.filter(usuario=usuario_info)

if filtro == 'mensual':
    inicio_mes = hoy.replace(day=1)
    transacciones = transacciones.filter(fecha__gte=inicio_mes)
elif filtro == 'trimestral':
    inicio_trimestre = hoy - timedelta(days=90)
    transacciones = transacciones.filter(fecha__gte=inicio_trimestre)
elif filtro == 'anual':
    inicio_anio = hoy.replace(month=1, day=1)
    transacciones = transacciones.filter(fecha__gte=inicio_anio)

return transacciones

def obtener_balance_por_periodo(tipo, filtro, usuario_info):
    hoy = timezone.now()
    transacciones = Transaccion.objects.filter(tipo=tipo, usuario=usuario_info)

if filtro == 'mensual':
    inicio_mes = hoy.replace(day=1)
    transacciones = transacciones.filter(fecha__gte=inicio_mes)
elif filtro == 'trimestral':
    inicio_trimestre = hoy - timedelta(days=90)
    transacciones = transacciones.filter(fecha__gte=inicio_trimestre)
elif filtro == 'anual':
    inicio_anio = hoy.replace(month=1, day=1)
    transacciones = transacciones.filter(fecha__gte=inicio_anio)

return transacciones.aggregate(Sum('monto'))['monto__sum'] or 0

class ListarGananciasCitasView(APIView):
"""
Listar las transacciones de ganancias por citas.
"""
def get(self, request):
    filtro = request.query_params.get('filtro', 'total')
    usuario_info = request.user.userInfo
    transacciones = obtener_transacciones_por_periodo(filtro, usuario_info)

    serializer = TransaccionSerializer(transacciones, many=True)
    return Response(serializer.data)

class CrearGananciasCitasView(APIView):
"""
Vista para crear una transacción de tipo 'Ingreso' o 'Gasto'.
Asocia la transacción a una cita y al usuario que la cotiza.
"""
def post(self, request, cita_id):
    if not request.user.is_authenticated:
        return Response({"detail": "Usuario no autenticado."}, status=status.HTTP_401_UNAUTHORIZED)

    try:
        cita = Citas.objects.get(id=cita_id)
    except Citas.DoesNotExist:
        raise NotFound("Cita no encontrada.")

    if cita.user != request.user:
        return Response({"detail": "No puedes modificar una cita de otro usuario."}, status=status.HTTP_403_FORBIDDEN)

    monto = request.data.get('monto')
    descripcion = request.data.get('descripcion')
    if monto is None or descripcion is None:
        return Response({"detail": "Se requieren los campos 'monto' y 'descripcion'."}, status=status.HTTP_400_BAD_REQUEST)

    tipo = 'INGRESO' if not cita.cotizada else 'INGRESO_COTIZADO'

    usuario_info = getattr(request.user, 'userInfo', None)
    if usuario_info is None:
        return Response({"detail": "Información del usuario no encontrada."}, status=status.HTTP_404_NOT_FOUND)
    transaccion = Transaccion.objects.create(
        tipo=tipo,
        monto=monto,
        descripcion=descripcion,
        cita=cita,
        user=usuario_info,
    )

    serializer = TransaccionSerializer(transaccion)
    return Response(serializer.data, status=status.HTTP_201_CREATED)

class MarcarCitaCotizadaView(APIView):
"""
Vista para marcar una cita como cotizada
"""
def post(self, request, cita_id):
    if not request.user.is_authenticated:
        return Response({"detail": "Usuario no autenticado."}, status=status.HTTP_401_UNAUTHORIZED)

    try:
        cita = Citas.objects.get(id=cita_id)
    except Citas.DoesNotExist:
        raise NotFound("Cita no encontrada.")

    if cita.usuario != request.user.userInfo:
        return Response({"detail": "No puedes modificar una cita de otro usuario."}, status=status.HTTP_403_FORBIDDEN)

    cita.cotizada = True
    cita.save()

    return Response({"message": "Cita marcada como cotizada"}, status=status.HTTP_200_OK)

class CrearGastoView(APIView):
"""
Vista para crear un gasto independiente de las citas.
"""
def post(self, request):
    if not request.user.is_authenticated:
        return Response({"detail": "Usuario no autenticado."}, status=status.HTTP_401_UNAUTHORIZED)

    monto = request.data.get('monto')
    descripcion = request.data.get('descripcion')
    if monto is None or descripcion is None:
        return Response({"detail": "Se requieren los campos 'monto' y 'descripcion'."}, status=status.HTTP_400_BAD_REQUEST)

    tipo = "GASTO"
    url = request.data.get('url', None)

    usuario_info = request.user.userInfo
    gasto = Transaccion.objects.create(
        tipo=tipo,
        monto=monto,
        descripcion=descripcion,
        usuario=usuario_info,
        url=url
    )

    serializer = TransaccionSerializer(gasto)
    return Response(serializer.data, status=status.HTTP_201_CREATED)

class ListarGastosView(APIView):
"""
Listar los gastos del usuario autenticado
"""
def get(self, request):
    filtro = request.query_params.get('filtro', 'total')
    usuario_info = request.user.userInfo
    gastos = obtener_transacciones_por_periodo(filtro, usuario_info)

    serializer = TransaccionSerializer(gastos, many=True)
    return Response(serializer.data)

class ConfiguracionFinanzasView(APIView):
"""
Obtener y actualizar la configuración de finanzas.
"""
def get(self, request):
    configuracion = ConfiguracionFinanzas.objects.first()
    if not configuracion:
        configuracion = ConfiguracionFinanzas.objects.create(precio_cita_base=0)
    serializer = ConfiguracionFinanzasSerializer(configuracion)
    return Response(serializer.data)

def put(self, request):
    configuracion = ConfiguracionFinanzas.objects.first()
    if not configuracion:
        configuracion = ConfiguracionFinanzas.objects.create(precio_cita_base=0)
    serializer = ConfiguracionFinanzasSerializer(configuracion, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class FinanzasBalanceView(APIView):
permission_classes = [IsAuthenticated]

def get(self, request):
    usuario_info = request.user.userInfo

    # Obtención de los balances por periodo
    ingresos_totales = obtener_balance_por_periodo('INGRESO', 'total', usuario_info)
    ingresos_cotizados_totales = obtener_balance_por_periodo('INGRESO_COTIZADO', 'total', usuario_info)
    gastos_totales = obtener_balance_por_periodo('GASTO', 'total', usuario_info)

    ingresos_mes = obtener_balance_por_periodo('INGRESO', 'mensual', usuario_info)
    ingresos_cotizados_mes = obtener_balance_por_periodo('INGRESO_COTIZADO', 'mensual', usuario_info)
    gastos_mes = obtener_balance_por_periodo('GASTO', 'mensual', usuario_info)

    ingresos_trimestre = obtener_balance_por_periodo('INGRESO', 'trimestral', usuario_info)
    ingresos_cotizados_trimestre = obtener_balance_por_periodo('INGRESO_COTIZADO', 'trimestral', usuario_info)
    gastos_trimestre = obtener_balance_por_periodo('GASTO', 'trimestral', usuario_info)

    ingresos_anio = obtener_balance_por_periodo('INGRESO', 'anual', usuario_info)
    ingresos_cotizados_anio = obtener_balance_por_periodo('INGRESO_COTIZADO', 'anual', usuario_info)
    gastos_anio = obtener_balance_por_periodo('GASTO', 'anual', usuario_info)

    return Response({
        'ingresos_totales': ingresos_totales,
        'ingresos_cotizados_totales': ingresos_cotizados_totales,
        'gastos_totales': gastos_totales,
        'ingresos_mes': ingresos_mes,
        'ingresos_cotizados_mes': ingresos_cotizados_mes,
        'gastos_mes': gastos_mes,
        'ingresos_trimestre': ingresos_trimestre,
        'ingresos_cotizados_trimestre': ingresos_cotizados_trimestre,
        'gastos_trimestre': gastos_trimestre,
        'ingresos_anio': ingresos_anio,
        'ingresos_cotizados_anio': ingresos_cotizados_anio,
        'gastos_anio': gastos_anio,
    })

My serializers.py

from rest_framework import serializers
from django.db import models
from finanzas.models import Transaccion, ConfiguracionFinanzas
from citas.models import Citas
from userinfo.models import UserInfo
from patients.models import Patient
from django.contrib.auth.models import User
from rest_framework.exceptions import ValidationError
from datetime import datetime


class UserSerializer(serializers.ModelSerializer):

class Meta:
    model = User
    fields = ['id', 'first_name', 'last_name', 'user']

class UserInfoSerializer(serializers.ModelSerializer):
user = UserSerializer()

class Meta:
    model = UserInfo
    fields = ['user', 'address',  'dni', 'postal_code', 'city', 'country', 'segundo_apellido']

def get_user(self, obj):
    # Asegúrate de que el objeto user esté presente y se pase correctamente
    return UserSerializer(obj.user).data if obj.user else None

class PatientSerializers(serializers.ModelSerializer):
class Meta:
    model = Patient
    fields = ['nombre', 'primer_apellido', 'segundo_apellido', 'dni', 'address', 'city', 'code_postal', 'country']

class CitaSerializers(serializers.ModelSerializer):
patient = PatientSerializers()
user = UserInfoSerializer()

class Meta:
    model = Citas
    fields = ['patient', 'fecha', 'descripcion', 'precio', 'cotizada', 'user']

def validate_precio(self, value):
    if value < 0:
        raise ValidationError("El precio de la cita no puede ser negativo.")
    return value

def validate(self, data):
    if data.get('fecha') > datetime.now():
        raise ValidationError("La fecha de la cita no puede ser en el futuro.")
    return data

class ConfiguracionFinanzasSerializer(serializers.ModelSerializer):
class Meta:
    model = ConfiguracionFinanzas
    fields = ['id', 'precio_cita_base', 'ultima_actualizacion']

def validate_precio_cita_base(self, value):
    if value < 0:
        raise ValidationError("El precio base de la cita no puede ser negativo.")
    return value

class TransaccionSerializer(serializers.ModelSerializer):
cita = CitaSerializers()
user = UserInfoSerializer()

class Meta:
    model = Transaccion
    fields = ['id', 'tipo', 'monto', 'descripcion', 'fecha', 'cita', 'user', 'url']

def validate_monto(self, value):
    if value <= 0:
        raise ValidationError("El monto de la transacción debe ser mayor que cero.")
    return value

def validate_tipo(self, value):
    if value not in ['INGRESO', 'GASTO']:
        raise ValidationError("El tipo de transacción debe ser 'INGRESO' o 'GASTO'.")
    return value

def validate(self, data):
    cita = data.get('cita')
    if not cita:
        raise ValidationError("Debe asociar una cita válida.")
    if cita.precio <= 0:
        raise ValidationError("El precio de la cita asociada debe ser mayor que cero.")

    usuario = data.get('user')
    if not usuario:
        raise ValidationError("Debe asociar un usuario válido.")
    return data

Please help me I am just starting in this world, all help is welcome.

'User' object has no attribute 'user'. is the key part of the raised exception.

Remove user from the list of fields in UserSerializer

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'first_name', 'last_name']
Вернуться на верх