Source code for src.dieta_especial.solicitacao_dieta_especial.models

"""Modelos do submódulo de solicitações de dieta especial.

Define o modelo central :class:`SolicitacaoDietaEspecial`, os catálogos de
apoio (motivos de negação, motivos de alteração de U.E., alergias e
intolerâncias, classificações de dieta), o :class:`Anexo` e a view não
gerenciada :class:`SolicitacoesDietaEspecialAtivasInativasPorAluno`.

As listas de constantes no topo do módulo agrupam os eventos de log
(:class:`~src.dados_comuns.models.LogSolicitacoesUsuario`) que caracterizam
cada situação da solicitação (pendente, autorizada, negada ou cancelada).
"""

from __future__ import annotations

import datetime
from typing import Any

from django.core.exceptions import ValidationError
from django.core.validators import MaxLengthValidator, MinLengthValidator
from django.db import models
from django.db.models import Q, QuerySet
from django_prometheus.models import ExportModelOperationsMixin

from src.dados_comuns.behaviors import (
    Ativavel,
    CriadoEm,
    CriadoPor,
    Descritivel,
    Logs,
    Nomeavel,
    TemChaveExterna,
    TemIdentificadorExternoAmigavel,
    TemPrioridade,
)
from src.dados_comuns.fluxo_status import FluxoDietaEspecialPartindoDaEscola
from src.dados_comuns.models import LogSolicitacoesUsuario
from src.dados_comuns.utils import convert_base64_to_contentfile
from src.escola.api.serializers import AlunoSerializer
from src.escola.models import Aluno, Escola

PENDENTES_EVENTO_DIETA_ESPECIAL: list[int] = [
    LogSolicitacoesUsuario.INICIO_FLUXO,
    LogSolicitacoesUsuario.INICIO_FLUXO_INATIVACAO,
    LogSolicitacoesUsuario.INICIO_FLUXO_ALTERACAO_UE_DIETA_ESPECIAL,
]

AUTORIZADO_EVENTO_DIETA_ESPECIAL: list[int] = [
    LogSolicitacoesUsuario.CODAE_AUTORIZOU,
    LogSolicitacoesUsuario.TERCEIRIZADA_TOMOU_CIENCIA,
    LogSolicitacoesUsuario.CODAE_AUTORIZOU_INATIVACAO,
    LogSolicitacoesUsuario.TERCEIRIZADA_TOMOU_CIENCIA_INATIVACAO,
    LogSolicitacoesUsuario.INICIO_FLUXO,
    LogSolicitacoesUsuario.CODAE_AUTORIZOU_ALTERACAO_UE_DIETA_ESPECIAL,
]

NEGADOS_EVENTO_DIETA_ESPECIAL: list[int] = [
    LogSolicitacoesUsuario.CODAE_NEGOU,
    LogSolicitacoesUsuario.CODAE_NEGOU_INATIVACAO,
    LogSolicitacoesUsuario.CODAE_NEGOU_CANCELAMENTO,
    LogSolicitacoesUsuario.CODAE_NEGOU_ALTERACAO_UE_DIETA_ESPECIAL,
]

CANCELADOS_EVENTO_DIETA_ESPECIAL: list[int] = [
    LogSolicitacoesUsuario.ESCOLA_CANCELOU,
    LogSolicitacoesUsuario.CODAE_AUTORIZOU_CANCELAMENTO_DIETA_ESPECIAL,
    LogSolicitacoesUsuario.CANCELADO_ALUNO_MUDOU_ESCOLA,
    LogSolicitacoesUsuario.CANCELADO_ALUNO_NAO_PERTENCE_REDE,
    LogSolicitacoesUsuario.TERMINADA_AUTOMATICAMENTE_SISTEMA,
    LogSolicitacoesUsuario.CANCELADO_ENCERRAMENTO_MATRICULA,
]

CANCELADOS_EVENTO_DIETA_ESPECIAL_TEMP: list[int] = [
    LogSolicitacoesUsuario.ESCOLA_CANCELOU,
    LogSolicitacoesUsuario.CODAE_AUTORIZOU_INATIVACAO,
    LogSolicitacoesUsuario.TERMINADA_AUTOMATICAMENTE_SISTEMA,
    LogSolicitacoesUsuario.TERCEIRIZADA_TOMOU_CIENCIA,
    LogSolicitacoesUsuario.CODAE_AUTORIZOU_CANCELAMENTO_DIETA_ESPECIAL,
    LogSolicitacoesUsuario.CANCELADO_ENCERRAMENTO_MATRICULA,
]


