No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Cálculo de Edad y Experiencia con Serializer Method Field en Django REST

18/21
Recursos

A veces necesitamos calcular y mostrar valores derivados en el resultado de un endpoint sin alterar el modelo de datos original. Un ejemplo común es calcular la edad de un paciente a partir de su fecha de nacimiento. Para ello, podemos utilizar el SerializerMethodField en Django REST Framework. Este campo permite realizar cálculos utilizando los datos del modelo, como veremos a continuación.

¿Cómo calculamos un valor con SerializerMethodField?

Para calcular un valor, primero debemos definir un nuevo campo en nuestro serializador usando SerializerMethodField. Este tipo de campo permite definir un método que realizará el cálculo y retornará el valor deseado. Aquí te mostramos cómo hacerlo:

  • Importa SerializerMethodField desde el módulo serializers.
  • Define un nuevo campo en el serializador, por ejemplo, “Age” para calcular la edad.
  • Si no especificas un método con el argumento method_name, Django REST Framework generará un nombre por defecto en la forma get_.

¿Cómo calculamos la edad usando la fecha de nacimiento?

La clave del cálculo es restar la fecha de nacimiento del paciente a la fecha actual. Este proceso genera un objeto timedelta, que representa la diferencia en días. Para convertirlo a años, sigue estos pasos:

  1. Importa date desde el módulo datetime, que es suficiente ya que trabajamos con fechas (no datetime).
  2. Obtén la fecha actual utilizando date.today().
  3. Calcula la diferencia entre la fecha actual y la fecha de nacimiento.
  4. Divide esta diferencia en días por 365 para obtener la edad aproximada en años.
  5. Retorna el valor numérico o, si es necesario, formatea el resultado como un string.

Ejemplo de código:

from rest_framework import serializers
from datetime import date

class PatientSerializer(serializers.ModelSerializer):
    age = serializers.SerializerMethodField()

    def get_age(self, obj):
        today = date.today()
        age_timedelta = today - obj.date_of_birth
        age = age_timedelta.days // 365  # Convertimos días a años
        return age

    class Meta:
        model = Patient
        fields = ['name', 'date_of_birth', 'age']

¿Qué sucede si obtenemos resultados incorrectos?

Un problema común al calcular la edad es no acceder correctamente al atributo days del objeto timedelta. Si simplemente restamos las fechas, obtendremos un objeto timedelta, que necesitamos dividir por 365 para convertirlo en años.

Otro detalle importante es no incluir texto como “años” en el resultado, ya que es preferible dejar el formato de presentación (e.g., el idioma) en manos del frontend.

¿Cómo calculamos la experiencia de un doctor?

Siguiendo el mismo patrón que para calcular la edad, podemos calcular la experiencia de un doctor usando su fecha de inicio de trabajo. Solo es necesario reemplazar la fecha de nacimiento con la fecha de inicio laboral.

Ejemplo de código para la experiencia:

class DoctorSerializer(serializers.ModelSerializer):
    experience = serializers.SerializerMethodField()

    def get_experience(self, obj):
        today = date.today()
        experience_timedelta = today - obj.start_date
        experience = experience_timedelta.days // 365
        return experience

    class Meta:
        model = Doctor
        fields = ['name', 'start_date', 'experience']

¿Qué otras aplicaciones tiene el SerializerMethodField?

  • Calcular otros valores derivados sin alterar el modelo de datos.
  • Agregar lógica personalizada en el serializador sin tocar la base de datos.
  • Permitir mostrar valores preprocesados para el frontend sin requerir cambios en el backend.

