Django フォームのデバッグ

Django フォーム処理コードを評価して、バグ、非効率性、セキュリティの問題を特定する。

モデルにクエリを直接実行して、返された結果をテストできます。その場合、Cloud コンソールでさまざまなパラメータ値を使用することも、Vertex AI API を直接呼び出すこともできます。

自由形式のプロンプト

大学の願書とテスト送信プロセス用に設計された次の Django フォーム処理コードを確認します。このコードは、個人情報、学業成績、標準化テストのスコアなど、出願者の情報を収集して検証します。バグ、非効率性、セキュリティ リスク、Django のベスト プラクティスや 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

回答

このコードにはいくつかの問題があります。

1. PEP 8 違反:

  • フィールドの配置: gpasat_scoreact_scoretranscript フィールドは Meta クラス外で定義されます。Meta クラス内にあるか、クラス内の個別の forms.fields として定義する必要があります。スタンドアロンの属性として定義することはできません。
  • 行の長さ: 推奨される 79 文字を超える行がいくつかあります。
  • 命名: emaiclean メソッドのスペルミスです。

2. 非効率性:

  • clean のデータベース クエリ: clean メソッド内の Application.objects.filter(...) 呼び出しは非効率的です。フォームの検証ごとにデータベース クエリを実行します。これは、より適切な場所(カスタム検証関数やシグナルなど)に移動する必要があります。

3. バグ:

  • clean メソッドの誤字脱字: cleaned_data.get('emai')cleaned_data.get('email') にする必要があります。
  • super().save()commit=False がない: save メソッドは applicationcommit=False を使用していますが、with transaction.atomic() ブロック内の super().save() 呼び出しにはありません。これにより、academic_record.save() 中にエラーが発生した場合にデータの不整合が生じる可能性があります。

4. セキュリティ リスク:

  • ファイルのアップロード処理: transcript フィールドに適切な検証とサニタイズがありません。脆弱性を防ぐには、ファイル形式とサイズを制限し、保存前に悪意のあるコンテンツをスキャンすることが重要です。
  • 入力のサニタイズが未処理: フォームでユーザー入力がサニタイズされないため、クロスサイト スクリプティング(XSS)攻撃やその他のインジェクションの脆弱性が生じます。

5. Django のベスト プラクティス:

  • ModelForms と関連モデル: フォームは、save メソッド内で関連モデル(AcademicRecordTestScore)を直接処理します。通常は、関連モデルに個別の ModelForm を使用するか、Django のシグナルを利用して、懸念事項を明確に分離することをおすすめします。
  • エラー処理: save メソッドにエラー処理がありません。いずれかの save 呼び出しが失敗した場合は、データの整合性を維持するためにトランザクションをロールバックする必要があります。より堅牢なエラー処理が必要です。
  • フォーム フィールドの選択: TestScoretest_type フィールドは、「SAT」と「ACT」としてハードコードされています。モデルの保守性と拡張性を高めるには、モデルで選択肢を定義することをおすすめします。

コードの改善:

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


この改良版では多くの問題が解決されていますが、さらに次のような改善が加えられています。

  • ファイル形式とサイズの検証: transcript フィールドのファイル形式とサイズを制限する検証を追加します。
  • ウイルス対策/マルウェア スキャン: アップロードされたファイルに悪意のあるコンテンツがないかスキャンするメカニズムを統合します。
  • カスタム検証関数: 検証ロジックを個別の関数にリファクタリングして、可読性と保守性を向上させます。
  • 個別の ModelForm: 整然性を高めるために、AcademicRecordTestScore に個別の ModelForm を使用することを検討します。
  • シグナル: Django シグナルを使用して、関連モデルの作成処理に、より分離したアプローチを行います。
  • 適切なロギング: 適切なロギングを実装して、エラーを追跡し、問題をデバッグします。

セキュリティ上の脆弱性を防ぐため、ユーザー入力は常に徹底的にサニタイズしてください。この改訂されたコードは、より堅牢で安全な基盤を提供しますが、さらなるセキュリティ対策を講じることをおすすめします。

モデル: gemini-1.5-flash-002
温度: 0.2
最大出力トークン: 8192
TopK: 40.0
TopP: 0.95