From 5aca9e77b17a2cb1a2bf7e01c69f6aca5c804263 Mon Sep 17 00:00:00 2001 From: RafaUC Date: Mon, 29 Jul 2024 11:21:11 -0600 Subject: [PATCH 1/3] modificando serializers --- cosiap_api/dynamic_forms/models.py | 15 +- cosiap_api/dynamic_forms/serializers.py | 362 ++++++++++++++++++++++++ cosiap_api/dynamic_forms/views.py | 49 +++- cosiap_api/modalidades/models.py | 5 +- 4 files changed, 423 insertions(+), 8 deletions(-) create mode 100644 cosiap_api/dynamic_forms/serializers.py diff --git a/cosiap_api/dynamic_forms/models.py b/cosiap_api/dynamic_forms/models.py index a439c3e..4358119 100644 --- a/cosiap_api/dynamic_forms/models.py +++ b/cosiap_api/dynamic_forms/models.py @@ -4,6 +4,7 @@ from model_utils.managers import InheritanceManager from django.db import IntegrityError from dynamic_formats.models import DynamicFormat from common.nombres_archivos import nombre_archivo_respuesta_doc +from django.core.exceptions import ValidationError class Opcion(models.Model): """ @@ -165,15 +166,15 @@ class Respuesta(models.Model): objects = InheritanceManager() def __str__(self): - return f"Respuesta {type(self)} - Elemento: {self.elemento} - Solicitante: {self.solicitante_id}" + return f"Respuesta {type(self)} - Elemento: {self.elemento} - Solicitud: {self.solicitud_id}" def save(self, *args, **kwargs): if self._state.adding: # Solo realiza la verificación si estás creando una respuesta nueva if self.elemento.seccion.tipo == 'unico': # Verificar si ya existe una respuesta para esta combinación - if Respuesta.objects.filter(elemento=self.elemento, solicitante=self.solicitante).exists(): - raise IntegrityError('Ya existe una respuesta para este elemento y solicitante') + if Respuesta.objects.filter(elemento=self.elemento, solicitud=self.solicitud).exists(): + raise IntegrityError('Ya existe una respuesta para este elemento y solicitud') super().save(*args, **kwargs) class Meta: @@ -185,7 +186,7 @@ class Respuesta(models.Model): return 'Respuesta no Implementado' -class RNumerico(Respuesta): +class RNumerico(models.Model): valor = models.CharField(max_length=255, null=True, blank=True) def getStringValue(self): @@ -195,6 +196,12 @@ class RNumerico(Respuesta): return str(self.valor) class Meta: verbose_name_plural = '10. R Numericos' + + def clean(self): + super().clean() + obligatorio = self.elemento.obligatorio + if (not self.valor or not self.valor.strip()) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") class RTextoCorto(Respuesta): diff --git a/cosiap_api/dynamic_forms/serializers.py b/cosiap_api/dynamic_forms/serializers.py new file mode 100644 index 0000000..75d14a3 --- /dev/null +++ b/cosiap_api/dynamic_forms/serializers.py @@ -0,0 +1,362 @@ +from rest_framework import serializers +from dynamic_forms.models import ( + Opcion, Elemento, ElementosOpciones, Seccion, SeccionesElementos, + DynamicForm, DynamicFormsSecciones, RAgregacion, Respuesta, RNumerico, + RTextoCorto, RTextoParrafo, RHora, RFecha, ROpcionMultiple, RCasillas, + RDesplegable, RDocumento +) +from django.core.validators import MinLengthValidator, MaxLengthValidator + +# Serializer para Opcion +class OpcionSerializer(serializers.ModelSerializer): + class Meta: + model = Opcion + fields = '__all__' + + def create(self, validated_data): + return Opcion.objects.create(**validated_data) + + def update(self, instance, validated_data): + instance.nombre = validated_data.get('nombre', instance.nombre) + instance.save() + return instance + +# Serializer para ElementosOpciones +class ElementosOpcionesSerializer(serializers.ModelSerializer): + opcion = OpcionSerializer() + + class Meta: + model = ElementosOpciones + fields = '__all__' + + def create(self, validated_data): + opcion_data = validated_data.pop('opcion') + opcion = Opcion.objects.create(**opcion_data) + elemento_opcion = ElementosOpciones.objects.create(opcion=opcion, **validated_data) + return elemento_opcion + + def update(self, instance, validated_data): + opcion_data = validated_data.pop('opcion') + opcion, _ = Opcion.objects.update_or_create(**opcion_data) + instance.opcion = opcion + instance.save() + return instance + +# Serializer para Elemento +class ElementoSerializer(serializers.ModelSerializer): + opciones = ElementosOpcionesSerializer(many=True) + + class Meta: + model = Elemento + fields = '__all__' + + def create(self, validated_data): + opciones_data = validated_data.pop('opciones', []) + elemento = Elemento.objects.create(**validated_data) + for opcion_data in opciones_data: + ElementosOpcionesSerializer().create({'opcion': opcion_data['opcion'], **opcion_data}) + return elemento + + def update(self, instance, validated_data): + opciones_data = validated_data.pop('opciones', []) + instance.nombre = validated_data.get('nombre', instance.nombre) + instance.save() + + existing_opciones_ids = set(instance.opciones.values_list('id', flat=True)) + new_opciones_ids = set() + + for opcion_data in opciones_data: + opcion, _ = Opcion.objects.update_or_create(**opcion_data.pop('opcion')) + elemento_opcion, _ = ElementosOpciones.objects.update_or_create( + opcion=opcion, + defaults=opcion_data + ) + new_opciones_ids.add(elemento_opcion.id) + + instance.opciones.exclude(id__in=new_opciones_ids).delete() + return instance + +# Serializer para SeccionesElementos +class SeccionesElementosSerializer(serializers.ModelSerializer): + elemento = ElementoSerializer() + + class Meta: + model = SeccionesElementos + fields = '__all__' + + def create(self, validated_data): + elemento_data = validated_data.pop('elemento') + elemento = ElementoSerializer().create(elemento_data) + seccion_elemento = SeccionesElementos.objects.create(elemento=elemento, **validated_data) + return seccion_elemento + + def update(self, instance, validated_data): + elemento_data = validated_data.pop('elemento') + elemento = ElementoSerializer().update(instance.elemento, elemento_data) + instance.elemento = elemento + instance.save() + return instance + +# Serializer para Seccion +class SeccionSerializer(serializers.ModelSerializer): + elementos = SeccionesElementosSerializer(many=True) + + class Meta: + model = Seccion + fields = '__all__' + + def create(self, validated_data): + elementos_data = validated_data.pop('elementos', []) + seccion = Seccion.objects.create(**validated_data) + for elemento_data in elementos_data: + SeccionesElementosSerializer().create({'elemento': elemento_data['elemento'], **elemento_data}) + return seccion + + def update(self, instance, validated_data): + elementos_data = validated_data.pop('elementos', []) + instance.nombre = validated_data.get('nombre', instance.nombre) + instance.save() + + existing_elementos_ids = set(instance.elementos.values_list('id', flat=True)) + new_elementos_ids = set() + + for elemento_data in elementos_data: + elemento, _ = Elemento.objects.update_or_create(**elemento_data.pop('elemento')) + seccion_elemento, _ = SeccionesElementos.objects.update_or_create( + elemento=elemento, + defaults=elemento_data + ) + new_elementos_ids.add(seccion_elemento.id) + + instance.elementos.exclude(id__in=new_elementos_ids).delete() + return instance + +# Serializer para DynamicFormsSecciones +class DynamicFormsSeccionesSerializer(serializers.ModelSerializer): + seccion = SeccionSerializer() + + class Meta: + model = DynamicFormsSecciones + fields = '__all__' + + def create(self, validated_data): + seccion_data = validated_data.pop('seccion') + seccion = SeccionSerializer().create(seccion_data) + dynamic_form_seccion = DynamicFormsSecciones.objects.create(seccion=seccion, **validated_data) + return dynamic_form_seccion + + def update(self, instance, validated_data): + seccion_data = validated_data.pop('seccion') + seccion = SeccionSerializer().update(instance.seccion, seccion_data) + instance.seccion = seccion + instance.save() + return instance + +# Serializer para DynamicForm +class DynamicFormSerializer(serializers.ModelSerializer): + secciones = DynamicFormsSeccionesSerializer(many=True) + + class Meta: + model = DynamicForm + fields = '__all__' + + def create(self, validated_data): + secciones_data = validated_data.pop('secciones', []) + dynamic_form = DynamicForm.objects.create(**validated_data) + for seccion_data in secciones_data: + DynamicFormsSeccionesSerializer().create({'seccion': seccion_data['seccion'], **seccion_data}) + return dynamic_form + + def update(self, instance, validated_data): + secciones_data = validated_data.pop('secciones', []) + instance.nombre = validated_data.get('nombre', instance.nombre) + instance.save() + + existing_secciones_ids = set(instance.secciones.values_list('id', flat=True)) + new_secciones_ids = set() + + for seccion_data in secciones_data: + seccion, _ = Seccion.objects.update_or_create(**seccion_data.pop('seccion')) + dynamic_form_seccion, _ = DynamicFormsSecciones.objects.update_or_create( + seccion=seccion, + defaults=seccion_data + ) + new_secciones_ids.add(dynamic_form_seccion.id) + + instance.secciones.exclude(id__in=new_secciones_ids).delete() + return instance + +# Serializers para las respuestas +class RespuestaSerializer(serializers.ModelSerializer): + class Meta: + model = Respuesta + fields = [] + + def __init__(self, *args, **kwargs): + elemento = kwargs.pop("elemento", None) + solicitud = kwargs.pop("solicitud", None) + super().__init__(*args, **kwargs) + if elemento and solicitud: + self.instance.solicitud = solicitud + self.instance.elemento = elemento + +class RNumericoSerializer(RespuestaSerializer): + valor = serializers.CharField(required=True) + + class Meta: + model = RNumerico + fields = ['valor'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + elemento = self.instance.elemento if self.instance else None + numMin = elemento.numMin if elemento else None + numMax = elemento.numMax if elemento else None + if numMin is not None: + self.fields['valor'].validators.append(MinLengthValidator(numMin)) + if numMax is not None: + self.fields['valor'].validators.append(MaxLengthValidator(numMax)) + + + +class RTextoCortoSerializer(RespuestaSerializer): + texto = serializers.CharField(required=True) + + class Meta: + model = RTextoCorto + fields = ['texto'] + + def validate_texto(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and (not value or not value.strip()): + raise serializers.ValidationError("Este campo es Obligatorio.") + return value + +class RTextoParrafoSerializer(RespuestaSerializer): + texto = serializers.CharField(required=True) + + class Meta: + model = RTextoParrafo + fields = ['texto'] + + def validate_texto(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and (not value or not value.strip()): + raise serializers.ValidationError("Este campo es Obligatorio.") + return value + +class RHoraSerializer(RespuestaSerializer): + hora = serializers.TimeField(required=True) + + class Meta: + model = RHora + fields = ['hora'] + + def validate_hora(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and not value: + raise serializers.ValidationError("Este campo es Obligatorio.") + return value + +class RFechaSerializer(RespuestaSerializer): + fecha = serializers.DateField(required=True) + + class Meta: + model = RFecha + fields = ['fecha'] + + def validate_fecha(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and not value: + raise serializers.ValidationError("Este campo es Obligatorio.") + return value + +class ROpcionMultipleSerializer(RespuestaSerializer): + valor = serializers.CharField(required=True) + otro = serializers.CharField(required=False, allow_blank=True) + + class Meta: + model = ROpcionMultiple + fields = ['valor', 'otro'] + + def validate(self, data): + elemento = self.instance.elemento + obligatorio = elemento.obligatorio + opcOtro = elemento.opcionOtro + respuesta = data.get('valor') + otro = data.get('otro') + + if obligatorio: + if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta == 'Otro'): + raise serializers.ValidationError("Este campo es obligatorio") + if (otro and otro.strip()) and (respuesta != 'Otro'): + raise serializers.ValidationError("No está seleccionada la opción 'Otro'") + if respuesta == 'Otro' and not (otro and otro.strip()): + raise serializers.ValidationError("Si eliges 'Otro', debes proporcionar más detalles en el campo 'Otro'.") + return data + + def validate_valor(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and not value: + raise serializers.ValidationError("Este campo es Obligatorio.") + return value + +class RCasillasSerializer(RespuestaSerializer): + valor = serializers.ListField(child=serializers.CharField()) + otro = serializers.CharField(required=False, allow_blank=True) + + class Meta: + model = RCasillas + fields = ['valor', 'otro'] + + def validate(self, data): + elemento = self.instance.elemento + obligatorio = elemento.obligatorio + opcOtro = elemento.opcionOtro + respuesta = data.get('valor') + otro = data.get('otro') + + if obligatorio: + if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and ('Otro' in respuesta): + raise serializers.ValidationError("Este campo es obligatorio") + if (otro and otro.strip()) and ('Otro' not in respuesta): + raise serializers.ValidationError("No está seleccionada la opción 'Otro'") + if 'Otro' in respuesta and not (otro and otro.strip()): + raise serializers.ValidationError("Si eliges 'Otro', debes proporcionar más detalles en el campo 'Otro'.") + return data + + def validate_valor(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and not value: + raise serializers.ValidationError("Este campo es Obligatorio.") + return value + +class RDesplegableSerializer(RespuestaSerializer): + valor = serializers.CharField(required=True) + otro = serializers.CharField(required=False, allow_blank=True) + + class Meta: + model = RDesplegable + fields = ['valor', 'otro'] + + def validate(self, data): + elemento = self.instance.elemento + obligatorio = elemento.obligatorio + opcOtro = elemento.opcionOtro + respuesta = data.get('valor') + otro = data.get('otro') + + if obligatorio: + if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta == 'Otro'): + raise serializers.ValidationError("Este campo es obligatorio") + if (otro and otro.strip()) and (respuesta != 'Otro'): + raise serializers.ValidationError("No está seleccionada la opción 'Otro'") + if respuesta == 'Otro' and not (otro and otro.strip()): + raise serializers.ValidationError("Si eliges 'Otro', debes proporcionar más detalles en el campo 'Otro'.") + return data + + def validate_valor(self, value): + elemento = self.instance.elemento + if elemento.obligatorio and not value: + raise serializers.ValidationError("Este campo es Obligatorio.") + return value diff --git a/cosiap_api/dynamic_forms/views.py b/cosiap_api/dynamic_forms/views.py index 91ea44a..b43cd1f 100644 --- a/cosiap_api/dynamic_forms/views.py +++ b/cosiap_api/dynamic_forms/views.py @@ -1,3 +1,48 @@ -from django.shortcuts import render +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework import status +from django.shortcuts import get_object_or_404 +from dynamic_forms.models import DynamicForm +from dynamic_forms.serializers import DynamicFormSerializer +from common.views import BasePermissionAPIView -# Create your views here. +class DynamicFormAPIView(BasePermissionAPIView): + ''' + Clase que maneja las solicitudes de los recursos de Modalidad + + Tipos de solicitud: + - GET (Obtiene toda la lista de modalidades o una modalidad específica) + - POST (Crea una nueva modalidad) + - PUT (Actualizar los datos de alguna modalidad existente) + - DELETE (Si bien no se permiten eliminar las modalidades, este método la archivará en su lugar) + ''' + + def get(self, request, pk=None): + if pk: + dynamic_form = get_object_or_404(DynamicForm, pk=pk) + serializer = DynamicFormSerializer(dynamic_form) + return Response(serializer.data) + else: + dynamic_forms = DynamicForm.objects.all() + serializer = DynamicFormSerializer(dynamic_forms, many=True) + return Response(serializer.data) + + def post(self, request): + serializer = DynamicFormSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + dynamic_form = get_object_or_404(DynamicForm, pk=pk) + serializer = DynamicFormSerializer(dynamic_form, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + dynamic_form = get_object_or_404(DynamicForm, pk=pk) + dynamic_form.delete() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/cosiap_api/modalidades/models.py b/cosiap_api/modalidades/models.py index f50cdef..06cb413 100644 --- a/cosiap_api/modalidades/models.py +++ b/cosiap_api/modalidades/models.py @@ -5,6 +5,7 @@ from django.utils import timezone import os from django.db.models.signals import pre_save from django.dispatch import receiver +from dynamic_forms.models import DynamicForm class Modalidad(models.Model): @@ -24,7 +25,7 @@ class Modalidad(models.Model): descripcion = models.TextField(verbose_name="Descripción", null=False) mostrar = models.BooleanField(default=True) archivado = models.BooleanField(default=False) - dynamic_form = None + dynamic_form = models.ForeignKey(DynamicForm, on_delete=models.SET_NULL, verbose_name="Modalidad") def __str__(self): return f'{self.nombre}' @@ -34,7 +35,7 @@ class Modalidad(models.Model): @receiver(pre_save, sender=Modalidad) def borrar_imagen_vieja(sender, instance, **kwargs): - #sie el objeto ya existe (es un update), removemos la imagen vieja + #si el objeto ya existe (es un update), removemos la imagen vieja if instance.pk: try: old_instance = Modalidad.objects.get(pk=instance.pk) -- GitLab From b6de428427a94da41bf975f0e37cb674cd59b536 Mon Sep 17 00:00:00 2001 From: RafaUC Date: Thu, 1 Aug 2024 12:22:42 -0600 Subject: [PATCH 2/3] dynamic_forms snapshot --- cosiap_api/common/custom_tests.py | 2 +- cosiap_api/common/serializers.py | 13 + cosiap_api/cosiap_api/urls.py | 2 + cosiap_api/dynamic_forms/models.py | 96 ++++- cosiap_api/dynamic_forms/serializers.py | 190 ++-------- cosiap_api/dynamic_forms/tests.py | 94 ++++- cosiap_api/dynamic_forms/urls.py | 33 ++ cosiap_api/dynamic_forms/views.py | 338 +++++++++++++++++- .../0002_relacion_modalidad_dynamicForm.py | 20 ++ cosiap_api/modalidades/models.py | 3 +- 10 files changed, 597 insertions(+), 194 deletions(-) create mode 100644 cosiap_api/common/serializers.py create mode 100644 cosiap_api/dynamic_forms/urls.py create mode 100644 cosiap_api/modalidades/migrations/0002_relacion_modalidad_dynamicForm.py diff --git a/cosiap_api/common/custom_tests.py b/cosiap_api/common/custom_tests.py index b1b5029..abe198b 100644 --- a/cosiap_api/common/custom_tests.py +++ b/cosiap_api/common/custom_tests.py @@ -63,7 +63,7 @@ class BasePerUserTestCase(APITestCase): RFC='1234567890123', # Ajustar tipo de dato direccion='Calle sin Nombre', codigo_postal='89890', # Ajustar tipo de dato - municipio_id=1, + municipio_id=2, poblacion=5, INE='awdawd' ) diff --git a/cosiap_api/common/serializers.py b/cosiap_api/common/serializers.py new file mode 100644 index 0000000..5db5750 --- /dev/null +++ b/cosiap_api/common/serializers.py @@ -0,0 +1,13 @@ +from rest_framework import serializers + +class DynamicModelSerializer(serializers.ModelSerializer): + def __init__(self, *args, **kwargs): + # Expecting 'model' to be passed in kwargs + model = kwargs.pop('model', None) + if model: + self.Meta.model = model + super().__init__(*args, **kwargs) + + class Meta: + model = None + fields = '__all__' \ No newline at end of file diff --git a/cosiap_api/cosiap_api/urls.py b/cosiap_api/cosiap_api/urls.py index a08789b..b881abe 100644 --- a/cosiap_api/cosiap_api/urls.py +++ b/cosiap_api/cosiap_api/urls.py @@ -28,6 +28,8 @@ urlpatterns = [ path('api/notificaciones/',include('notificaciones.urls')), path('api/solicitudes/',include('solicitudes.urls')), path('api/dynamic-tables/',include('dynamic_tables.urls')), + path('api/formularios/',include('dynamic_forms.urls')), + # API Doc UI: path('api/schema/', SpectacularAPIView.as_view(), name='schema'), diff --git a/cosiap_api/dynamic_forms/models.py b/cosiap_api/dynamic_forms/models.py index 4358119..4b41739 100644 --- a/cosiap_api/dynamic_forms/models.py +++ b/cosiap_api/dynamic_forms/models.py @@ -5,6 +5,7 @@ from django.db import IntegrityError from dynamic_formats.models import DynamicFormat from common.nombres_archivos import nombre_archivo_respuesta_doc from django.core.exceptions import ValidationError +from django.core.validators import MinLengthValidator, MaxLengthValidator class Opcion(models.Model): """ @@ -186,7 +187,7 @@ class Respuesta(models.Model): return 'Respuesta no Implementado' -class RNumerico(models.Model): +class RNumerico(Respuesta): valor = models.CharField(max_length=255, null=True, blank=True) def getStringValue(self): @@ -197,6 +198,19 @@ class RNumerico(models.Model): class Meta: verbose_name_plural = '10. R Numericos' + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Accede a la instancia de elemento y obtén los valores numMin y numMax + elemento = self.elemento if self.pk else None + numMin = elemento.numMin if elemento else None + numMax = elemento.numMax if elemento else None + # Configura validadores de longitud mínima y máxima en el campo 'valor' + if numMin is not None: + self._meta.get_field('valor').validators.append(MinLengthValidator(numMin)) + if numMax is not None: + self._meta.get_field('valor').validators.append(MaxLengthValidator(numMax)) + + def clean(self): super().clean() obligatorio = self.elemento.obligatorio @@ -215,6 +229,12 @@ class RTextoCorto(Respuesta): class Meta: verbose_name_plural = '11. R TextoCortos' + def clean(self): + super().clean() + obligatorio = self.elemento.obligatorio + if (not self.valor or not self.valor.strip()) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + class RTextoParrafo(Respuesta): valor = models.TextField(null=True, blank=True) @@ -227,6 +247,12 @@ class RTextoParrafo(Respuesta): class Meta: verbose_name_plural = '12. R TextoParrafos' + def clean(self): + super().clean() + obligatorio = self.elemento.obligatorio + if (not self.valor or not self.valor.strip()) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + class RHora(Respuesta): valor = models.TimeField(null=True, blank=True) @@ -239,6 +265,12 @@ class RHora(Respuesta): class Meta: verbose_name_plural = '13. R Horas' + def clean(self): + super().clean() + obligatorio = self.elemento.obligatorio + if (not self.valor) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + class RFecha(Respuesta): valor = models.DateField(null=True, blank=True) @@ -251,6 +283,12 @@ class RFecha(Respuesta): class Meta: verbose_name_plural = '14. R Fechas' + def clean(self): + super().clean() + obligatorio = self.elemento.obligatorio + if (not self.valor) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + class ROpcionMultiple(Respuesta): valor = models.ForeignKey(Opcion, on_delete=models.CASCADE, null=True, blank=True) otro = models.CharField(max_length=255, verbose_name="Otro", null=True, blank=True) @@ -266,6 +304,22 @@ class ROpcionMultiple(Respuesta): class Meta: verbose_name_plural = '15. R Opcion Multiples' + def clean(self): + super().clean() + obligatorio = self.elemento.obligatorio + opcOtro = self.elemento.opcion_otro + respuesta = self.valor + otro = self.otro + if (not respuesta) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + if obligatorio: + if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta and respuesta.nombre == 'Otro'): + raise ValidationError("Este campo es obligatorio") + if (otro and otro.strip()) and (respuesta and not respuesta.nombre == 'Otro'): + raise ValidationError("No esta seleccionada opcion Otro") + if respuesta and (respuesta.nombre == 'Otro' and not( otro and otro.strip())): + raise ValidationError("Si eliges 'otro', debes proporcionar más detalles en el campo 'otro'.") + class RCasillas(Respuesta): valor = models.ManyToManyField(Opcion, blank=True) otro = models.CharField(max_length=255, verbose_name="Otro", null=True, blank=True) @@ -285,6 +339,23 @@ class RCasillas(Respuesta): return string class Meta: verbose_name_plural = '16. R Casillas' + + def clean(self): + super().clean() + respuesta = self.valor + noRespuesta = respuesta.count() == 0 + obligatorio = self.elemento.obligatorio + opcOtro = self.elemento.opcionOtro + otro = self.otro + if (noRespuesta) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + if obligatorio: + if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta and respuesta.filter(nombre='Otro').exists()): + raise ValidationError("Este campo es obligatorio") + if (otro and otro.strip()) and (respuesta and not respuesta.filter(nombre='Otro').exists()): + raise ValidationError("No esta seleccionada opcion Otro") + if respuesta and (respuesta.filter(nombre='Otro').exists() and not( otro and otro.strip())): + raise ValidationError("Si eliges 'otro', debes proporcionar más detalles en el campo 'otro'.") class RDesplegable(Respuesta): @@ -302,6 +373,22 @@ class RDesplegable(Respuesta): class Meta: verbose_name_plural = '17. R Desplegables' + def clean(self): + super().clean() + respuesta = self.valor + obligatorio = self.elemento.obligatorio + opcOtro = self.elemento.opcionOtro + otro = self.otro + if (not respuesta) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") + if obligatorio: + if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta and respuesta.nombre == 'Otro'): + raise ValidationError("Este campo es obligatorio") + if (otro and otro.strip()) and (respuesta and not respuesta.nombre == 'Otro'): + raise ValidationError("No esta seleccionada opcion Otro") + if respuesta and (respuesta.nombre == 'Otro' and not( otro and otro.strip())): + raise ValidationError("Si eliges 'otro', debes proporcionar más detalles en el campo 'otro'.") + class RDocumento(Respuesta): valor = models.FileField(verbose_name='Subir Documento', upload_to=nombre_archivo_respuesta_doc , null=True, blank=True) @@ -309,3 +396,10 @@ class RDocumento(Respuesta): return self.valor.name if self.valor else '-----' class Meta: verbose_name_plural = '18. R Documentos' + + def clean(self): + super().clean() + respuesta = self.valor + obligatorio = self.elemento.obligatorio + if (not respuesta) and obligatorio: + raise ValidationError("Este campo es Obligatorio.") \ No newline at end of file diff --git a/cosiap_api/dynamic_forms/serializers.py b/cosiap_api/dynamic_forms/serializers.py index 75d14a3..977ce0b 100644 --- a/cosiap_api/dynamic_forms/serializers.py +++ b/cosiap_api/dynamic_forms/serializers.py @@ -6,6 +6,7 @@ from dynamic_forms.models import ( RDesplegable, RDocumento ) from django.core.validators import MinLengthValidator, MaxLengthValidator +from common.serializers import DynamicModelSerializer # Serializer para Opcion class OpcionSerializer(serializers.ModelSerializer): @@ -186,177 +187,28 @@ class DynamicFormSerializer(serializers.ModelSerializer): instance.secciones.exclude(id__in=new_secciones_ids).delete() return instance -# Serializers para las respuestas -class RespuestaSerializer(serializers.ModelSerializer): - class Meta: - model = Respuesta - fields = [] - - def __init__(self, *args, **kwargs): - elemento = kwargs.pop("elemento", None) - solicitud = kwargs.pop("solicitud", None) - super().__init__(*args, **kwargs) - if elemento and solicitud: - self.instance.solicitud = solicitud - self.instance.elemento = elemento - -class RNumericoSerializer(RespuestaSerializer): - valor = serializers.CharField(required=True) - - class Meta: - model = RNumerico - fields = ['valor'] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - elemento = self.instance.elemento if self.instance else None - numMin = elemento.numMin if elemento else None - numMax = elemento.numMax if elemento else None - if numMin is not None: - self.fields['valor'].validators.append(MinLengthValidator(numMin)) - if numMax is not None: - self.fields['valor'].validators.append(MaxLengthValidator(numMax)) - - - -class RTextoCortoSerializer(RespuestaSerializer): - texto = serializers.CharField(required=True) - - class Meta: - model = RTextoCorto - fields = ['texto'] - - def validate_texto(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and (not value or not value.strip()): - raise serializers.ValidationError("Este campo es Obligatorio.") - return value - -class RTextoParrafoSerializer(RespuestaSerializer): - texto = serializers.CharField(required=True) - - class Meta: - model = RTextoParrafo - fields = ['texto'] - - def validate_texto(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and (not value or not value.strip()): - raise serializers.ValidationError("Este campo es Obligatorio.") - return value - -class RHoraSerializer(RespuestaSerializer): - hora = serializers.TimeField(required=True) - - class Meta: - model = RHora - fields = ['hora'] - - def validate_hora(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and not value: - raise serializers.ValidationError("Este campo es Obligatorio.") - return value -class RFechaSerializer(RespuestaSerializer): - fecha = serializers.DateField(required=True) - - class Meta: - model = RFecha - fields = ['fecha'] - - def validate_fecha(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and not value: - raise serializers.ValidationError("Este campo es Obligatorio.") - return value - -class ROpcionMultipleSerializer(RespuestaSerializer): - valor = serializers.CharField(required=True) - otro = serializers.CharField(required=False, allow_blank=True) +RESPUESTA_MODELOS = { + 'RNumerico': RNumerico, + 'RTextoCorto': RTextoCorto, + 'RTextoParrafo': RTextoParrafo, + 'RHora': RHora, + 'RFecha': RFecha, + 'ROpcionMultiple': ROpcionMultiple, + 'RCasillas': RCasillas, + 'RDesplegable': RDesplegable, + 'RDocumento': RDocumento, +} +class RespuestaSerializer(serializers.ModelSerializer): class Meta: - model = ROpcionMultiple - fields = ['valor', 'otro'] - - def validate(self, data): - elemento = self.instance.elemento - obligatorio = elemento.obligatorio - opcOtro = elemento.opcionOtro - respuesta = data.get('valor') - otro = data.get('otro') - - if obligatorio: - if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta == 'Otro'): - raise serializers.ValidationError("Este campo es obligatorio") - if (otro and otro.strip()) and (respuesta != 'Otro'): - raise serializers.ValidationError("No está seleccionada la opción 'Otro'") - if respuesta == 'Otro' and not (otro and otro.strip()): - raise serializers.ValidationError("Si eliges 'Otro', debes proporcionar más detalles en el campo 'Otro'.") - return data - - def validate_valor(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and not value: - raise serializers.ValidationError("Este campo es Obligatorio.") - return value - -class RCasillasSerializer(RespuestaSerializer): - valor = serializers.ListField(child=serializers.CharField()) - otro = serializers.CharField(required=False, allow_blank=True) + model = Respuesta + fields = '__all__' - class Meta: - model = RCasillas - fields = ['valor', 'otro'] - - def validate(self, data): - elemento = self.instance.elemento - obligatorio = elemento.obligatorio - opcOtro = elemento.opcionOtro - respuesta = data.get('valor') - otro = data.get('otro') - - if obligatorio: - if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and ('Otro' in respuesta): - raise serializers.ValidationError("Este campo es obligatorio") - if (otro and otro.strip()) and ('Otro' not in respuesta): - raise serializers.ValidationError("No está seleccionada la opción 'Otro'") - if 'Otro' in respuesta and not (otro and otro.strip()): - raise serializers.ValidationError("Si eliges 'Otro', debes proporcionar más detalles en el campo 'Otro'.") - return data - - def validate_valor(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and not value: - raise serializers.ValidationError("Este campo es Obligatorio.") - return value - -class RDesplegableSerializer(RespuestaSerializer): - valor = serializers.CharField(required=True) - otro = serializers.CharField(required=False, allow_blank=True) + def to_representation(self, instance): + serializer = DynamicModelSerializer(instance=instance, model=type(instance)) + return serializer.data - class Meta: - model = RDesplegable - fields = ['valor', 'otro'] - - def validate(self, data): - elemento = self.instance.elemento - obligatorio = elemento.obligatorio - opcOtro = elemento.opcionOtro - respuesta = data.get('valor') - otro = data.get('otro') - - if obligatorio: - if not ((opcOtro and (otro and otro.strip())) or not opcOtro) and (respuesta == 'Otro'): - raise serializers.ValidationError("Este campo es obligatorio") - if (otro and otro.strip()) and (respuesta != 'Otro'): - raise serializers.ValidationError("No está seleccionada la opción 'Otro'") - if respuesta == 'Otro' and not (otro and otro.strip()): - raise serializers.ValidationError("Si eliges 'Otro', debes proporcionar más detalles en el campo 'Otro'.") - return data - - def validate_valor(self, value): - elemento = self.instance.elemento - if elemento.obligatorio and not value: - raise serializers.ValidationError("Este campo es Obligatorio.") - return value + def to_internal_value(self, data): + # Implementación del mapeo inverso según sea necesario + raise NotImplementedError("This method should be implemented dynamically.") diff --git a/cosiap_api/dynamic_forms/tests.py b/cosiap_api/dynamic_forms/tests.py index 7ce503c..9ca0218 100644 --- a/cosiap_api/dynamic_forms/tests.py +++ b/cosiap_api/dynamic_forms/tests.py @@ -1,3 +1,93 @@ -from django.test import TestCase +from rest_framework import status +from common import custom_tests as c_tests +from common.custom_tests import BasePerUserTestCase +from django.core.files.uploadedfile import SimpleUploadedFile +from PIL import Image +import io +import os +from dynamic_forms.models import ( + Opcion, Elemento, ElementosOpciones, Seccion, SeccionesElementos, + DynamicForm, DynamicFormsSecciones, Respuesta +) -# Create your tests here. +class TestsPermisosOpcion(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:opciones' + + methods_responses = { + 'get': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_200_OK, + 'solicitante': status.HTTP_200_OK, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'post': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class TestsPermisosOpcionPK(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:opciones_pk' + + def get_url_kwargs(self): + self.opcion = Opcion.objects.create(nombre='Opcion de Prueba 0') + return {'pk': self.opcion.pk} + + methods_responses = { + 'put': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'delete': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_204_NO_CONTENT, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + + +class OpcionTests(BasePerUserTestCase): + def reset(self): + print('') + Opcion.objects.all().exclude(nombre='Otro').delete() + Elemento.objects.all().delete() + Seccion.objects.all().delete() + DynamicForm.objects.all().delete() + + self.opcion1 = Opcion.objects.create(nombre='Opcion prueba 1 piña') + self.opcion2 = Opcion.objects.create(nombre='Opcion prueba 2 manzana') + self.opcion3 = Opcion.objects.create(nombre='Opcion prueba 3 naranja') + self.opcion_count = 4 + + def tests(self): + subtest_name = 'test_get_opciones' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:opciones', token=self.solicitante_token, user=self.solicitante_user) + print(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), self.opcion_count) + + subtest_name = 'test_get_opciones_incorrect_user' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:opciones', token=self.user_token, user=self.user) + print(response.data) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse('data' in response.data) + + subtest_name = 'test_get_opciones_substring_filter' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:opciones', data={'q': 'an'}, token=self.solicitante_token, user=self.solicitante_user) + print(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), 2) diff --git a/cosiap_api/dynamic_forms/urls.py b/cosiap_api/dynamic_forms/urls.py new file mode 100644 index 0000000..07e5f3b --- /dev/null +++ b/cosiap_api/dynamic_forms/urls.py @@ -0,0 +1,33 @@ +from django.urls import path +from dynamic_forms.views import ( + OpcionAPIView, ElementosOpcionesAPIView, ElementoAPIView, + SeccionesElementosAPIView, SeccionAPIView, DynamicFormsSeccionesAPIView, + DynamicFormAPIView, RespuestaAPIView +) + +app_name = 'dynamic_forms' +urlpatterns = [ + path('opciones/', OpcionAPIView.as_view(), name='opciones'), + path('opciones//', OpcionAPIView.as_view(), name='opciones_pk'), + + path('elementos-opciones/', ElementosOpcionesAPIView.as_view(), name='elementos-opciones'), + path('elementos-opciones//', ElementosOpcionesAPIView.as_view(), name='elementos-opciones_pk'), + + path('elementos/', ElementoAPIView.as_view(), name='elementos'), + path('elementos//', ElementoAPIView.as_view(), name='elementos_pk'), + + path('secciones-elementos/', SeccionesElementosAPIView.as_view(), name='secciones_elementos'), + path('secciones-elementos//', SeccionesElementosAPIView.as_view(), name='secciones_elementos_pk'), + + path('secciones/', SeccionAPIView.as_view(), name='secciones'), + path('secciones//', SeccionAPIView.as_view(), name='secciones_pk'), + + path('forms-secciones/', DynamicFormsSeccionesAPIView.as_view(), name='dynamic_forms_secciones'), + path('forms-secciones//', DynamicFormsSeccionesAPIView.as_view(), name='dynamic-forms-secciones_pk'), + + path('', DynamicFormAPIView.as_view(), name='dynamic_forms'), + path('/', DynamicFormAPIView.as_view(), name='dynamic_forms_pk'), + + path('respuestas/', RespuestaAPIView.as_view(), name='respuestas'), + path('respuestas//', RespuestaAPIView.as_view(), name='respuestas_pk'), +] diff --git a/cosiap_api/dynamic_forms/views.py b/cosiap_api/dynamic_forms/views.py index b43cd1f..77f1601 100644 --- a/cosiap_api/dynamic_forms/views.py +++ b/cosiap_api/dynamic_forms/views.py @@ -1,31 +1,294 @@ -from rest_framework.views import APIView +from common.views import BasePermissionAPIView from rest_framework.response import Response from rest_framework import status -from django.shortcuts import get_object_or_404 -from dynamic_forms.models import DynamicForm -from dynamic_forms.serializers import DynamicFormSerializer -from common.views import BasePermissionAPIView +from notificaciones.mensajes import Mensaje +from dynamic_forms.models import ( + Opcion, Elemento, ElementosOpciones, Seccion, SeccionesElementos, + DynamicForm, DynamicFormsSecciones, Respuesta +) +from .serializers import ( + OpcionSerializer, ElementoSerializer, ElementosOpcionesSerializer, + SeccionSerializer, SeccionesElementosSerializer, DynamicFormSerializer, + DynamicFormsSeccionesSerializer, RespuestaSerializer +) +from rest_framework.permissions import IsAuthenticated +from users.permisos import es_admin, primer_login -class DynamicFormAPIView(BasePermissionAPIView): - ''' - Clase que maneja las solicitudes de los recursos de Modalidad +class BaseFormAPIView(BasePermissionAPIView): + +# APIView para Opcion +class OpcionAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] + + def get(self, request, pk=None, *args, **kwargs): + if pk: + opcion = Opcion.objects.get(pk=pk) + serializer = OpcionSerializer(opcion) + else: + substring = request.query_params.get('q', None) + if substring: + opciones = Opcion.objects.filter(nombre__icontains=substring) + else: + opciones = Opcion.objects.all() + serializer = OpcionSerializer(opciones, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = OpcionSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + response_data = {'data': serializer.data} + Mensaje.success(response_data, 'Opción creada con éxito.') + return Response(response_data, status=status.HTTP_201_CREATED) + response_data = {'errors': serializer.errors} + Mensaje.warning(response_data, 'No se pudo guardar la ópcion.') + Mensaje.error(response_data, serializer.errors) + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + opcion = Opcion.objects.get(pk=pk) + serializer = OpcionSerializer(opcion, data=request.data) + if serializer.is_valid(): + serializer.save() + response_data = {'data': serializer.data} + Mensaje.success(response_data, 'Opción actualizada con éxito.') + return Response(serializer.data, status=status.HTTP_200_OK) + response_data = {'errors': serializer.errors} + Mensaje.warning(response_data, 'No se pudo actualizar la opción.') + Mensaje.error(response_data, serializer.errors) + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + opcion = Opcion.objects.get(pk=pk) + opcion.delete() + response_data = {} + Mensaje.success(response_data, 'Opción eliminada con éxito') + return Response(response_data, status=status.HTTP_204_NO_CONTENT) - Tipos de solicitud: - - GET (Obtiene toda la lista de modalidades o una modalidad específica) - - POST (Crea una nueva modalidad) - - PUT (Actualizar los datos de alguna modalidad existente) - - DELETE (Si bien no se permiten eliminar las modalidades, este método la archivará en su lugar) - ''' +# APIView para ElementosOpciones +class ElementosOpcionesAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] def get(self, request, pk=None): if pk: - dynamic_form = get_object_or_404(DynamicForm, pk=pk) - serializer = DynamicFormSerializer(dynamic_form) + elemento_opcion = ElementosOpciones.objects.get(pk=pk) + serializer = ElementosOpcionesSerializer(elemento_opcion) + else: + elementos_opciones = ElementosOpciones.objects.all() + serializer = ElementosOpcionesSerializer(elementos_opciones, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = ElementosOpcionesSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + response_data = {'data': serializer.data} + Mensaje.success(response_data, 'Relacion Elmento Opcion creada con éxito.') + return Response(response_data, status=status.HTTP_201_CREATED) + response_data = {'errors': serializer.errors} + Mensaje.error(response_data, serializer.errors) + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + elemento_opcion = ElementosOpciones.objects.get(pk=pk) + serializer = ElementosOpcionesSerializer(elemento_opcion, data=request.data) + if serializer.is_valid(): + serializer.save() + response_data = {'data': serializer.data} + Mensaje.success(response_data, 'Relacion modificada con éxito.') + return Response(response_data, status=status.HTTP_200_OK) + response_data = {'errors': serializer.errors} + Mensaje.success(response_data, serializer.errors) + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + elemento_opcion = ElementosOpciones.objects.get(pk=pk) + elemento_opcion.delete() + response_data = {} + Mensaje.success(response_data, 'Relacion Elemento Opcion eliminada con éxito') + return Response(response_data, status=status.HTTP_204_NO_CONTENT) + +# APIView para Elemento +class ElementoAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] + + def get(self, request, pk=None): + if pk: + elemento = Elemento.objects.get(pk=pk) + serializer = ElementoSerializer(elemento) + else: + substring = request.query_params.get('q', None) + if substring: + elementos = Elemento.objects.filter(nombre__icontains=substring) + else: + elementos = Elemento.objects.all() + serializer = ElementoSerializer(elementos, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = ElementoSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + response_data = {'data': serializer.data} + Mensaje.success(response_data, 'Elemento creado con éxito.') + return Response(response_data, status=status.HTTP_201_CREATED) + response_data = {'errors': serializer.errors} + Mensaje.warning(response_data, 'No se pudo guardar el elemento.') + Mensaje.error(response_data, serializer.errors) + return Response(response_data, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + elemento = Elemento.objects.get(pk=pk) + serializer = ElementoSerializer(elemento, data=request.data) + if serializer.is_valid(): + serializer.save() return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + elemento = Elemento.objects.get(pk=pk) + elemento.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + +# APIView para SeccionesElementos +class SeccionesElementosAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] + + def get(self, request, pk=None): + if pk: + seccion_elemento = SeccionesElementos.objects.get(pk=pk) + serializer = SeccionesElementosSerializer(seccion_elemento) + else: + secciones_elementos = SeccionesElementos.objects.all() + serializer = SeccionesElementosSerializer(secciones_elementos, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = SeccionesElementosSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + seccion_elemento = SeccionesElementos.objects.get(pk=pk) + serializer = SeccionesElementosSerializer(seccion_elemento, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + seccion_elemento = SeccionesElementos.objects.get(pk=pk) + seccion_elemento.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + +# APIView para Seccion +class SeccionAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] + + def get(self, request, pk=None): + if pk: + seccion = Seccion.objects.get(pk=pk) + serializer = SeccionSerializer(seccion) + else: + secciones = Seccion.objects.all() + serializer = SeccionSerializer(secciones, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = SeccionSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + seccion = Seccion.objects.get(pk=pk) + serializer = SeccionSerializer(seccion, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + seccion = Seccion.objects.get(pk=pk) + seccion.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + +# APIView para DynamicFormsSecciones +class DynamicFormsSeccionesAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] + + def get(self, request, pk=None): + if pk: + dynamic_form_seccion = DynamicFormsSecciones.objects.get(pk=pk) + serializer = DynamicFormsSeccionesSerializer(dynamic_form_seccion) + else: + dynamic_forms_secciones = DynamicFormsSecciones.objects.all() + serializer = DynamicFormsSeccionesSerializer(dynamic_forms_secciones, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = DynamicFormsSeccionesSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + dynamic_form_seccion = DynamicFormsSecciones.objects.get(pk=pk) + serializer = DynamicFormsSeccionesSerializer(dynamic_form_seccion, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + dynamic_form_seccion = DynamicFormsSecciones.objects.get(pk=pk) + dynamic_form_seccion.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + +# APIView para DynamicForm +class DynamicFormAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, es_admin] + permission_classes_delete = [IsAuthenticated, es_admin] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, es_admin] + + def get(self, request, pk=None): + if pk: + dynamic_form = DynamicForm.objects.get(pk=pk) + serializer = DynamicFormSerializer(dynamic_form) else: dynamic_forms = DynamicForm.objects.all() serializer = DynamicFormSerializer(dynamic_forms, many=True) - return Response(serializer.data) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) def post(self, request): serializer = DynamicFormSerializer(data=request.data) @@ -35,7 +298,7 @@ class DynamicFormAPIView(BasePermissionAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def put(self, request, pk): - dynamic_form = get_object_or_404(DynamicForm, pk=pk) + dynamic_form = DynamicForm.objects.get(pk=pk) serializer = DynamicFormSerializer(dynamic_form, data=request.data) if serializer.is_valid(): serializer.save() @@ -43,6 +306,43 @@ class DynamicFormAPIView(BasePermissionAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, pk): - dynamic_form = get_object_or_404(DynamicForm, pk=pk) + dynamic_form = DynamicForm.objects.get(pk=pk) dynamic_form.delete() return Response(status=status.HTTP_204_NO_CONTENT) + +# APIView para Respuesta +class RespuestaAPIView(BasePermissionAPIView): + permission_classes_create = [IsAuthenticated, primer_login] + permission_classes_delete = [IsAuthenticated, primer_login] + permission_classes_list = [IsAuthenticated, primer_login] + permission_classes_update = [IsAuthenticated, primer_login] + + def get(self, request, pk=None): + if pk: + respuesta = Respuesta.objects.get(pk=pk) + serializer = RespuestaSerializer(respuesta) + else: + respuestas = Respuesta.objects.all() + serializer = RespuestaSerializer(respuestas, many=True) + response_data = {'data': serializer.data} + return Response(response_data, status=status.HTTP_200_OK) + + def post(self, request): + serializer = RespuestaSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def put(self, request, pk): + respuesta = Respuesta.objects.get(pk=pk) + serializer = RespuestaSerializer(respuesta, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def delete(self, request, pk): + respuesta = Respuesta.objects.get(pk=pk) + respuesta.delete() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/cosiap_api/modalidades/migrations/0002_relacion_modalidad_dynamicForm.py b/cosiap_api/modalidades/migrations/0002_relacion_modalidad_dynamicForm.py new file mode 100644 index 0000000..2b02d55 --- /dev/null +++ b/cosiap_api/modalidades/migrations/0002_relacion_modalidad_dynamicForm.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.7 on 2024-07-29 18:23 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic_forms', '0002_creacion_modelos_formularios_dinamicos_02'), + ('modalidades', '0001_creacion_inicial_modulos_dynamic_formats__modalidades__solicitudes'), + ] + + operations = [ + migrations.AddField( + model_name='modalidad', + name='dynamic_form', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='dynamic_forms.dynamicform', verbose_name='Modalidad'), + ), + ] diff --git a/cosiap_api/modalidades/models.py b/cosiap_api/modalidades/models.py index 06cb413..b6dd1ba 100644 --- a/cosiap_api/modalidades/models.py +++ b/cosiap_api/modalidades/models.py @@ -5,7 +5,6 @@ from django.utils import timezone import os from django.db.models.signals import pre_save from django.dispatch import receiver -from dynamic_forms.models import DynamicForm class Modalidad(models.Model): @@ -25,7 +24,7 @@ class Modalidad(models.Model): descripcion = models.TextField(verbose_name="Descripción", null=False) mostrar = models.BooleanField(default=True) archivado = models.BooleanField(default=False) - dynamic_form = models.ForeignKey(DynamicForm, on_delete=models.SET_NULL, verbose_name="Modalidad") + dynamic_form = models.ForeignKey('dynamic_forms.DynamicForm', on_delete=models.SET_NULL, verbose_name="Modalidad", null=True, blank=False) def __str__(self): return f'{self.nombre}' -- GitLab From 6194ff3d27291bc36b7ec10ec8e96009248c9b17 Mon Sep 17 00:00:00 2001 From: RafaUC Date: Wed, 7 Aug 2024 14:17:26 -0600 Subject: [PATCH 3/3] implementada funcionalidad de creacion y configuracion de formularios dinamicos --- cosiap_api/common/custom_tests.py | 6 +- cosiap_api/common/utils.py | 5 + .../0003_permitir_null_campos_many_to_many.py | 33 + .../0004_permitir_null_campos_many_to_many.py | 33 + .../migrations/0005_set_null_ordenes.py | 28 + cosiap_api/dynamic_forms/models.py | 16 +- cosiap_api/dynamic_forms/serializers.py | 203 ++---- cosiap_api/dynamic_forms/tests.py | 655 +++++++++++++++++- cosiap_api/dynamic_forms/urls.py | 21 +- cosiap_api/dynamic_forms/views.py | 347 +++------- 10 files changed, 928 insertions(+), 419 deletions(-) create mode 100644 cosiap_api/common/utils.py create mode 100644 cosiap_api/dynamic_forms/migrations/0003_permitir_null_campos_many_to_many.py create mode 100644 cosiap_api/dynamic_forms/migrations/0004_permitir_null_campos_many_to_many.py create mode 100644 cosiap_api/dynamic_forms/migrations/0005_set_null_ordenes.py diff --git a/cosiap_api/common/custom_tests.py b/cosiap_api/common/custom_tests.py index abe198b..86b8de6 100644 --- a/cosiap_api/common/custom_tests.py +++ b/cosiap_api/common/custom_tests.py @@ -7,6 +7,7 @@ from common.views import BasePermissionAPIView from rest_framework.response import Response from django.urls import reverse from django.test import tag +from urllib.parse import urlencode from users.permisos import es_admin, primer_login from rest_framework.permissions import AllowAny, IsAuthenticated @@ -79,7 +80,7 @@ class BasePerUserTestCase(APITestCase): 'access': str(refresh.access_token), } - def perform_request(self, method, url_name, url_kwargs=None, token=None, user=None, data=None, is_multipart=False): + def perform_request(self, method, url_name, url_kwargs=None, query_params=None, token=None, user=None, data=None, is_multipart=False): """ Realiza una solicitud a la vista especificada utilizando el método HTTP indicado. @@ -100,6 +101,9 @@ class BasePerUserTestCase(APITestCase): client.force_authenticate(user=user, token=token['access']) url = reverse(url_name, kwargs=url_kwargs) + if query_params: + query_string = urlencode(query_params) + url = f"{url}?{query_string}" method = method.lower() if is_multipart: diff --git a/cosiap_api/common/utils.py b/cosiap_api/common/utils.py new file mode 100644 index 0000000..e63ba45 --- /dev/null +++ b/cosiap_api/common/utils.py @@ -0,0 +1,5 @@ +import json + +def printDict(diccionario): + #print(json.dumps(diccionario, indent=4, separators=(",", ": "), ensure_ascii=False)) + pass \ No newline at end of file diff --git a/cosiap_api/dynamic_forms/migrations/0003_permitir_null_campos_many_to_many.py b/cosiap_api/dynamic_forms/migrations/0003_permitir_null_campos_many_to_many.py new file mode 100644 index 0000000..8536fab --- /dev/null +++ b/cosiap_api/dynamic_forms/migrations/0003_permitir_null_campos_many_to_many.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0.7 on 2024-08-02 18:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic_forms', '0002_creacion_modelos_formularios_dinamicos_02'), + ] + + operations = [ + migrations.AlterField( + model_name='dynamicform', + name='secciones', + field=models.ManyToManyField(blank=True, null=True, through='dynamic_forms.DynamicFormsSecciones', to='dynamic_forms.seccion', verbose_name='Secciones'), + ), + migrations.AlterField( + model_name='elemento', + name='opciones', + field=models.ManyToManyField(blank=True, null=True, through='dynamic_forms.ElementosOpciones', to='dynamic_forms.opcion', verbose_name='Opciones'), + ), + migrations.AlterField( + model_name='rcasillas', + name='valor', + field=models.ManyToManyField(blank=True, null=True, to='dynamic_forms.opcion'), + ), + migrations.AlterField( + model_name='seccion', + name='elementos', + field=models.ManyToManyField(blank=True, null=True, through='dynamic_forms.SeccionesElementos', to='dynamic_forms.elemento', verbose_name='Elementos'), + ), + ] diff --git a/cosiap_api/dynamic_forms/migrations/0004_permitir_null_campos_many_to_many.py b/cosiap_api/dynamic_forms/migrations/0004_permitir_null_campos_many_to_many.py new file mode 100644 index 0000000..f77abf3 --- /dev/null +++ b/cosiap_api/dynamic_forms/migrations/0004_permitir_null_campos_many_to_many.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0.7 on 2024-08-02 19:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic_forms', '0003_permitir_null_campos_many_to_many'), + ] + + operations = [ + migrations.AlterField( + model_name='dynamicform', + name='secciones', + field=models.ManyToManyField(blank=True, through='dynamic_forms.DynamicFormsSecciones', to='dynamic_forms.seccion', verbose_name='Secciones'), + ), + migrations.AlterField( + model_name='elemento', + name='opciones', + field=models.ManyToManyField(blank=True, through='dynamic_forms.ElementosOpciones', to='dynamic_forms.opcion', verbose_name='Opciones'), + ), + migrations.AlterField( + model_name='rcasillas', + name='valor', + field=models.ManyToManyField(blank=True, to='dynamic_forms.opcion'), + ), + migrations.AlterField( + model_name='seccion', + name='elementos', + field=models.ManyToManyField(blank=True, through='dynamic_forms.SeccionesElementos', to='dynamic_forms.elemento', verbose_name='Elementos'), + ), + ] diff --git a/cosiap_api/dynamic_forms/migrations/0005_set_null_ordenes.py b/cosiap_api/dynamic_forms/migrations/0005_set_null_ordenes.py new file mode 100644 index 0000000..5ad7597 --- /dev/null +++ b/cosiap_api/dynamic_forms/migrations/0005_set_null_ordenes.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0.7 on 2024-08-02 19:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('dynamic_forms', '0004_permitir_null_campos_many_to_many'), + ] + + operations = [ + migrations.AlterField( + model_name='dynamicformssecciones', + name='orden', + field=models.IntegerField(default=0, verbose_name='Orden'), + ), + migrations.AlterField( + model_name='elementosopciones', + name='orden', + field=models.IntegerField(default=0, verbose_name='Orden'), + ), + migrations.AlterField( + model_name='seccioneselementos', + name='orden', + field=models.IntegerField(default=0, verbose_name='Orden'), + ), + ] diff --git a/cosiap_api/dynamic_forms/models.py b/cosiap_api/dynamic_forms/models.py index 4b41739..2711f42 100644 --- a/cosiap_api/dynamic_forms/models.py +++ b/cosiap_api/dynamic_forms/models.py @@ -51,7 +51,7 @@ class Elemento(models.Model): min_digits = models.IntegerField(default=0, verbose_name='Mínimo de dígitos') max_digits = models.IntegerField(default=10, verbose_name='Máximo de dígitos') - opciones = models.ManyToManyField(Opcion, through='ElementosOpciones', verbose_name='Opciones') + opciones = models.ManyToManyField(Opcion, through='ElementosOpciones', verbose_name='Opciones', blank=True) formato = models.ForeignKey(DynamicFormat, on_delete=models.SET_NULL, verbose_name='Elemento', default=None, null=True, blank=True) class Meta: verbose_name_plural = '05. Elementos' @@ -68,7 +68,7 @@ class ElementosOpciones(models.Model): """ elemento = models.ForeignKey(Elemento, on_delete=models.CASCADE, verbose_name='Elemento') opcion = models.ForeignKey(Opcion, on_delete=models.CASCADE, verbose_name='Opción') - orden = models.IntegerField(verbose_name='Orden') + orden = models.IntegerField(verbose_name='Orden', default=0) class Meta: unique_together = ('elemento', 'opcion') verbose_name_plural = '06. Rel Elemento - Opciones' @@ -83,12 +83,12 @@ class Seccion(models.Model): """ class Tipo(models.TextChoices): UNICO = 'unico', 'Único' - AGREGACION = 'agregacion', 'Agregación' + LISTA = 'lista', 'Lista' nombre = models.CharField(max_length=100, verbose_name='Nombre de la sección') tipo = models.CharField(max_length=20, choices=Tipo.choices, default=Tipo.UNICO, verbose_name='Tipo de sección') - elementos = models.ManyToManyField(Elemento, through='SeccionesElementos', verbose_name='Elementos') + elementos = models.ManyToManyField(Elemento, through='SeccionesElementos', verbose_name='Elementos', blank=True) class Meta: verbose_name_plural = '03. Secciones' @@ -104,7 +104,7 @@ class SeccionesElementos(models.Model): """ seccion = models.ForeignKey(Seccion, on_delete=models.CASCADE, verbose_name='Sección') elemento = models.ForeignKey(Elemento, on_delete=models.CASCADE, verbose_name='Elemento') - orden = models.IntegerField(verbose_name='Orden') + orden = models.IntegerField(verbose_name='Orden', default=0) class Meta: unique_together = ('seccion', 'elemento') verbose_name_plural = '04. Rel Secciones - Elementos' @@ -118,7 +118,7 @@ class DynamicForm(models.Model): - nombre (str, Nombre del formulario dinámico) """ nombre = models.CharField(max_length=100, verbose_name='Nombre del formulario') - secciones = models.ManyToManyField(Seccion, through='DynamicFormsSecciones', verbose_name='Secciones') + secciones = models.ManyToManyField(Seccion, through='DynamicFormsSecciones', verbose_name='Secciones', blank=True) class Meta: verbose_name_plural = "01. Formularios" @@ -133,7 +133,7 @@ class DynamicFormsSecciones(models.Model): """ dynamic_form = models.ForeignKey(DynamicForm, on_delete=models.CASCADE, verbose_name='Formulario dinámico') seccion = models.ForeignKey(Seccion, on_delete=models.CASCADE, verbose_name='Sección') - orden = models.IntegerField(verbose_name='Orden') + orden = models.IntegerField(verbose_name='Orden', default=0) class Meta: unique_together = ('dynamic_form', 'seccion') verbose_name_plural = '02. Rel ormulario - Secciones' @@ -321,7 +321,7 @@ class ROpcionMultiple(Respuesta): raise ValidationError("Si eliges 'otro', debes proporcionar más detalles en el campo 'otro'.") class RCasillas(Respuesta): - valor = models.ManyToManyField(Opcion, blank=True) + valor = models.ManyToManyField(Opcion, blank=True) otro = models.CharField(max_length=255, verbose_name="Otro", null=True, blank=True) def getStringValue(self): diff --git a/cosiap_api/dynamic_forms/serializers.py b/cosiap_api/dynamic_forms/serializers.py index 977ce0b..2e95c3f 100644 --- a/cosiap_api/dynamic_forms/serializers.py +++ b/cosiap_api/dynamic_forms/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers +from django.db import models from dynamic_forms.models import ( Opcion, Elemento, ElementosOpciones, Seccion, SeccionesElementos, DynamicForm, DynamicFormsSecciones, RAgregacion, Respuesta, RNumerico, @@ -8,184 +9,106 @@ from dynamic_forms.models import ( from django.core.validators import MinLengthValidator, MaxLengthValidator from common.serializers import DynamicModelSerializer +class BaseDynamicFormSerializer(serializers.ModelSerializer): + def get_child_queryset(self, obj, lookup_str, debug=False): + child_objs = getattr(obj, lookup_str) + child_objs = child_objs.all() if isinstance(child_objs, models.manager.BaseManager) else child_objs + if debug: + print(f'\nPREFETCH DATA {self.__class__.__name__}({obj}) ::: {obj._prefetched_objects_cache}') if hasattr(obj, '_prefetched_objects_cache') else print(f"\n> {self.__class__.__name__}({obj}) no tiene _prefetched_objects_cache") + print(f'|-CHILD OBJECTS {obj}: {child_objs}') + return child_objs + + def __str__(self): + # Representa la clase y el objeto relacionado si existe + instance_str = f"{self.instance}" if self.instance else "No instance" + return f"{self.__class__.__name__}({instance_str})" + # Serializer para Opcion -class OpcionSerializer(serializers.ModelSerializer): +class OpcionSerializer(BaseDynamicFormSerializer): class Meta: model = Opcion fields = '__all__' - def create(self, validated_data): - return Opcion.objects.create(**validated_data) - - def update(self, instance, validated_data): - instance.nombre = validated_data.get('nombre', instance.nombre) - instance.save() - return instance -# Serializer para ElementosOpciones -class ElementosOpcionesSerializer(serializers.ModelSerializer): - opcion = OpcionSerializer() +class ElementosOpcionesSerializer(BaseDynamicFormSerializer): + opcion = serializers.SerializerMethodField() class Meta: model = ElementosOpciones - fields = '__all__' + fields = ['opcion','orden'] + + def get_opcion(self, obj): + ''' Definimos la logica para obtener la representacion de opcion''' + instance = self.get_child_queryset(obj, 'opcion') + return OpcionSerializer(instance).data - def create(self, validated_data): - opcion_data = validated_data.pop('opcion') - opcion = Opcion.objects.create(**opcion_data) - elemento_opcion = ElementosOpciones.objects.create(opcion=opcion, **validated_data) - return elemento_opcion - def update(self, instance, validated_data): - opcion_data = validated_data.pop('opcion') - opcion, _ = Opcion.objects.update_or_create(**opcion_data) - instance.opcion = opcion - instance.save() - return instance - -# Serializer para Elemento -class ElementoSerializer(serializers.ModelSerializer): - opciones = ElementosOpcionesSerializer(many=True) +class ElementoSerializer(BaseDynamicFormSerializer): + opciones = serializers.SerializerMethodField() class Meta: model = Elemento - fields = '__all__' - - def create(self, validated_data): - opciones_data = validated_data.pop('opciones', []) - elemento = Elemento.objects.create(**validated_data) - for opcion_data in opciones_data: - ElementosOpcionesSerializer().create({'opcion': opcion_data['opcion'], **opcion_data}) - return elemento - - def update(self, instance, validated_data): - opciones_data = validated_data.pop('opciones', []) - instance.nombre = validated_data.get('nombre', instance.nombre) - instance.save() - - existing_opciones_ids = set(instance.opciones.values_list('id', flat=True)) - new_opciones_ids = set() - - for opcion_data in opciones_data: - opcion, _ = Opcion.objects.update_or_create(**opcion_data.pop('opcion')) - elemento_opcion, _ = ElementosOpciones.objects.update_or_create( - opcion=opcion, - defaults=opcion_data - ) - new_opciones_ids.add(elemento_opcion.id) - - instance.opciones.exclude(id__in=new_opciones_ids).delete() - return instance + fields = '__all__' + + def get_opciones(self, obj): + ''' Definimos la logica para obtener la representacion de opciones''' + queryset = self.get_child_queryset(obj, 'elementosopciones_set') + return ElementosOpcionesSerializer(queryset, many=True).data # Serializer para SeccionesElementos -class SeccionesElementosSerializer(serializers.ModelSerializer): - elemento = ElementoSerializer() +class SeccionesElementosSerializer(BaseDynamicFormSerializer): + elemento = serializers.SerializerMethodField() class Meta: model = SeccionesElementos - fields = '__all__' + fields = ['elemento','orden'] - def create(self, validated_data): - elemento_data = validated_data.pop('elemento') - elemento = ElementoSerializer().create(elemento_data) - seccion_elemento = SeccionesElementos.objects.create(elemento=elemento, **validated_data) - return seccion_elemento - - def update(self, instance, validated_data): - elemento_data = validated_data.pop('elemento') - elemento = ElementoSerializer().update(instance.elemento, elemento_data) - instance.elemento = elemento - instance.save() - return instance + def get_elemento(self, obj): + ''' Definimos la logica para obtener la representacion de elemento''' + instance = self.get_child_queryset(obj, 'elemento') + return ElementoSerializer(instance).data # Serializer para Seccion -class SeccionSerializer(serializers.ModelSerializer): - elementos = SeccionesElementosSerializer(many=True) +class SeccionSerializer(BaseDynamicFormSerializer): + elementos = serializers.SerializerMethodField() class Meta: model = Seccion fields = '__all__' - - def create(self, validated_data): - elementos_data = validated_data.pop('elementos', []) - seccion = Seccion.objects.create(**validated_data) - for elemento_data in elementos_data: - SeccionesElementosSerializer().create({'elemento': elemento_data['elemento'], **elemento_data}) - return seccion - - def update(self, instance, validated_data): - elementos_data = validated_data.pop('elementos', []) - instance.nombre = validated_data.get('nombre', instance.nombre) - instance.save() - - existing_elementos_ids = set(instance.elementos.values_list('id', flat=True)) - new_elementos_ids = set() - - for elemento_data in elementos_data: - elemento, _ = Elemento.objects.update_or_create(**elemento_data.pop('elemento')) - seccion_elemento, _ = SeccionesElementos.objects.update_or_create( - elemento=elemento, - defaults=elemento_data - ) - new_elementos_ids.add(seccion_elemento.id) - - instance.elementos.exclude(id__in=new_elementos_ids).delete() - return instance + + def get_elementos(self, obj): + ''' Definimos la logica para obtener la representacion de elementos''' + queryset = self.get_child_queryset(obj, 'seccioneselementos_set') + return SeccionesElementosSerializer(queryset, many=True).data + # Serializer para DynamicFormsSecciones -class DynamicFormsSeccionesSerializer(serializers.ModelSerializer): - seccion = SeccionSerializer() +class DynamicFormsSeccionesSerializer(BaseDynamicFormSerializer): + seccion = serializers.SerializerMethodField() class Meta: model = DynamicFormsSecciones - fields = '__all__' - - def create(self, validated_data): - seccion_data = validated_data.pop('seccion') - seccion = SeccionSerializer().create(seccion_data) - dynamic_form_seccion = DynamicFormsSecciones.objects.create(seccion=seccion, **validated_data) - return dynamic_form_seccion + fields = ['seccion','orden'] - def update(self, instance, validated_data): - seccion_data = validated_data.pop('seccion') - seccion = SeccionSerializer().update(instance.seccion, seccion_data) - instance.seccion = seccion - instance.save() - return instance + def get_seccion(self, obj): + ''' Definimos la logica para obtener la representacion de seccion''' + instance = self.get_child_queryset(obj, 'seccion') + return SeccionSerializer(instance).data # Serializer para DynamicForm -class DynamicFormSerializer(serializers.ModelSerializer): - secciones = DynamicFormsSeccionesSerializer(many=True) +class DynamicFormSerializer(BaseDynamicFormSerializer): + secciones = serializers.SerializerMethodField() class Meta: model = DynamicForm - fields = '__all__' + fields = '__all__' + + def get_secciones(self, obj): + ''' Definimos la logica para obtener la representacion de secciones''' + queryset = self.get_child_queryset(obj, 'dynamicformssecciones_set') + return DynamicFormsSeccionesSerializer(queryset, many=True).data + - def create(self, validated_data): - secciones_data = validated_data.pop('secciones', []) - dynamic_form = DynamicForm.objects.create(**validated_data) - for seccion_data in secciones_data: - DynamicFormsSeccionesSerializer().create({'seccion': seccion_data['seccion'], **seccion_data}) - return dynamic_form - - def update(self, instance, validated_data): - secciones_data = validated_data.pop('secciones', []) - instance.nombre = validated_data.get('nombre', instance.nombre) - instance.save() - - existing_secciones_ids = set(instance.secciones.values_list('id', flat=True)) - new_secciones_ids = set() - - for seccion_data in secciones_data: - seccion, _ = Seccion.objects.update_or_create(**seccion_data.pop('seccion')) - dynamic_form_seccion, _ = DynamicFormsSecciones.objects.update_or_create( - seccion=seccion, - defaults=seccion_data - ) - new_secciones_ids.add(dynamic_form_seccion.id) - - instance.secciones.exclude(id__in=new_secciones_ids).delete() - return instance RESPUESTA_MODELOS = { diff --git a/cosiap_api/dynamic_forms/tests.py b/cosiap_api/dynamic_forms/tests.py index 9ca0218..4f4866a 100644 --- a/cosiap_api/dynamic_forms/tests.py +++ b/cosiap_api/dynamic_forms/tests.py @@ -2,6 +2,7 @@ from rest_framework import status from common import custom_tests as c_tests from common.custom_tests import BasePerUserTestCase from django.core.files.uploadedfile import SimpleUploadedFile +from common.utils import printDict from PIL import Image import io import os @@ -10,6 +11,13 @@ from dynamic_forms.models import ( DynamicForm, DynamicFormsSecciones, Respuesta ) +from django.db import connection +from django.test.utils import CaptureQueriesContext + + + +#TESTS PARA OPCIONES + class TestsPermisosOpcion(c_tests.PermissionTestCase): url_name = 'dynamic_forms:opciones' @@ -62,24 +70,30 @@ class OpcionTests(BasePerUserTestCase): self.opcion1 = Opcion.objects.create(nombre='Opcion prueba 1 piña') self.opcion2 = Opcion.objects.create(nombre='Opcion prueba 2 manzana') self.opcion3 = Opcion.objects.create(nombre='Opcion prueba 3 naranja') - self.opcion_count = 4 + self.opcion4 = Opcion.objects.create(nombre='Opcion prueba 4 perejil') + self.opcion5 = Opcion.objects.create(nombre='Opcion prueba 5 apio') + self.opcion6 = Opcion.objects.create(nombre='Opcion prueba 6 tomate') + self.opcion7 = Opcion.objects.create(nombre='Opcion prueba 7 pez') + self.opcion_count = 8 def tests(self): subtest_name = 'test_get_opciones' - with self.subTest(subtest_name): + with self.subTest(subtest_name): self.reset() - print(subtest_name) - response = self.perform_request('get', url_name='dynamic_forms:opciones', token=self.solicitante_token, user=self.solicitante_user) - print(response.data) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data['data']), self.opcion_count) + with CaptureQueriesContext(connection) as ctx: + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:opciones', token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), self.opcion_count) + print(f'\nRENDIMIENTO QUERYS: {len(ctx.captured_queries)}') subtest_name = 'test_get_opciones_incorrect_user' with self.subTest(subtest_name): self.reset() print(subtest_name) response = self.perform_request('get', url_name='dynamic_forms:opciones', token=self.user_token, user=self.user) - print(response.data) + printDict(response.data) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertFalse('data' in response.data) @@ -87,7 +101,628 @@ class OpcionTests(BasePerUserTestCase): with self.subTest(subtest_name): self.reset() print(subtest_name) - response = self.perform_request('get', url_name='dynamic_forms:opciones', data={'q': 'an'}, token=self.solicitante_token, user=self.solicitante_user) - print(response.data) + response = self.perform_request('get', url_name='dynamic_forms:opciones', query_params={'q': 'an'}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(len(response.data['data']), 2) + + subtest_name = 'test_get_opciones_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': self.opcion1.pk}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['data']['nombre'], 'Opcion prueba 1 piña') + self.assertEqual(response.status_code, status.HTTP_200_OK) + + subtest_name = 'test_get_opciones_pk_not_found' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': 99999999999}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + subtest_name = 'test_post_opciones' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = { + 'nombre': 'Nueva opcion recien creada' + } + response = self.perform_request('post', url_name='dynamic_forms:opciones', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(Opcion.objects.count(), self.opcion_count+1) + + subtest_name = 'test_post_opciones_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = { + + } + response = self.perform_request('post', url_name='dynamic_forms:opciones', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Opcion.objects.count(), self.opcion_count) + + subtest_name = 'test_put_opciones_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.opcion1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': 'Nombre modificado' + } + response = self.perform_request('put', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + dict_nuevo = Opcion.objects.get(id=dict_original['id']).__dict__.copy() + dict_nuevo.pop('_state', None) + print(dict_nuevo) + printDict(response.data) + self.assertNotEqual(dict_nuevo, dict_original) + self.assertEqual(dict_nuevo, response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(Opcion.objects.count(), self.opcion_count) + + subtest_name = 'test_put_opciones_pk_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.opcion1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + + } + response = self.perform_request('put', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + dict_nuevo = Opcion.objects.get(id=dict_original['id']).__dict__.copy() + dict_nuevo.pop('_state', None) + print(dict_nuevo) + printDict(response.data) + self.assertEqual(dict_nuevo, dict_original) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Opcion.objects.count(), self.opcion_count) + + subtest_name = 'test_put_opciones_pk_not_found' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('put', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': 99999999999}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + subtest_name = 'test_delete_opciones_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('delete', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': self.opcion1.pk}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(Opcion.objects.count(), self.opcion_count-1) + + subtest_name = 'test_delete_opciones_pk_not_found' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('delete', url_name='dynamic_forms:opciones_pk', url_kwargs={'pk': 99999999999}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + +#TESTS PARA ElementoAPIVIEW +class TestsPermisosElemento(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:elementos' + + methods_responses = { + 'get': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_200_OK, + 'solicitante': status.HTTP_200_OK, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'post': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class TestsPermisosElementoPK(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:elementos_pk' + + def get_url_kwargs(self): + self.elemento = Elemento.objects.create(nombre='Elemento de Prueba 0', tipo=Elemento.Tipo.TEXTO_CORTO) + return {'pk': self.elemento.pk} + + methods_responses = { + 'put': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'delete': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_204_NO_CONTENT, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class ElementoTests(OpcionTests): + def reset(self): + super().reset() + Elemento.objects.all().delete() + + self.elemento1 = Elemento.objects.create(nombre='Elemento prueba 1', tipo=Elemento.Tipo.TEXTO_CORTO) + self.elemento2 = Elemento.objects.create(nombre='Elemento prueba 2', tipo=Elemento.Tipo.NUMERICO) + self.elemento3 = Elemento.objects.create(nombre='Elemento prueba 3', tipo=Elemento.Tipo.FECHA) + + self.elemento4 = Elemento.objects.create(nombre='Elemento prueba 4', tipo=Elemento.Tipo.NUMERICO) + self.elemento5 = Elemento.objects.create(nombre='Elemento prueba 5', tipo=Elemento.Tipo.FECHA) + self.elemento_count = 5 + + self.elemento2.opciones.set([self.opcion1, self.opcion2, self.opcion3, self.opcion4]) + self.elemento3.opciones.set([self.opcion5, self.opcion6, self.opcion7]) + + self.elemento4.opciones.set([self.opcion1, self.opcion2, self.opcion3, self.opcion4]) + self.elemento5.opciones.set([self.opcion5, self.opcion6, self.opcion7]) + + def tests(self): + subtest_name = 'test_get_elementos' + with self.subTest(subtest_name): + self.reset() + with CaptureQueriesContext(connection) as ctx: + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:elementos', token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), self.elemento_count) + + print(f'\nRENDIMIENTO QUERYS: {len(ctx.captured_queries)}') + + subtest_name = 'test_get_elementos_incorrect_user' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:elementos', token=self.user_token, user=self.user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse('data' in response.data) + + subtest_name = 'test_get_elementos_substring_filter' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:elementos', query_params={'q': 'prueba 2'}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), 1) + + subtest_name = 'test_get_elementos_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:elementos_pk', url_kwargs={'pk': self.elemento1.pk}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['data']['nombre'], 'Elemento prueba 1') + + subtest_name = 'test_get_elementos_pk_not_found' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:elementos_pk', url_kwargs={'pk': 99999999999}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + subtest_name = 'test_post_elementos' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = { + 'nombre': 'Nuevo elemento', + 'tipo': Elemento.Tipo.TEXTO_CORTO + } + response = self.perform_request('post', url_name='dynamic_forms:elementos', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(Elemento.objects.count(), self.elemento_count+1) + + subtest_name = 'test_post_elementos_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = {} + response = self.perform_request('post', url_name='dynamic_forms:elementos', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Elemento.objects.count(), self.elemento_count) + + subtest_name = 'test_put_elementos_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.elemento1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': 'Nombre modificado' + } + response = self.perform_request('put', url_name='dynamic_forms:elementos_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(Elemento.objects.get(pk=self.elemento1.pk).nombre, 'Nombre modificado') + + subtest_name = 'test_put_elementos_pk_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.elemento1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': '' + } + response = self.perform_request('put', url_name='dynamic_forms:elementos_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Elemento.objects.get(pk=self.elemento1.pk).nombre, 'Elemento prueba 1') + + subtest_name = 'test_delete_elementos_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('delete', url_name='dynamic_forms:elementos_pk', url_kwargs={'pk': self.elemento1.pk}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(Elemento.objects.count(), self.elemento_count-1) + +#TESTS para SeccionAPIView + +class TestsPermisosSeccion(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:secciones' + + methods_responses = { + 'get': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_200_OK, + 'solicitante': status.HTTP_200_OK, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'post': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class TestsPermisosSeccionPK(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:secciones_pk' + + def get_url_kwargs(self): + self.seccion = Seccion.objects.create(nombre='Sección de Prueba 0', tipo=Seccion.Tipo.UNICO) + return {'pk': self.seccion.pk} + + methods_responses = { + 'put': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'delete': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_204_NO_CONTENT, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class SeccionTests(ElementoTests): + def reset(self): + super().reset() + Seccion.objects.all().delete() + + self.seccion1 = Seccion.objects.create(nombre='Sección prueba 1', tipo=Seccion.Tipo.UNICO) + self.seccion2 = Seccion.objects.create(nombre='Sección prueba 2', tipo=Seccion.Tipo.UNICO) + self.seccion3 = Seccion.objects.create(nombre='Sección prueba 3', tipo=Seccion.Tipo.LISTA) + + self.seccion4 = Seccion.objects.create(nombre='Sección prueba 4', tipo=Seccion.Tipo.UNICO) + self.seccion5 = Seccion.objects.create(nombre='Sección prueba 5', tipo=Seccion.Tipo.LISTA) + self.seccion_count = 5 + + self.seccion2.elementos.set([self.elemento1, self.elemento2]) + self.seccion3.elementos.set([self.elemento2, self.elemento3, self.elemento5]) + + self.seccion4.elementos.set([self.elemento4, self.elemento2]) + self.seccion5.elementos.set([self.elemento2, self.elemento5]) + + def tests(self): + subtest_name = 'test_get_secciones' + with self.subTest(subtest_name): + self.reset() + with CaptureQueriesContext(connection) as ctx: + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:secciones', token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), self.seccion_count) + print(f'\nRENDIMIENTO QUERYS: {len(ctx.captured_queries)}') + + subtest_name = 'test_get_secciones_incorrect_user' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:secciones', token=self.user_token, user=self.user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse('data' in response.data) + + subtest_name = 'test_get_secciones_substring_filter' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:secciones', query_params={'q': 'prueba 2'}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), 1) + + subtest_name = 'test_get_secciones_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:secciones_pk', url_kwargs={'pk': self.seccion1.pk}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['data']['nombre'], 'Sección prueba 1') + + subtest_name = 'test_get_secciones_pk_not_found' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:secciones_pk', url_kwargs={'pk': 99999999999}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + subtest_name = 'test_post_secciones' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = { + 'nombre': 'Nueva seccion', + 'tipo': Seccion.Tipo.UNICO + } + response = self.perform_request('post', url_name='dynamic_forms:secciones', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(Seccion.objects.count(), self.seccion_count+1) + + subtest_name = 'test_post_secciones_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = {} + response = self.perform_request('post', url_name='dynamic_forms:secciones', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Seccion.objects.count(), self.seccion_count) + + subtest_name = 'test_put_secciones_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.seccion1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': 'Nombre modificado' + } + response = self.perform_request('put', url_name='dynamic_forms:secciones_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(Seccion.objects.get(pk=self.seccion1.pk).nombre, 'Nombre modificado') + + subtest_name = 'test_put_secciones_pk_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.seccion1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': '' + } + response = self.perform_request('put', url_name='dynamic_forms:secciones_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(Seccion.objects.get(pk=self.seccion1.pk).nombre, 'Sección prueba 1') + + subtest_name = 'test_delete_secciones_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('delete', url_name='dynamic_forms:secciones_pk', url_kwargs={'pk': self.seccion1.pk}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(Seccion.objects.count(), self.seccion_count-1) + +#TESTS para DynamicFormAPIView +class TestsPermisosDynamicForm(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:dynamic_forms' + + methods_responses = { + 'get': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_200_OK, + 'solicitante': status.HTTP_200_OK, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'post': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class TestsPermisosDynamicFormPK(c_tests.PermissionTestCase): + url_name = 'dynamic_forms:dynamic_forms_pk' + + def get_url_kwargs(self): + self.dynamic_form = DynamicForm.objects.create(nombre='Form prueba') + print(f'LLAVE PRIMARIA: {self.dynamic_form.pk}') + return {'pk': self.dynamic_form.pk} + + methods_responses = { + 'put': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_400_BAD_REQUEST, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + }, + 'delete': { + 'user': status.HTTP_403_FORBIDDEN, + 'admin': status.HTTP_204_NO_CONTENT, + 'solicitante': status.HTTP_403_FORBIDDEN, + 'anonymous': status.HTTP_401_UNAUTHORIZED + } + } + +class DynamicFormTests(SeccionTests): + def reset(self): + super().reset() + DynamicForm.objects.all().delete() + + self.dynamic_form1 = DynamicForm.objects.create(nombre='Form prueba 1') + self.dynamic_form2 = DynamicForm.objects.create(nombre='Form prueba 2') + self.dynamic_form3 = DynamicForm.objects.create(nombre='Form prueba 3') + + self.dynamic_form4 = DynamicForm.objects.create(nombre='Form prueba 4') + self.dynamic_form5 = DynamicForm.objects.create(nombre='Form prueba 5') + self.dynamic_form6 = DynamicForm.objects.create(nombre='Form prueba 6') + self.dynamic_form_count = 6 + + self.dynamic_form2.secciones.set([self.seccion1, self.seccion5]) + self.dynamic_form3.secciones.set([self.seccion3, self.seccion3]) + + self.dynamic_form4.secciones.set([self.seccion1, self.seccion2]) + self.dynamic_form5.secciones.set([self.seccion4, self.seccion4]) + self.dynamic_form6.secciones.set([self.seccion3, self.seccion5]) + + def tests(self): + subtest_name = 'test_get_forms' + with self.subTest(subtest_name): + self.reset() + with CaptureQueriesContext(connection) as ctx: + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:dynamic_forms', token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), self.dynamic_form_count) + print(f'\nRENDIMIENTO QUERYS: {len(ctx.captured_queries)}') + + subtest_name = 'test_get_forms_incorrect_user' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:dynamic_forms', token=self.user_token, user=self.user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertFalse('data' in response.data) + + subtest_name = 'test_get_forms_substring_filter' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:dynamic_forms', query_params={'q': 'prueba 1'}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data['data']), 1) + + subtest_name = 'test_get_form_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:dynamic_forms_pk', url_kwargs={'pk': self.dynamic_form1.pk}, token=self.solicitante_token, user=self.solicitante_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['data']['nombre'], 'Form prueba 1') + + subtest_name = 'test_get_form_pk_not_found' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('get', url_name='dynamic_forms:dynamic_forms_pk', url_kwargs={'pk': 99999999999}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + subtest_name = 'test_post_form' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = { + 'nombre': 'Nuevo form', + 'seccion': self.seccion1.pk + } + response = self.perform_request('post', url_name='dynamic_forms:dynamic_forms', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(DynamicForm.objects.count(), self.dynamic_form_count+1) + + subtest_name = 'test_post_form_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + data = {} + response = self.perform_request('post', url_name='dynamic_forms:dynamic_forms', data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(DynamicForm.objects.count(), self.dynamic_form_count) + + subtest_name = 'test_put_form_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.dynamic_form1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': 'Form modificado' + } + response = self.perform_request('put', url_name='dynamic_forms:dynamic_forms_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(DynamicForm.objects.get(pk=self.dynamic_form1.pk).nombre, 'Form modificado') + + subtest_name = 'test_put_form_pk_bad_request' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + dict_original = self.dynamic_form1.__dict__.copy() + dict_original.pop('_state', None) + print(dict_original) + data = { + 'nombre': '' + } + response = self.perform_request('put', url_name='dynamic_forms:dynamic_forms_pk', url_kwargs={'pk': dict_original['id']}, data=data, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(DynamicForm.objects.get(pk=self.dynamic_form1.pk).nombre, 'Form prueba 1') + + subtest_name = 'test_delete_form_pk' + with self.subTest(subtest_name): + self.reset() + print(subtest_name) + response = self.perform_request('delete', url_name='dynamic_forms:dynamic_forms_pk', url_kwargs={'pk': self.dynamic_form1.pk}, token=self.admin_token, user=self.admin_user) + printDict(response.data) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(DynamicForm.objects.count(), self.dynamic_form_count-1) + + diff --git a/cosiap_api/dynamic_forms/urls.py b/cosiap_api/dynamic_forms/urls.py index 07e5f3b..9c81ba6 100644 --- a/cosiap_api/dynamic_forms/urls.py +++ b/cosiap_api/dynamic_forms/urls.py @@ -1,33 +1,32 @@ from django.urls import path from dynamic_forms.views import ( - OpcionAPIView, ElementosOpcionesAPIView, ElementoAPIView, - SeccionesElementosAPIView, SeccionAPIView, DynamicFormsSeccionesAPIView, - DynamicFormAPIView, RespuestaAPIView + OpcionAPIView, ElementoAPIView, SeccionAPIView, DynamicFormAPIView, RespuestaAPIView, + #DynamicFormsSeccionesAPIView, ElementosOpcionesAPIView, SeccionesElementosAPIView, ) app_name = 'dynamic_forms' urlpatterns = [ path('opciones/', OpcionAPIView.as_view(), name='opciones'), path('opciones//', OpcionAPIView.as_view(), name='opciones_pk'), - - path('elementos-opciones/', ElementosOpcionesAPIView.as_view(), name='elementos-opciones'), - path('elementos-opciones//', ElementosOpcionesAPIView.as_view(), name='elementos-opciones_pk'), + + #path('elementos-opciones/', ElementosOpcionesAPIView.as_view(), name='elementos-opciones'), + #path('elementos-opciones//', ElementosOpcionesAPIView.as_view(), name='elementos-opciones_pk'), path('elementos/', ElementoAPIView.as_view(), name='elementos'), path('elementos//', ElementoAPIView.as_view(), name='elementos_pk'), - path('secciones-elementos/', SeccionesElementosAPIView.as_view(), name='secciones_elementos'), - path('secciones-elementos//', SeccionesElementosAPIView.as_view(), name='secciones_elementos_pk'), + #path('secciones-elementos/', SeccionesElementosAPIView.as_view(), name='secciones_elementos'), + #path('secciones-elementos//', SeccionesElementosAPIView.as_view(), name='secciones_elementos_pk'), path('secciones/', SeccionAPIView.as_view(), name='secciones'), path('secciones//', SeccionAPIView.as_view(), name='secciones_pk'), - path('forms-secciones/', DynamicFormsSeccionesAPIView.as_view(), name='dynamic_forms_secciones'), - path('forms-secciones//', DynamicFormsSeccionesAPIView.as_view(), name='dynamic-forms-secciones_pk'), + #path('forms-secciones/', DynamicFormsSeccionesAPIView.as_view(), name='dynamic_forms_secciones'), + #path('forms-secciones//', DynamicFormsSeccionesAPIView.as_view(), name='dynamic-forms-secciones_pk'), path('', DynamicFormAPIView.as_view(), name='dynamic_forms'), path('/', DynamicFormAPIView.as_view(), name='dynamic_forms_pk'), - path('respuestas/', RespuestaAPIView.as_view(), name='respuestas'), + path('respuestas/', RespuestaAPIView.as_view(), name='respuestas'), #FormularioRecord path('respuestas//', RespuestaAPIView.as_view(), name='respuestas_pk'), ] diff --git a/cosiap_api/dynamic_forms/views.py b/cosiap_api/dynamic_forms/views.py index 77f1601..b818892 100644 --- a/cosiap_api/dynamic_forms/views.py +++ b/cosiap_api/dynamic_forms/views.py @@ -2,6 +2,7 @@ from common.views import BasePermissionAPIView from rest_framework.response import Response from rest_framework import status from notificaciones.mensajes import Mensaje +from django.shortcuts import get_object_or_404 from dynamic_forms.models import ( Opcion, Elemento, ElementosOpciones, Seccion, SeccionesElementos, DynamicForm, DynamicFormsSecciones, Respuesta @@ -14,301 +15,149 @@ from .serializers import ( from rest_framework.permissions import IsAuthenticated from users.permisos import es_admin, primer_login + + class BaseFormAPIView(BasePermissionAPIView): + model_class = None + serializer_class = None + genero_gramatical = False #False masculino, True Femenino + str_simple = None + str_plural = None + model_queryset = None -# APIView para Opcion -class OpcionAPIView(BasePermissionAPIView): permission_classes_create = [IsAuthenticated, es_admin] permission_classes_delete = [IsAuthenticated, es_admin] permission_classes_list = [IsAuthenticated, primer_login] permission_classes_update = [IsAuthenticated, es_admin] - def get(self, request, pk=None, *args, **kwargs): + def __init__(self): + if not self.model_queryset: + self.model_queryset = self.model_class.objects.all() + if not self.genero_gramatical: + # Masculino + self.articulo_definido = "el" + self.articulo_indefinido = "un" + self.desinencia_singular = "o" + self.desinencia_plural = "os" + else: + # Femenino + self.articulo_definido = "la" + self.articulo_indefinido = "una" + self.desinencia_singular = "a" + self.desinencia_plural = "as" + super().__init__() + + def get(self, request, pk=None, *args, **kwargs): if pk: - opcion = Opcion.objects.get(pk=pk) - serializer = OpcionSerializer(opcion) + instance = get_object_or_404(self.model_class, pk=pk) + serializer = self.serializer_class(instance) else: substring = request.query_params.get('q', None) if substring: - opciones = Opcion.objects.filter(nombre__icontains=substring) + instances = self.model_queryset.filter(nombre__icontains=substring) else: - opciones = Opcion.objects.all() - serializer = OpcionSerializer(opciones, many=True) - response_data = {'data': serializer.data} + instances = self.model_queryset + serializer = self.serializer_class(instances, many=True) + + response_data = {'data': serializer.data} return Response(response_data, status=status.HTTP_200_OK) def post(self, request): - serializer = OpcionSerializer(data=request.data) + serializer = self.serializer_class(data=request.data) if serializer.is_valid(): serializer.save() response_data = {'data': serializer.data} - Mensaje.success(response_data, 'Opción creada con éxito.') + Mensaje.success(response_data, f'{self.str_simple.capitalize()} cread{self.desinencia_singular} con éxito.') return Response(response_data, status=status.HTTP_201_CREATED) response_data = {'errors': serializer.errors} - Mensaje.warning(response_data, 'No se pudo guardar la ópcion.') + Mensaje.warning(response_data, f'No se pudo guardar {self.articulo_definido} {self.str_simple.lower()}.') Mensaje.error(response_data, serializer.errors) return Response(response_data, status=status.HTTP_400_BAD_REQUEST) def put(self, request, pk): - opcion = Opcion.objects.get(pk=pk) - serializer = OpcionSerializer(opcion, data=request.data) + instance = get_object_or_404(self.model_class, pk=pk) + serializer = self.serializer_class(instance, data=request.data) if serializer.is_valid(): serializer.save() response_data = {'data': serializer.data} - Mensaje.success(response_data, 'Opción actualizada con éxito.') + Mensaje.success(response_data, f'{self.str_simple.capitalize()} actualizad{self.desinencia_singular} con éxito.') return Response(serializer.data, status=status.HTTP_200_OK) response_data = {'errors': serializer.errors} - Mensaje.warning(response_data, 'No se pudo actualizar la opción.') + Mensaje.warning(response_data, f'No se pudo actualizar {self.articulo_definido} {self.str_simple.lower()}.') Mensaje.error(response_data, serializer.errors) return Response(response_data, status=status.HTTP_400_BAD_REQUEST) - def delete(self, request, pk): - opcion = Opcion.objects.get(pk=pk) - opcion.delete() + def delete(self, request, pk): + instance = get_object_or_404(self.model_class, pk=pk) + instance.delete() response_data = {} - Mensaje.success(response_data, 'Opción eliminada con éxito') + Mensaje.success(response_data, f'{self.str_simple.capitalize()} eliminad{self.desinencia_singular} con éxito') return Response(response_data, status=status.HTTP_204_NO_CONTENT) + +# APIView para Opcion +class OpcionAPIView(BaseFormAPIView): + model_class = Opcion + serializer_class = OpcionSerializer + genero_gramatical = True #False masculino, True Femenino + str_simple = 'Opción' + str_plural = 'Opciones' # APIView para ElementosOpciones -class ElementosOpcionesAPIView(BasePermissionAPIView): - permission_classes_create = [IsAuthenticated, es_admin] - permission_classes_delete = [IsAuthenticated, es_admin] - permission_classes_list = [IsAuthenticated, primer_login] - permission_classes_update = [IsAuthenticated, es_admin] - - def get(self, request, pk=None): - if pk: - elemento_opcion = ElementosOpciones.objects.get(pk=pk) - serializer = ElementosOpcionesSerializer(elemento_opcion) - else: - elementos_opciones = ElementosOpciones.objects.all() - serializer = ElementosOpcionesSerializer(elementos_opciones, many=True) - response_data = {'data': serializer.data} - return Response(response_data, status=status.HTTP_200_OK) - - def post(self, request): - serializer = ElementosOpcionesSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - response_data = {'data': serializer.data} - Mensaje.success(response_data, 'Relacion Elmento Opcion creada con éxito.') - return Response(response_data, status=status.HTTP_201_CREATED) - response_data = {'errors': serializer.errors} - Mensaje.error(response_data, serializer.errors) - return Response(response_data, status=status.HTTP_400_BAD_REQUEST) - - def put(self, request, pk): - elemento_opcion = ElementosOpciones.objects.get(pk=pk) - serializer = ElementosOpcionesSerializer(elemento_opcion, data=request.data) - if serializer.is_valid(): - serializer.save() - response_data = {'data': serializer.data} - Mensaje.success(response_data, 'Relacion modificada con éxito.') - return Response(response_data, status=status.HTTP_200_OK) - response_data = {'errors': serializer.errors} - Mensaje.success(response_data, serializer.errors) - return Response(response_data, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk): - elemento_opcion = ElementosOpciones.objects.get(pk=pk) - elemento_opcion.delete() - response_data = {} - Mensaje.success(response_data, 'Relacion Elemento Opcion eliminada con éxito') - return Response(response_data, status=status.HTTP_204_NO_CONTENT) +''' +class ElementosOpcionesAPIView(BaseFormAPIView): + model_class = ElementosOpciones + serializer_class = ElementosOpcionesSerializer + genero_gramatical = True #False masculino, True Femenino + str_simple = 'Relación Elemento Opción' + str_plural = 'Relaciones Elemento Opción' +''' # APIView para Elemento -class ElementoAPIView(BasePermissionAPIView): - permission_classes_create = [IsAuthenticated, es_admin] - permission_classes_delete = [IsAuthenticated, es_admin] - permission_classes_list = [IsAuthenticated, primer_login] - permission_classes_update = [IsAuthenticated, es_admin] - - def get(self, request, pk=None): - if pk: - elemento = Elemento.objects.get(pk=pk) - serializer = ElementoSerializer(elemento) - else: - substring = request.query_params.get('q', None) - if substring: - elementos = Elemento.objects.filter(nombre__icontains=substring) - else: - elementos = Elemento.objects.all() - serializer = ElementoSerializer(elementos, many=True) - response_data = {'data': serializer.data} - return Response(response_data, status=status.HTTP_200_OK) - - def post(self, request): - serializer = ElementoSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - response_data = {'data': serializer.data} - Mensaje.success(response_data, 'Elemento creado con éxito.') - return Response(response_data, status=status.HTTP_201_CREATED) - response_data = {'errors': serializer.errors} - Mensaje.warning(response_data, 'No se pudo guardar el elemento.') - Mensaje.error(response_data, serializer.errors) - return Response(response_data, status=status.HTTP_400_BAD_REQUEST) - - def put(self, request, pk): - elemento = Elemento.objects.get(pk=pk) - serializer = ElementoSerializer(elemento, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk): - elemento = Elemento.objects.get(pk=pk) - elemento.delete() - return Response(status=status.HTTP_204_NO_CONTENT) +class ElementoAPIView(BaseFormAPIView): + model_class = Elemento + model_queryset = Elemento.objects.all().prefetch_related('elementosopciones_set__opcion') + serializer_class = ElementoSerializer + genero_gramatical = False #False masculino, True Femenino + str_simple = 'Elemento' + str_plural = 'Elementos' # APIView para SeccionesElementos -class SeccionesElementosAPIView(BasePermissionAPIView): - permission_classes_create = [IsAuthenticated, es_admin] - permission_classes_delete = [IsAuthenticated, es_admin] - permission_classes_list = [IsAuthenticated, primer_login] - permission_classes_update = [IsAuthenticated, es_admin] - - def get(self, request, pk=None): - if pk: - seccion_elemento = SeccionesElementos.objects.get(pk=pk) - serializer = SeccionesElementosSerializer(seccion_elemento) - else: - secciones_elementos = SeccionesElementos.objects.all() - serializer = SeccionesElementosSerializer(secciones_elementos, many=True) - response_data = {'data': serializer.data} - return Response(response_data, status=status.HTTP_200_OK) - - def post(self, request): - serializer = SeccionesElementosSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def put(self, request, pk): - seccion_elemento = SeccionesElementos.objects.get(pk=pk) - serializer = SeccionesElementosSerializer(seccion_elemento, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk): - seccion_elemento = SeccionesElementos.objects.get(pk=pk) - seccion_elemento.delete() - return Response(status=status.HTTP_204_NO_CONTENT) +''' +class SeccionesElementosAPIView(BaseFormAPIView): + model_class = SeccionesElementos + serializer_class = SeccionesElementosSerializer + genero_gramatical = True #False masculino, True Femenino + str_simple = 'Relación Sección Elemento' + str_plural = 'Relaciones Sección Elemento' + ''' # APIView para Seccion -class SeccionAPIView(BasePermissionAPIView): - permission_classes_create = [IsAuthenticated, es_admin] - permission_classes_delete = [IsAuthenticated, es_admin] - permission_classes_list = [IsAuthenticated, primer_login] - permission_classes_update = [IsAuthenticated, es_admin] - - def get(self, request, pk=None): - if pk: - seccion = Seccion.objects.get(pk=pk) - serializer = SeccionSerializer(seccion) - else: - secciones = Seccion.objects.all() - serializer = SeccionSerializer(secciones, many=True) - response_data = {'data': serializer.data} - return Response(response_data, status=status.HTTP_200_OK) - - def post(self, request): - serializer = SeccionSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def put(self, request, pk): - seccion = Seccion.objects.get(pk=pk) - serializer = SeccionSerializer(seccion, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk): - seccion = Seccion.objects.get(pk=pk) - seccion.delete() - return Response(status=status.HTTP_204_NO_CONTENT) +class SeccionAPIView(BaseFormAPIView): + model_class = Seccion + model_queryset = Seccion.objects.all().prefetch_related('seccioneselementos_set__elemento__elementosopciones_set__opcion') + serializer_class = SeccionSerializer + genero_gramatical = True #False masculino, True Femenino + str_simple = 'Sección' + str_plural = 'Secciones' # APIView para DynamicFormsSecciones -class DynamicFormsSeccionesAPIView(BasePermissionAPIView): - permission_classes_create = [IsAuthenticated, es_admin] - permission_classes_delete = [IsAuthenticated, es_admin] - permission_classes_list = [IsAuthenticated, primer_login] - permission_classes_update = [IsAuthenticated, es_admin] - - def get(self, request, pk=None): - if pk: - dynamic_form_seccion = DynamicFormsSecciones.objects.get(pk=pk) - serializer = DynamicFormsSeccionesSerializer(dynamic_form_seccion) - else: - dynamic_forms_secciones = DynamicFormsSecciones.objects.all() - serializer = DynamicFormsSeccionesSerializer(dynamic_forms_secciones, many=True) - response_data = {'data': serializer.data} - return Response(response_data, status=status.HTTP_200_OK) - - def post(self, request): - serializer = DynamicFormsSeccionesSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def put(self, request, pk): - dynamic_form_seccion = DynamicFormsSecciones.objects.get(pk=pk) - serializer = DynamicFormsSeccionesSerializer(dynamic_form_seccion, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk): - dynamic_form_seccion = DynamicFormsSecciones.objects.get(pk=pk) - dynamic_form_seccion.delete() - return Response(status=status.HTTP_204_NO_CONTENT) +''' +class DynamicFormsSeccionesAPIView(BaseFormAPIView): + model_class = DynamicFormsSecciones + serializer_class = DynamicFormsSeccionesSerializer + genero_gramatical = True #False masculino, True Femenino + str_simple = 'Relación Formulario Sección' + str_plural = 'Relaciones Formulario Sección' + ''' # APIView para DynamicForm -class DynamicFormAPIView(BasePermissionAPIView): - permission_classes_create = [IsAuthenticated, es_admin] - permission_classes_delete = [IsAuthenticated, es_admin] - permission_classes_list = [IsAuthenticated, primer_login] - permission_classes_update = [IsAuthenticated, es_admin] - - def get(self, request, pk=None): - if pk: - dynamic_form = DynamicForm.objects.get(pk=pk) - serializer = DynamicFormSerializer(dynamic_form) - else: - dynamic_forms = DynamicForm.objects.all() - serializer = DynamicFormSerializer(dynamic_forms, many=True) - response_data = {'data': serializer.data} - return Response(response_data, status=status.HTTP_200_OK) - - def post(self, request): - serializer = DynamicFormSerializer(data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data, status=status.HTTP_201_CREATED) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def put(self, request, pk): - dynamic_form = DynamicForm.objects.get(pk=pk) - serializer = DynamicFormSerializer(dynamic_form, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response(serializer.data) - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - - def delete(self, request, pk): - dynamic_form = DynamicForm.objects.get(pk=pk) - dynamic_form.delete() - return Response(status=status.HTTP_204_NO_CONTENT) +class DynamicFormAPIView(BaseFormAPIView): + model_class = DynamicForm + model_queryset = DynamicForm.objects.all().prefetch_related('dynamicformssecciones_set__seccion__seccioneselementos_set__elemento__elementosopciones_set__opcion') + serializer_class = DynamicFormSerializer + genero_gramatical = False #False masculino, True Femenino + str_simple = 'Formulario' + str_plural = 'Formularios' # APIView para Respuesta class RespuestaAPIView(BasePermissionAPIView): -- GitLab