[docs] class MotivoNegacao(Descritivel): """Catálogo de motivos de negação de uma solicitação de dieta especial. Attributes: processo (str): Processo ao qual o motivo se aplica. Pode ser ``CANCELAMENTO`` (solicitação de cancelamento de uma dieta especial ativa) ou ``INCLUSAO`` (solicitação de inclusão de uma nova dieta especial). """ CANCELAMENTO = "CANCELAMENTO" INCLUSAO = "INCLUSAO" PROCESSO_CHOICES = ( (CANCELAMENTO, "Solicitação de Cancelamento"), (INCLUSAO, "Solicitação de Inclusão"), ) processo = models.CharField( choices=PROCESSO_CHOICES, default=INCLUSAO, blank=False, max_length=20 ) def __str__(self) -> str: """Retorna a descrição do motivo de negação.""" return self.descricao
[docs] class MotivoAlteracaoUE(Descritivel, Nomeavel, TemChaveExterna, Ativavel): """Catálogo de motivos de alteração de unidade educacional da dieta. Motivos possíveis: - **Recreio nas Férias**: a criança vai passar alguns dias em outra unidade educacional durante as férias escolares e precisa da sua dieta especial atendida nessa outra unidade. - **Outro**: a criança vai passar alguns dias em outra unidade educacional por algum outro motivo e precisa da sua dieta especial atendida nessa outra unidade. """ def __str__(self) -> str: """Retorna o nome do motivo de alteração de U.E.""" return self.nome class Meta: verbose_name = "Motivo Alteração U.E" verbose_name_plural = "Motivo Alteração U.E"
[docs] class AlergiaIntolerancia(Descritivel): """Catálogo de alergias e intolerâncias alimentares (diagnósticos). Exemplos de diagnósticos: - Alergias (ovo, leite, amendoim) - TEA - Diabetes - Doenças - Intolerâncias """ def __str__(self) -> str: """Retorna a descrição da alergia/intolerância.""" return self.descricao
[docs] class ClassificacaoDieta(Descritivel, Nomeavel): """Catálogo de classificações de dieta especial. Classificações possíveis: - **Tipo A**: alimentos diferenciados mais caros para troca; a troca vale apenas para o lanche. - **Tipo A Enteral / Aminoácidos**: alimentos caros para troca igual ao tipo A; a troca vale para lanche e refeição. - **Tipo B**: alimentos diferenciados, com menor custo que os do tipo A. - **Tipo C**: apenas altera algum ingrediente por outro, sem custo adicional. Ex.: troca peixe por carne. """ def __str__(self) -> str: """Retorna o nome da classificação de dieta.""" return self.nome
[docs] class SolicitacaoDietaEspecial( ExportModelOperationsMixin("dieta_especial"), TemChaveExterna, CriadoEm, CriadoPor, FluxoDietaEspecialPartindoDaEscola, TemPrioridade, Logs, TemIdentificadorExternoAmigavel, Ativavel, ): """Solicitação de dieta especial de um aluno. Modelo central do módulo de dieta especial. Representa a solicitação de dieta especial de um aluno e percorre um fluxo de autorização que envolve a escola e a CODAE. Pode ser uma inclusão comum, uma alteração de U.E., um cancelamento de dieta ou uma solicitação para aluno não matriculado. """ COMUM = "COMUM" ALUNO_NAO_MATRICULADO = "ALUNO_NAO_MATRICULADO" ALTERACAO_UE = "ALTERACAO_UE" CANCELAMENTO_DIETA = "CANCELAMENTO_DIETA" DESCRICAO_SOLICITACAO = { "CODAE_A_AUTORIZAR": "Solicitação de Inclusão", "CODAE_NEGOU_PEDIDO": "Negada a Inclusão", "CODAE_AUTORIZADO": "Autorizada", "ESCOLA_SOLICITOU_INATIVACAO": "Solicitação de Cancelamento", "CODAE_NEGOU_INATIVACAO": "Negada o Cancelamento", "CODAE_AUTORIZOU_INATIVACAO": "Cancelamento Autorizado", "ESCOLA_CANCELOU": "Cancelada pela Unidade Escolar", } TIPO_SOLICITACAO_CHOICES = [ (COMUM, "Comum"), (ALUNO_NAO_MATRICULADO, "Aluno não matriculado"), (ALTERACAO_UE, "Alteração U.E"), (CANCELAMENTO_DIETA, "Cancelamento de dieta especial"), ] aluno = models.ForeignKey( "escola.Aluno", null=True, on_delete=models.PROTECT, related_name="dietas_especiais", ) nome_completo_pescritor = models.CharField( "Nome completo do pescritor da receita", max_length=200, validators=[MinLengthValidator(6)], blank=True, ) registro_funcional_pescritor = models.CharField( "Registro funcional do pescritor da receita", help_text="CRN/CRM/CRFa...", max_length=200, validators=[MinLengthValidator(4), MaxLengthValidator(6)], blank=True, ) registro_funcional_nutricionista = models.CharField( "Registro funcional do nutricionista", help_text="CRN/CRM/CRFa...", max_length=200, validators=[MinLengthValidator(6)], blank=True, ) # Preenchido pela Escola observacoes = models.TextField("Observações", blank=True) # Preenchido pela_ CODAE ao autorizar a dieta informacoes_adicionais = models.TextField("Informações Adicionais", blank=True) protocolo_padrao = models.ForeignKey( "ProtocoloPadraoDietaEspecial", on_delete=models.PROTECT, related_name="solicitacoes_dietas_especiais", blank=True, null=True, ) nome_protocolo = models.TextField("Nome do Protocolo", blank=True) # Preenchido pela NutriCODAE ao autorizar a dieta orientacoes_gerais = models.TextField("Orientações Gerais", blank=True) # TODO: Confirmar se PROTECT é a melhor escolha para o campos abaixo classificacao = models.ForeignKey( "ClassificacaoDieta", blank=True, null=True, on_delete=models.PROTECT ) alergias_intolerancias = models.ManyToManyField("AlergiaIntolerancia", blank=True) # TODO: Confirmar se PROTECT é a melhor escolha para o campos abaixo motivo_negacao = models.ForeignKey( "MotivoNegacao", on_delete=models.PROTECT, null=True ) # TODO: Mover essa justificativa para o log de transição de status justificativa_negacao = models.TextField(blank=True) data_termino = models.DateField(null=True) motivo_alteracao_ue = models.ForeignKey( "MotivoAlteracaoUE", blank=True, null=True, on_delete=models.CASCADE ) escola_destino = models.ForeignKey( "escola.Escola", blank=True, null=True, on_delete=models.CASCADE ) dieta_alterada = models.ForeignKey( "self", blank=True, null=True, on_delete=models.CASCADE ) data_inicio = models.DateField(null=True, blank=True) tipo_solicitacao = models.CharField( max_length=30, choices=TIPO_SOLICITACAO_CHOICES, default="COMUM", ) observacoes_alteracao = models.TextField("Observações Alteração", blank=True) caracteristicas_do_alimento = models.TextField( "Características dos alimentos", blank=True ) conferido = models.BooleanField("Marcar como conferido?", default=False) eh_importado = models.BooleanField("Proveniente de importacao?", default=False) dieta_para_recreio_ferias = models.BooleanField( "Dieta para Recreio nas Férias", default=False ) @classmethod def _get_quantidade_solicitacoes_que_ja_estiveram_nos_status( cls, solicitacoes: QuerySet[SolicitacaoDietaEspecial], status: list[int] ) -> int: """Conta quantas solicitações distintas já passaram pelos status informados. Args: solicitacoes (QuerySet[SolicitacaoDietaEspecial]): Queryset de solicitações a analisar. status (list[int]): Eventos de log que caracterizam os status. Returns: int: Quantidade de solicitações que já estiveram em ao menos um dos status informados. """ uuids = set(list(solicitacoes.values_list("uuid", flat=True))) return len( set( LogSolicitacoesUsuario.objects.filter( uuid_original__in=uuids, status_evento__in=status ).values_list("uuid_original", flat=True) ) )
[docs] @classmethod def quantidade_solicitacoes_que_ja_estiveram_pendentes( cls, solicitacoes: QuerySet[SolicitacaoDietaEspecial] ) -> int: """Conta quantas solicitações do queryset já estiveram pendentes. Args: solicitacoes (QuerySet[SolicitacaoDietaEspecial]): Queryset de solicitações a analisar. Returns: int: Quantidade de solicitações que já estiveram pendentes. """ return cls._get_quantidade_solicitacoes_que_ja_estiveram_nos_status( solicitacoes, PENDENTES_EVENTO_DIETA_ESPECIAL )
[docs] @classmethod def quantidade_solicitacoes_que_ja_estiveram_autorizadas( cls, solicitacoes: QuerySet[SolicitacaoDietaEspecial] ) -> int: """Conta quantas solicitações do queryset já estiveram autorizadas. Args: solicitacoes (QuerySet[SolicitacaoDietaEspecial]): Queryset de solicitações a analisar. Returns: int: Quantidade de solicitações que já estiveram autorizadas. """ return cls._get_quantidade_solicitacoes_que_ja_estiveram_nos_status( solicitacoes, AUTORIZADO_EVENTO_DIETA_ESPECIAL )
[docs] @classmethod def quantidade_solicitacoes_que_ja_estiveram_negadas( cls, solicitacoes: QuerySet[SolicitacaoDietaEspecial] ) -> int: """Conta quantas solicitações do queryset já estiveram negadas. Args: solicitacoes (QuerySet[SolicitacaoDietaEspecial]): Queryset de solicitações a analisar. Returns: int: Quantidade de solicitações que já estiveram negadas. """ return cls._get_quantidade_solicitacoes_que_ja_estiveram_nos_status( solicitacoes, NEGADOS_EVENTO_DIETA_ESPECIAL )
[docs] @classmethod def quantidade_solicitacoes_que_ja_estiveram_canceladas( cls, solicitacoes: QuerySet[SolicitacaoDietaEspecial] ) -> int: """Conta quantas solicitações do queryset já estiveram canceladas. Considera apenas solicitações do tipo ``ALTERACAO_UE`` e ``COMUM``. Args: solicitacoes (QuerySet[SolicitacaoDietaEspecial]): Queryset de solicitações a analisar. Returns: int: Quantidade de solicitações que já estiveram canceladas. """ solicitacoes = solicitacoes.filter( tipo_solicitacao__in=["ALTERACAO_UE", "COMUM"] ) status = ( CANCELADOS_EVENTO_DIETA_ESPECIAL_TEMP + CANCELADOS_EVENTO_DIETA_ESPECIAL ) return cls._get_quantidade_solicitacoes_que_ja_estiveram_nos_status( solicitacoes, status )
[docs] @classmethod def get_totais_gerencial_dietas( cls, queryset: QuerySet[SolicitacaoDietaEspecial] | None = None ) -> dict[str, int]: """Calcula os totais gerenciais de dietas (solicitadas e por situação). Args: queryset (QuerySet[SolicitacaoDietaEspecial] | None): Queryset opcional de solicitações. Se ``None``, usa todas as solicitações. Returns: dict[str, int]: Dicionário com os totais de solicitações, autorizadas, negadas e canceladas. """ queryset = ( SolicitacaoDietaEspecial.objects.all() if queryset is None else queryset ) total_solicitacoes = ( SolicitacaoDietaEspecial.quantidade_solicitacoes_que_ja_estiveram_pendentes( queryset ) ) total_autorizadas = SolicitacaoDietaEspecial.quantidade_solicitacoes_que_ja_estiveram_autorizadas( queryset ) total_negadas = ( SolicitacaoDietaEspecial.quantidade_solicitacoes_que_ja_estiveram_negadas( queryset ) ) total_canceladas = SolicitacaoDietaEspecial.quantidade_solicitacoes_que_ja_estiveram_canceladas( queryset ) return { "total_solicitacoes": total_solicitacoes, "total_autorizadas": total_autorizadas, "total_negadas": total_negadas, "total_canceladas": total_canceladas, }
[docs] @classmethod def aluno_possui_dieta_especial_autorizada_alteracao_ue_recreio_ferias( cls, aluno: Aluno, dieta_alterada: SolicitacaoDietaEspecial, motivo_recreio_ferias: MotivoAlteracaoUE, ) -> bool: """Verifica se o aluno já possui alteração de U.E. autorizada para recreio nas férias. Args: aluno (Aluno): Aluno a verificar. dieta_alterada (SolicitacaoDietaEspecial): Dieta alterada (solicitação original). motivo_recreio_ferias (MotivoAlteracaoUE): Motivo de alteração de U.E. de recreio nas férias. Returns: bool: ``True`` se já existir alteração de U.E. autorizada com o motivo informado; ``False`` caso contrário. """ return cls.objects.filter( aluno=aluno, dieta_alterada=dieta_alterada, status=SolicitacaoDietaEspecial.workflow_class.CODAE_AUTORIZADO, tipo_solicitacao="ALTERACAO_UE", motivo_alteracao_ue=motivo_recreio_ferias, ).exists()
[docs] @classmethod def aluno_possui_dieta_especial_pendente(cls, aluno: Aluno) -> bool: """Verifica se o aluno possui alguma dieta especial pendente de autorização. Args: aluno (Aluno): Aluno a verificar. Returns: bool: ``True`` se existir dieta especial com status ``CODAE_A_AUTORIZAR``; ``False`` caso contrário. """ return cls.objects.filter( aluno=aluno, status=cls.workflow_class.CODAE_A_AUTORIZAR ).exists()
@property def DESCRICAO(self) -> str: """Retorna a descrição legível da solicitação conforme o status atual. Returns: str: Texto no formato ``"Dieta Especial - {descricao}"``, ou ``"Dieta Especial"`` quando não há descrição mapeada. """ descricao = self.DESCRICAO_SOLICITACAO.get(self.status) return f"Dieta Especial - {descricao}" if descricao else "Dieta Especial" # Property necessária para retornar dados no serializer de criação de # Dieta Especial @property def aluno_json(self) -> dict: """Retorna os dados do aluno serializados. Returns: dict: Dicionário com os dados do aluno retornados pelo :class:`~src.escola.api.serializers.AlunoSerializer`. """ return AlunoSerializer(self.aluno).data @property def anexos(self) -> QuerySet[Anexo]: """Retorna os anexos associados à solicitação. Returns: QuerySet[Anexo]: Queryset com os anexos da solicitação. """ return self.anexo_set.all() @property def numero_alunos(self) -> str: """Retorna a quantidade de alunos associada à solicitação. Returns: str: String vazia (mantida por compatibilidade com o serializer). """ return "" @property def escola(self) -> Escola | None: """Retorna a escola vinculada ao rastro da solicitação. Returns: Escola | None: Escola associada ao rastro da solicitação, ou ``None`` se não houver rastro. """ return self.rastro_escola
[docs] def cria_anexos_inativacao(self, anexos: list[dict[str, Any]]) -> None: """Cria os anexos de laudo de alta para a inativação da dieta. Args: anexos (list[dict[str, Any]]): Lista de anexos, em que cada item possui as chaves ``base64`` (conteúdo do arquivo) e ``nome`` (nome do arquivo). Raises: AssertionError: Se ``anexos`` não for uma lista não vazia. """ assert isinstance(anexos, list), "anexos precisa ser uma lista" # nosec assert len(anexos) > 0, "anexos não pode ser vazio" # nosec for anexo in anexos: data = convert_base64_to_contentfile(anexo.get("base64")) Anexo.objects.create( solicitacao_dieta_especial=self, arquivo=data, nome=anexo.get("nome", ""), eh_laudo_alta=True, )
@property def substituicoes(self) -> QuerySet: """Retorna as substituições de alimento associadas à solicitação. Returns: QuerySet: Queryset de :class:`~src.dieta_especial.protocolo_padrao.models.SubstituicaoAlimento`. """ return self.substituicaoalimento_set.all() @property def str_dre_lote_escola(self) -> str: """Monta uma string legível com DRE, lote e escola de destino. Returns: str: Texto no formato ``"DRE X - LOTE - ESCOLA"``, usando ``"SEM DRE"``, ``"SEM LOTE"`` e ``"SEM ESCOLA"`` quando os dados não estiverem disponíveis. """ dre = "SEM DRE" lote = "SEM LOTE" escola = "SEM ESCOLA" if self.escola_destino: escola = f"{self.escola_destino.nome}" if self.escola_destino.diretoria_regional: dre = ( f'DRE {self.escola_destino.diretoria_regional.nome.split(" ")[-1]}' ) if self.escola_destino.lote: lote = f"{self.escola_destino.lote.nome}" return f"{dre} - {lote} - {escola}"
[docs] def salvar_log_transicao( self, status_evento: int, usuario: Any, **kwargs: Any ) -> None: """Registra um log de transição de status da solicitação. Args: status_evento (int): Evento de log (transição de status) a gravar. usuario (perfil.Usuario): Usuário responsável pela transição. **kwargs (Any): Argumentos extras. Aceita ``justificativa`` (str) para a transição. """ justificativa = kwargs.get("justificativa", "") LogSolicitacoesUsuario.objects.create( descricao=str(self), status_evento=status_evento, solicitacao_tipo=LogSolicitacoesUsuario.DIETA_ESPECIAL, usuario=usuario, uuid_original=self.uuid, justificativa=justificativa, )
@property def display_nutricionista_with_registro_funcional(self) -> str: """Retorna o nome do nutricionista que autorizou a dieta, com registro funcional. Returns: str: Texto ``"Elaborado por {nome} - RF {registro}"`` ou, quando o usuário não possui registro funcional, o registro funcional informado na solicitação. """ usuario = self.logs.get( status_evento=LogSolicitacoesUsuario.CODAE_AUTORIZOU, solicitacao_tipo=LogSolicitacoesUsuario.DIETA_ESPECIAL, ).usuario if usuario.registro_funcional: return f"Elaborado por {usuario.nome} - RF {usuario.registro_funcional}" return self.registro_funcional_nutricionista @property def data_ultimo_log(self) -> str | None: """Retorna a data do último log da solicitação. Returns: str | None: Data formatada ``DD/MM/AAAA`` do último log, ou ``None`` se não houver logs. """ return ( datetime.datetime.strftime(self.logs.last().criado_em, "%d/%m/%Y") if self.logs else None ) @property def get_log_autorizado(self) -> LogSolicitacoesUsuario | None: """Retorna o log de autorização da solicitação, se existir. Returns: LogSolicitacoesUsuario | None: Log de autorização (``CODAE_AUTORIZOU`` ou ``CODAE_AUTORIZOU_ALTERACAO_UE_DIETA_ESPECIAL``), ou ``None`` se a solicitação ainda não foi autorizada. """ try: return self.logs.get( Q(status_evento=LogSolicitacoesUsuario.CODAE_AUTORIZOU) | Q( status_evento=LogSolicitacoesUsuario.CODAE_AUTORIZOU_ALTERACAO_UE_DIETA_ESPECIAL ) ) except LogSolicitacoesUsuario.DoesNotExist: return None
[docs] def clean(self) -> None: """Valida os campos da solicitação antes de salvar. Raises: ValidationError: Se a dieta for para recreio nas férias e não possuir período preenchido, ou se a data final for anterior à data inicial. """ super().clean() if self.dieta_para_recreio_ferias: if not self.data_inicio or not self.data_termino: raise ValidationError( "Os campos de período são obrigatórios quando dieta para recreio nas férias está selecionada." ) if self.data_termino < self.data_inicio: raise ValidationError( "A data final não pode ser anterior à data inicial." )
class Meta: ordering = ("-ativo", "-criado_em") verbose_name = "Solicitação de dieta especial" verbose_name_plural = "Solicitações de dieta especial" def __str__(self) -> str: """Retorna a representação legível da solicitação. Returns: str: ``"{código_eol}: {nome}"`` do aluno, ou ``"Solicitação #{id_externo}"`` quando não há aluno vinculado. """ if self.aluno: return f"{self.aluno.codigo_eol}: {self.aluno.nome}" return f"Solicitação #{self.id_externo}"
[docs] class Anexo(ExportModelOperationsMixin("anexo"), models.Model): """Anexo (arquivo) vinculado a uma solicitação de dieta especial. Attributes: solicitacao_dieta_especial (SolicitacaoDietaEspecial): Solicitação à qual o anexo pertence. nome (str): Nome do arquivo. arquivo (FileField): Arquivo enviado. eh_laudo_alta (bool): Indica se o anexo é um laudo de alta. """ solicitacao_dieta_especial = models.ForeignKey( SolicitacaoDietaEspecial, on_delete=models.CASCADE ) nome = models.CharField(max_length=100, blank=True) arquivo = models.FileField() eh_laudo_alta = models.BooleanField(default=False) def __str__(self) -> str: """Retorna o nome do anexo.""" return self.nome
[docs] class SolicitacoesDietaEspecialAtivasInativasPorAluno(models.Model): """View não gerenciada com a quantidade de dietas ativas e inativas por aluno. Attributes: aluno (Aluno): Aluno referenciado (chave primária). ativas (int): Quantidade de dietas ativas do aluno. inativas (int): Quantidade de dietas inativas do aluno. """ aluno = models.OneToOneField(Aluno, on_delete=models.DO_NOTHING, primary_key=True) ativas = models.IntegerField() inativas = models.IntegerField() class Meta: managed = False db_table = "dietas_ativas_inativas_por_aluno"