Depuração de formulários do Django

Avaliar o código de processamento de formulários do Django para identificar bugs, ineficiências e problemas de segurança.

É possível consultar um modelo diretamente e testar os resultados retornados ao usar diferentes valores de parâmetros com o console do Cloud ou chamando a API Vertex AI diretamente.

Comando Formato livre

Analise o código de processamento de formulários do Django abaixo, criado para um processo de envio de inscrição e teste de faculdade. O código gerencia a coleta e a validação das informações do candidato, incluindo detalhes pessoais, registros acadêmicos e notas de testes padronizados. Identifique bugs, ineficiências, riscos de segurança ou desvios das práticas recomendadas do Django e dos padrões PEP 8.

from django import forms
from django.core.exceptions import ValidationError
from college.models import Application, AcademicRecord, TestScore
from django.utils.translation import gettext_lazy as _
from django.db import transaction

class CollegeApplicationForm(forms.ModelForm):
    class Meta:
        model = Application
        fields = ['first_name', 'last_name', 'email', 'phone_number', 'address', 'birthdate', 'intended_major']

    gpa = forms.DecimalField(max_digits=4, decimal_places=2, min_value=0.0, max_value=4.0)
    sat_score = forms.IntegerField(min_value=400, max_value=1600, required=False)
    act_score = forms.IntegerField(min_value=1, max_value=36, required=False)
    transcript = forms.FileField(required=True)

    def clean(self):
        cleaned_data = super().clean()
        sat_score = cleaned_data.get('sat_score')
        act_score = cleaned_data.get('act_score')

        if not sat_score and not act_score:
            raise ValidationError(_('At least one test score (SAT or ACT) is required.'))

        if cleaned_data.get('emai') and Application.objects.filter(email=cleaned_data.get('email')).exists():
            raise ValidationError(_('An application with this email already exists.'))

        return cleaned_data

    def save(self, commit=True):
        with transaction.atomic():
            application = super().save(commit=False)

            academic_record = AcademicRecord(
                application=application,
                gpa=self.cleaned_data['gpa'],
                transcript=self.cleaned_data['transcript']
            )

            if commit:
                application.save()
                academic_record.save()

            if self.cleaned_data.get('sat_score'):
                TestScore.objects.create(
                    application=application,
                    test_type='SAT',
                    score=self.cleaned_data['sat_score']
                )

            if self.cleaned_data.get('act_score'):
                TestScore.objects.create(
                    application=application,
                    test_type='ACT',
                    score=self.cleaned_data['act_score']
                )

        return application

Resposta

Esse código tem vários problemas:

1. Violações do PEP 8:

  • Posição do campo:os campos gpa, sat_score, act_score e transcript são definidos fora da classe Meta. Eles precisam estar na classe Meta ou definidos como forms.fields separados na classe, mas não como atributos independentes.
  • Comprimento da linha:algumas linhas excedem os 79 caracteres recomendados.
  • Nomenclatura:emai é um erro de digitação no método clean.

2. Ineficiências:

  • Consultas de banco de dados em clean:a chamada Application.objects.filter(...) no método clean é ineficiente. Ele realiza uma consulta de banco de dados para cada validação de formulário. Ele precisa ser movido para um local mais adequado (por exemplo, uma função de validação personalizada ou um indicador).

3. Bugs:

  • Erro de digitação no método clean:cleaned_data.get('emai') deveria ser cleaned_data.get('email').
  • commit=False ausente em super().save():embora o método save use commit=False para application, ele está ausente na chamada super().save() dentro do bloco with transaction.atomic(). Isso pode levar a dados inconsistentes se ocorrer um erro durante a academic_record.save().

4. Riscos de segurança:

  • Processamento de upload de arquivos:o campo transcript não tem validação e sanitização adequadas. É essencial limitar os tipos e tamanhos de arquivos e verificar se há conteúdo malicioso antes de salvar para evitar vulnerabilidades.
  • Nenhuma limpeza de entrada:o formulário não limpa nenhuma entrada do usuário, deixando-o vulnerável a ataques de scripting em vários locais (XSS) e outras vulnerabilidades de injeção.

