diff --git a/cosiap_api/common/serializers.py b/cosiap_api/common/serializers.py index 5db575078ce5353ed46cb3e685e2c76b8358e203..58c63870aae1c4eeab47fbdd7aacb09179cac0c4 100644 --- a/cosiap_api/common/serializers.py +++ b/cosiap_api/common/serializers.py @@ -1,13 +1,2 @@ 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/dynamic_forms/admin.py b/cosiap_api/dynamic_forms/admin.py index ffe0913c82a513d2e503bbf4b89e2fbbc608fd74..715f11ab2380f9d2e9350c049ebdf43394f79225 100644 --- a/cosiap_api/dynamic_forms/admin.py +++ b/cosiap_api/dynamic_forms/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from dynamic_forms.models import DynamicForm, DynamicFormsSecciones, \ Seccion, SeccionesElementos, Elemento, ElementosOpciones, Opcion, \ - RAgregacion, Respuesta, RTextoCorto, RTextoParrafo, RNumerico, \ + Registro, Respuesta, RTextoCorto, RTextoParrafo, RNumerico, \ RHora, RFecha, ROpcionMultiple, RDesplegable, RCasillas, RDocumento # Register your models here. @@ -12,7 +12,7 @@ admin.site.register(SeccionesElementos) admin.site.register(Elemento) admin.site.register(ElementosOpciones) admin.site.register(Opcion) -admin.site.register(RAgregacion) +admin.site.register(Registro) admin.site.register(Respuesta) admin.site.register(RTextoCorto) admin.site.register(RTextoParrafo) diff --git a/cosiap_api/dynamic_forms/migrations/0007_crear_modelo_registro.py b/cosiap_api/dynamic_forms/migrations/0007_crear_modelo_registro.py new file mode 100644 index 0000000000000000000000000000000000000000..18fd988df650d4ebd2ba38a2f731f415f7f84a30 --- /dev/null +++ b/cosiap_api/dynamic_forms/migrations/0007_crear_modelo_registro.py @@ -0,0 +1,52 @@ +# Generated by Django 5.0.7 on 2024-08-12 15:54 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('dynamic_forms', '0006_merge_migrations'), + ] + + operations = [ + migrations.CreateModel( + name='Registro', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('owner_id', models.PositiveIntegerField()), + ('owner_content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ('seccion', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dynamic_forms.seccion')), + ], + options={ + 'verbose_name_plural': '08. Rel Respuestas Agrecacion', + }, + ), + migrations.AddField( + model_name='respuesta', + name='registro', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='dynamic_forms.registro'), + preserve_default=False, + ), + migrations.AlterUniqueTogether( + name='respuesta', + unique_together={('registro', 'elemento')}, + ), + migrations.AddIndex( + model_name='registro', + index=models.Index(fields=['owner_content_type', 'owner_id'], name='dynamic_for_owner_c_70b1a6_idx'), + ), + migrations.RemoveField( + model_name='respuesta', + name='rAgregacion', + ), + migrations.RemoveField( + model_name='respuesta', + name='solicitud', + ), + migrations.DeleteModel( + name='RAgregacion', + ), + ] diff --git a/cosiap_api/dynamic_forms/models.py b/cosiap_api/dynamic_forms/models.py index c4f8bf1ae0d674c0d6c9e03ed4e520876f69d5aa..1609460989d530ba0511b9bbb75f34e5f82ab807 100644 --- a/cosiap_api/dynamic_forms/models.py +++ b/cosiap_api/dynamic_forms/models.py @@ -6,6 +6,8 @@ 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 +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType class Opcion(models.Model): """ @@ -139,33 +141,46 @@ class DynamicFormsSecciones(models.Model): verbose_name_plural = '02. Rel ormulario - Secciones' -class RAgregacion(models.Model): +class Registro(models.Model): ''' - Modelo que define el id de una lista de respuestas de una seccion de agregcion + Modelo que define el id de una lista de respuestas de una seccion Campos: -id (id) has ''' + owner_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + owner_id = models.PositiveIntegerField() + owner = GenericForeignKey('owner_content_type', 'owner_id') + seccion = models.ForeignKey(Seccion, on_delete=models.CASCADE, null=False, blank=False) + class Meta: verbose_name_plural = '08. Rel Respuestas Agrecacion' + indexes = [ + models.Index(fields=['owner_content_type', 'owner_id']), + ] class Respuesta(models.Model): ''' Modelo que define la infromacion abstracta de una respuesta. Campos: - - rAgregacion (RAgregacion, lista de seccion Agregacion a la que pertenece la respuesta.) + - registro (RAgregacion, lista de seccion Agregacion a la que pertenece la respuesta.) - solicitud (Solicitud, solicitud a la que pertenece la respuesta.) - elemento (Elemento, elemento al que pertenece la respuesta.) - otro (None, Campo donde se almacena la opcion otro, no definido en esta clase) ''' - rAgregacion = models.ForeignKey(RAgregacion, on_delete=models.CASCADE, null=True, blank=True) - solicitud = models.ForeignKey(Solicitud, on_delete=models.CASCADE) - elemento = models.ForeignKey(Elemento, on_delete=models.CASCADE) + registro = models.ForeignKey(Registro, on_delete=models.CASCADE, null=False, blank=False) + elemento = models.ForeignKey(Elemento, on_delete=models.CASCADE, null=False, blank=False) valor = None otro = None objects = InheritanceManager() + class Meta: + verbose_name = 'Respuesta' + verbose_name_plural = '09. Respuestas' + unique_together = ('registro', 'elemento') + + def __str__(self): return f"Respuesta {type(self)} - Elemento: {self.elemento} - Solicitante: {self.solicitud.solicitante_id}" @@ -174,17 +189,14 @@ class Respuesta(models.Model): if self.elemento.seccioneselementos_set.filter(seccion__tipo='unico').exists(): 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: - verbose_name = 'Respuesta' - verbose_name_plural = '09. Respuestas' + super().save(*args, **kwargs) def getStringValue(self): return 'Respuesta no Implementado' + class RNumerico(Respuesta): valor = models.CharField(max_length=255, null=True, blank=True) diff --git a/cosiap_api/dynamic_forms/serializers.py b/cosiap_api/dynamic_forms/serializers.py index f6ac41285687c377574e93502630bf0ea012496c..65e56de3bfa2dfcc9314ed354a266f2e51d3f5ae 100644 --- a/cosiap_api/dynamic_forms/serializers.py +++ b/cosiap_api/dynamic_forms/serializers.py @@ -2,12 +2,12 @@ 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, + DynamicForm, DynamicFormsSecciones, Registro, Respuesta, RNumerico, RTextoCorto, RTextoParrafo, RHora, RFecha, ROpcionMultiple, RCasillas, RDesplegable, RDocumento ) from django.core.validators import MinLengthValidator, MaxLengthValidator -from common.serializers import DynamicModelSerializer +from django.contrib.contenttypes.models import ContentType class BaseDynamicFormSerializer(serializers.ModelSerializer): def __str__(self): @@ -106,6 +106,22 @@ class DynamicFormSerializer(BaseDynamicFormSerializer): fields = '__all__' + +###RESPUESTAS + +class DynamicModelSerializer(serializers.ModelSerializer): + def __init__(self, *args, **kwargs): + # Expecting 'model' to be passed in kwargs + model = kwargs.pop('model_class', None) + if model: + self.Meta.model = model + super().__init__(*args, **kwargs) + + class Meta: + model = None + fields = '__all__' + + RESPUESTA_MODELOS = { 'RNumerico': RNumerico, 'RTextoCorto': RTextoCorto, @@ -118,14 +134,23 @@ RESPUESTA_MODELOS = { 'RDocumento': RDocumento, } -class RespuestaSerializer(serializers.ModelSerializer): - class Meta: - model = Respuesta - fields = '__all__' +class RespuestaFormularioSerializer(DynamicModelSerializer): def to_representation(self, instance): - serializer = DynamicModelSerializer(instance=instance, model=type(instance)) - return serializer.data + # Obtiene el content_type del modelo proporcionado + content_type = ContentType.objects.get_for_model(self.model_class) + + if self.context.get('many', False): + # Si es many=True, obtiene todos los registros cuyo owner sea del tipo de modelo dado + registros = Registro.objects.filter(owner_content_type=content_type) + # Mapea los registros a sus respuestas correspondientes + respuestas = Respuesta.objects.filter(registro__in=registros) + return super().to_representation(respuestas, many=True) + else: + # Si es una instancia única, obtiene el registro correspondiente al owner específico + registro = Registro.objects.get(owner_content_type=content_type, owner_id=instance.id) + respuesta = Respuesta.objects.get(registro=registro) + return super().to_representation(respuesta) def to_internal_value(self, data): # Implementación del mapeo inverso según sea necesario diff --git a/cosiap_api/dynamic_forms/views.py b/cosiap_api/dynamic_forms/views.py index fc90720438ea52eb5a5c7f2e82ae3f07f05d2bd4..0d61e0f5a6bdf9865cba130477f01a43d9d28803 100644 --- a/cosiap_api/dynamic_forms/views.py +++ b/cosiap_api/dynamic_forms/views.py @@ -10,7 +10,7 @@ from dynamic_forms.models import ( from .serializers import ( OpcionSerializer, ElementoSerializer, ElementosOpcionesSerializer, SeccionSerializer, SeccionesElementosSerializer, DynamicFormSerializer, - DynamicFormsSeccionesSerializer, RespuestaSerializer + DynamicFormsSeccionesSerializer, RespuestaFormularioSerializer ) from rest_framework.permissions import IsAuthenticated from users.permisos import es_admin, primer_login @@ -221,15 +221,15 @@ class RespuestaAPIView(BasePermissionAPIView): def get(self, request, pk=None): if pk: respuesta = Respuesta.objects.get(pk=pk) - serializer = RespuestaSerializer(respuesta) + serializer = RespuestaFormularioSerializer(respuesta) else: respuestas = Respuesta.objects.all() - serializer = RespuestaSerializer(respuestas, many=True) + serializer = RespuestaFormularioSerializer(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) + serializer = RespuestaFormularioSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -237,7 +237,7 @@ class RespuestaAPIView(BasePermissionAPIView): def put(self, request, pk): respuesta = Respuesta.objects.get(pk=pk) - serializer = RespuestaSerializer(respuesta, data=request.data) + serializer = RespuestaFormularioSerializer(respuesta, data=request.data) if serializer.is_valid(): serializer.save() return Response(serializer.data)