Proses debug formulir Django

Mengevaluasi kode penanganan formulir Django untuk mengidentifikasi bug, inefisiensi, dan masalah keamanan.

Anda dapat membuat kueri model secara langsung dan menguji hasil yang ditampilkan saat menggunakan parameter value yang berbeda dengan Konsol Cloud, atau dengan memanggil Vertex AI API secara langsung.

Perintah Freeform

Tinjau kode penanganan formulir Django berikut yang dirancang untuk proses pengiriman aplikasi dan pengujian perguruan tinggi. Kode ini mengelola pengumpulan dan validasi informasi pelamar, termasuk detail pribadi, catatan akademik, dan skor tes standar. Identifikasi bug, inefisiensi, risiko keamanan, atau penyimpangan dari praktik terbaik Django dan standar 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

Respons

Kode ini memiliki beberapa masalah:

1. Pelanggaran PEP 8:

  • Penempatan kolom: Kolom gpa, sat_score, act_score, dan transcript ditentukan di luar class Meta. Atribut tersebut harus berada dalam class Meta atau ditentukan sebagai forms.fields terpisah dalam class, tetapi bukan sebagai atribut mandiri.
  • Panjang baris: Beberapa baris melebihi 79 karakter yang direkomendasikan.
  • Penamaan: emai adalah kesalahan ketik dalam metode clean.

2. Inefisiensi:

  • Kueri database di clean: Panggilan Application.objects.filter(...) dalam metode clean tidak efisien. Fungsi ini menjalankan kueri database untuk setiap validasi formulir. Ini harus dipindahkan ke lokasi yang lebih sesuai (misalnya, fungsi validasi kustom atau sinyal).

3. Bug:

  • Kesalahan ketik dalam metode clean: cleaned_data.get('emai') harus cleaned_data.get('email').
  • commit=False tidak ada di super().save(): Meskipun metode save menggunakan commit=False untuk application, metode tersebut tidak ada dalam panggilan super().save() dalam blok with transaction.atomic(). Hal ini dapat menyebabkan data tidak konsisten jika terjadi error selama academic_record.save().

4. Risiko Keamanan:

  • Penanganan upload file: Kolom transcript tidak memiliki validasi dan pembersihan yang tepat. Anda harus membatasi jenis file, ukuran, dan berpotensi memindai konten berbahaya sebelum menyimpan untuk mencegah kerentanan.
  • Tidak ada pembersihan input: Formulir tidak membersihkan input pengguna, sehingga rentan terhadap serangan pembuatan skrip lintas situs (XSS) dan kerentanan injection lainnya.

5. Praktik Terbaik Django:

  • ModelForms dan model terkait: Formulir menangani model terkait (AcademicRecord dan TestScore) langsung dalam metode save. Umumnya lebih baik menggunakan ModelForms terpisah untuk model terkait atau memanfaatkan sinyal Django untuk pemisahan masalah yang lebih rapi.
  • Penanganan error: Metode save tidak memiliki penanganan error. Jika salah satu panggilan save gagal, transaksi harus di-roll back untuk mempertahankan konsistensi data. Diperlukan penanganan error yang lebih andal.
  • Pilihan kolom formulir: Kolom test_type di TestScore di-hardcode sebagai 'SAT' dan 'ACT'. Sebaiknya tentukan pilihan dalam model agar lebih mudah dikelola dan diperluas.

Kode yang Ditingkatkan:

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."))


Versi yang ditingkatkan ini mengatasi banyak masalah, tetapi peningkatan lebih lanjut dapat mencakup:

  • Validasi jenis dan ukuran file: Menambahkan validasi untuk membatasi jenis dan ukuran file untuk kolom transcript.
  • Pemindaian antivirus/malware: Mengintegrasikan mekanisme untuk memindai file yang diupload guna mendeteksi konten berbahaya.
  • Fungsi validasi kustom: Faktorkan ulang logika validasi menjadi fungsi terpisah untuk meningkatkan keterbacaan dan pengelolaan.
  • ModelForms Terpisah: Pertimbangkan untuk menggunakan ModelForms terpisah untuk AcademicRecord dan TestScore guna mendapatkan pengaturan yang lebih baik.
  • Sinyal: Gunakan sinyal Django untuk pendekatan yang lebih terpisah dalam menangani pembuatan model terkait.
  • Pembuatan log yang tepat: Terapkan pembuatan log yang tepat untuk melacak error dan men-debug masalah.

Ingatlah untuk selalu membersihkan input pengguna secara menyeluruh untuk mencegah kerentanan keamanan. Kode yang direvisi ini memberikan fondasi yang lebih andal dan aman, tetapi langkah-langkah keamanan lebih lanjut tetap direkomendasikan.

Model: gemini-1.5-flash-002
Suhu: 0,2
Token output maks: 8192
TopK: 40,0
TopP: 0,95