Aportes 5

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Adapatado para la experiencia de un doctor: ```js from datetime import date from rest_framework import serializers from .models import Doctor class DoctorSerializer(serializers.ModelSerializer): experience_years = serializers.SerializerMethodField() # Campo personalizado para años de experiencia class Meta: model = Doctor fields = [ 'first_name', 'last_name', 'qualification', 'email', 'start_date', 'experience_years' # Incluir el campo calculado en la salida ] def get_experience_years(self, obj): experience_td = date.today() - obj.start_date # Calcula la diferencia entre hoy y la fecha de inicio years = experience_td.days // 365 # Convierte los días en años return f"{years} años" # Retorna los años de experiencia con el texto "años" ```
**Que muestre años, meses y dias** ```python def get_age(self, obj): # Muestar los años que tiene el paciente # age_td = date.today() - obj.date_of_birth # years = age_td.days // 365 # return f"{years} años" # Muestar los años, meses y dias que tiene el paciente age_td = date.today() - obj.date_of_birth years = age_td.days // 365 remaining_days = age_td.days % 365 months = remaining_days // 30 days = remaining_days % 30 return f"{years} años, {months} meses y {days} días" ```def get\_age(self, obj):    # Muestar los años que tiene el paciente        # age\_td = date.today() - obj.date\_of\_birth        # years = age\_td.days // 365        # return f"{years} años"     # Muestar los años, meses y dias que tiene el paciente        age\_td = date.today() - obj.date\_of\_birth        years = age\_td.days // 365        remaining\_days = age\_td.days % 365        months = remaining\_days // 30        days = remaining\_days % 30        return f"{years} años, {months} meses y {days} días"
My resume, important put DoctorAvailabilitySerializer before DoctorSerializer: ```js class DoctorAvailabilitySerializer(serializers.ModelSerializer): class Meta: model = DoctorAvailability fields = "__all__" class DoctorSerializer(serializers.ModelSerializer): appointments = AppointmentSerializer(many=True, read_only=True) # >>> from datetime import date, time # >>> from doctors.models import Doctor # >>> from .models import DoctorAvailability # >>> doctor = Doctor.objects.first() # >>> DoctorAvailability.objects.create( # ... doctor=doctor, # ... start_date=date(2022, 12, 5), # ... end_date=date(2030, 12, 5), # ... start_time=time(9, 0), # ... end_time=time(17, 0) # ... ) availabilities = DoctorAvailabilitySerializer(many=True, read_only=True) years_of_experience = serializers.SerializerMethodField() class Meta: model = Doctor fields = [ "id", "first_name", "last_name", "qualification", "years_of_experience", "contact_number", "email", "address", "biography", "is_on_vacation", "availabilities", "appointments", ] def get_years_of_experience(self, obj): first_availability = obj.availabilities.order_by("start_date").first() if first_availability: return (datetime.now().date() - first_availability.start_date).days // 365 return 0 ```
Relacioné los modelos de DoctorAvailability y Doctor para obtener la fecha de inicio del doctor y modifique el resultado de tiempo de experiencia para hacerlo más específico implementando la librería de python-dateutil ```js from datetime import date, datetime from dateutil.relativedelta import relativedelta class DoctorSerializer(serializers.ModelSerializer): # Relacionar con el serializador de DoctorAvailability availabilities = DoctorAvailabilitySerializer(many=True, read_only=True) experience_years = serializers.SerializerMethodField() class Meta: model = Doctor fields = [ 'id', 'first_name', 'last_name', 'qualification', 'contact_number', 'email', 'address', 'biography', 'is_on_vacation', 'availabilities', 'experience_years', ] def get_experience_years(self, obj): first_availability = obj.availabilities.order_by('start_date').first() if first_availability: # Calcular la diferencia entre la fecha actual y la primera disponibilidad now = datetime.now() dob = datetime.combine(first_availability.start_date, datetime.min.time()) experience_time_delta = relativedelta(now, dob) # Obtener los años, meses y días years = experience_time_delta.years months = experience_time_delta.months days = experience_time_delta.days return f'{years} años, {months} meses, {days} días' else: # En caso de que no tenga disponibilidad, retornar 'Sin experiencia' return 'Sin experiencia' ```Y así ajuste el serializador de PatientSerializer ```js def get_age(self, obj): # Combinar con tiempo para obtener datetime completo dob = datetime.combine(obj.date_of_birth, datetime.min.time()) # Calcular la diferencia entre la fecha de nacimiento y el momento actual age_time_delta = relativedelta(date.today(), dob) # Obtener los años, meses, días y horas years = age_time_delta.years months = age_time_delta.months days = age_time_delta.days return f'{years} años, {months} meses, {days} días' ```
Asi quedo el SerializerMetodField para calcular la experiencia de los doctores. Esta hecha basada en el modelo DoctorAvailability pero seguramente se puede hacer mucho mejor desde el modelo Doctor. `class DoctorAvailabilitySerializer(serializers.ModelSerializer): experience = serializers.SerializerMethodField()` ` class Meta: model = DoctorAvailability fields = [ 'doctor', 'experience', 'start_date', 'end_date', 'start_time', 'end_time', ]` ` def get_experience(self, obj): """ Return the doctor experience in years """` ` # get age in days (timedelta) experience_timedelta = date.today() - obj.start_date years = experience_timedelta.days // 365 return f"{years}"` y se ve de esta manera: ![](https://ajha.info/images/doctorAvailabilityDetail.png)