5. Práticas recomendadas do Django:

  • ModelForms e modelos relacionados:o formulário processa modelos relacionados (AcademicRecord e TestScore) diretamente no método save. Geralmente, é melhor usar ModelForms separados para modelos relacionados ou aproveitar os indicadores do Django para uma separação mais limpa das preocupações.
  • Tratamento de erros:o método save não tem tratamento de erros. Se alguma das chamadas save falhar, a transação precisará ser revertida para manter a consistência dos dados. É necessário um tratamento de erros mais robusto.
  • Opções de campo de formulário:o campo test_type em TestScore é codificado como "SAT" e "ACT". Seria melhor definir opções no modelo para torná-lo mais fácil de manter e estender.

Código melhorado:

from django import forms
from django.core.exceptions import ValidationError
from college.models import Application, AcademicRecord, TestScore
from django.utils.translation import gettext_lazy as _
from django.db import transaction
from django.core.files.storage import default_storage


class CollegeApplicationForm(forms.ModelForm):
    class Meta:
        model = Application
        fields = ['first_name', 'last_name', 'email', 'phone_number', 'address', 'birthdate', 'intended_major']
        widgets = {
            'birthdate': forms.DateInput(attrs={'type': 'date'}),
        }

    gpa = forms.DecimalField(max_digits=4, decimal_places=2, min_value=0.0, max_value=4.0, label="GPA")
    sat_score = forms.IntegerField(min_value=400, max_value=1600, required=False, label="SAT Score")
    act_score = forms.IntegerField(min_value=1, max_value=36, required=False, label="ACT Score")
    transcript = forms.FileField(required=True, label="Transcript")

    def clean_email(self):
        email = self.cleaned_data['email']
        if Application.objects.filter(email=email).exists():
            raise ValidationError(_('An application with this email already exists.'))
        return email

    def clean(self):
        cleaned_data = super().clean()
        sat_score = cleaned_data.get('sat_score')
        act_score = cleaned_data.get('act_score')

        if not sat_score and not act_score:
            raise ValidationError(_('At least one test score (SAT or ACT) is required.'))
        return cleaned_data

    def save(self, commit=True):
        try:
            with transaction.atomic():
                application = super().save(commit=False)
                application.save()  # Save the application first

                AcademicRecord.objects.create(
                    application=application,
                    gpa=self.cleaned_data['gpa'],
                    transcript=self.cleaned_data['transcript']
                )

                if self.cleaned_data.get('sat_score'):
                    TestScore.objects.create(
                        application=application,
                        test_type='SAT',
                        score=self.cleaned_data['sat_score']
                    )

                if self.cleaned_data.get('act_score'):
                    TestScore.objects.create(
                        application=application,
                        test_type='ACT',
                        score=self.cleaned_data['act_score']
                    )
            return application
        except Exception as e:
            # Handle exceptions appropriately, log the error, and potentially raise a more user-friendly exception.
            print(f"Error saving application: {e}")  # Replace with proper logging
            raise ValidationError(_("An error occurred while saving your application. Please try again later."))


Essa versão aprimorada resolve muitos dos problemas, mas outras melhorias podem incluir:

  • Validação de tipo e tamanho de arquivo:adicione validação para restringir os tipos e tamanhos de arquivo do campo transcript.
  • Verificação de antivírus/malware:integre um mecanismo para verificar se os arquivos enviados contêm conteúdo malicioso.
  • Funções de validação personalizadas:refatore a lógica de validação em funções separadas para melhorar a legibilidade e a manutenção.
  • ModelForms separados:considere usar ModelForms separados para AcademicRecord e TestScore para uma melhor organização.
  • Sinais:use os sinais do Django para uma abordagem mais desassociada ao processamento da criação de modelos relacionados.
  • Registro adequado:implemente o registro adequado para acompanhar erros e depurar problemas.

Sempre limpe completamente a entrada do usuário para evitar vulnerabilidades de segurança. Esse código revisado oferece uma base mais robusta e segura, mas outras medidas de segurança ainda são recomendadas.

Modelo: gemini-1.5-flash-002
Temperatura: 0,2
Máximo de tokens de saída: 8192
TopK: 40.0
TopP: 0,95