【问题标题】:Pass object to modelForm before CreateView form_valid method在 CreateView form_valid 方法之前将对象传递给 modelForm
【发布时间】:2015-07-22 03:42:54
【问题描述】:

我有一个经过良好验证的模型,我在 模型 中使用clean 方法。问题是当我验证时,我正在使用一个尚未以表单设置的对象,这会引发该对象尚不存在的异常。

我想要一个解决方案,在任何验证之前将对象从 url 主键传递到表单,所以我的 clean 方法可以正常工作。

这是一个类似的例子。

主要型号

class Student(models.Model):
    first_name = models.CharField(max_length=30)

让我们坐下来,每个学生一次可能有一个学期。但是,如果之前有任何学期,则开始日期必须在最后一个学期结束日期之后。

class Semester(models.Model):
    student = models.OneToOneField(Student)
    start_date = models.DateField()

    def clean(self):
        # do not allow the start date to be before last semester end date
        if self.student.semesterhistory_set.all().count() > 0:
            last_semester_end_date = self.student.semesterhistory_set.last().end_date
            if last_semester_end_date >= self.start_date:
                message = _("Start Date for this semester must be after %s" % last_date)
                raise ValidationError(message)

class SemesterHistory(models.Model):
    student = models.ForeignKey(Student)
    start_date = models.DateField()
    end_date = models.DateField()

在视图中,我正在传递将在验证表单后用于验证的学生对象。 (问题

# URL for this is like this student/(pk)/semesters/create/
class SemesterCreate(CreateView):
    model = Semester
    fields = ['start_date']

    def form_valid(self, form):
        form.instance.student = get_object_or_404(Student, id=int(self.kwargs['pk']))
        return super(SemesterCreate, self).form_valid(form)

错误:

RelatedObjectDoesNotExist Semester has no student

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    显然你需要调用form.save(commit=False),它返回instance ...在form_valid中提出404的语义错误方法...

    class SemesterCreate(CreateView):
        model = Semester
        fields = ['start_date']
    
        student = object = None
    
        def dispatch(self, request, *args, **kwargs):
            self.student = get_object_or_404(Student, id=kwargs['pk'])
    
            return super(SemesterCreate, self).dispatch(request, *args, **kwargs)
    
        def form_valid(self, form):
            self.object = form.save(commit=False)
            self.object.student = self.student
            self.object.save()
    
            return HttpResponseRedirect(self.get_success_url())
    
        def get_success_url(self):
            return reverse('...')
    

    https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#the-save-method

    【讨论】:

    • 仍然,表单实例在验证之前没有学生对象。如果您在 form_valid 中,那么您通过了model.clean(),如果没有设置学生对象,这是不可能发生的。不过,感谢您的 404 提示
    • 嗯,我已经彻底阅读了您的问题...抱歉快速猜测:D 在您的情况下,我建议使用事务...将您的代码从 clean 移动到 save 方法并使用rollback 如果某些条件(你也可以提出 ValidationError :D)docs.djangoproject.com/en/1.8/topics/db/transactions/… ... PS 我明天会编辑答案,或者你会更快地编辑它:D
    【解决方案2】:

    实际上,要拥有一个干净的集合,我会添加一个自定义 ModelForm。我也使用 CreateView,这就是我使用它的方式。

    首先添加一个自定义 ModelForm(我个人在我的应用中添加了一个 forms.py 文件):

    from django.contrib.auth.forms import UserCreationForm
    from .model import Semester
    
    class CreateSemesterForm(UserCreationForm):
        error_messages = {
            'last_date': _("some message"),
        }
    
        class Meta:
            model = Semester
            fields = ('some', 'fields') #or __all__
    
        def clean_your_date_field(self):
            #clean_name_field will be called when the form_valid will be called with all clean_fields functions
            #here you define your clean method and raise Validation error
            return field
    
        def save(self, commit=True):
            semester = super(CreateSemesterForm, self).save(commit=False)
            #here you can set some other values
            if commit:
                semester.save()
            return semester
    

    在您的自定义 CreateView 中,您必须添加:

    class SemesterCreate(CreateView):
        form_class = CreateArtistForm
    

    当您在 ModelForm 中设置模型和字段时,您可以从 CreateView 中删除字段和模型参数。

    您还可以在自定义 ModelForm 中覆盖 form_valid。

    现在 CreateView 会调用 form_valid ,它会调用所有的 clean 函数,如果都通过了,它会返回并保存你的学期。

    【讨论】:

    • 不过,表单不会包含student 字段,这是清理start_date 字段所必需的。此外,为模型字段提供验证将反映到 ModelForm。
    • 如果需要,您可以在表单中包含学生字段,或者喜欢名称(不知道结构)并通过执行 Student.objects.get(id=id) 将其加载到保存功能中,其中 id 取自表格cleaned_data
    【解决方案3】:

    在我的项目遇到完全相同的问题后,我昨天遇到了这个问题。

    自从您发布此内容以来已经有几年了,但我想我会发布我的解决方案以帮助其他可能偶然发现此问题的人。

    我遇到的解决方案是使用自定义模型:

     from django import forms
     from .models import Blade
    
     class SemesterForm(forms.ModelForm):
        class Meta:
            model = Semester
            fields = '__all__'
            widgets = {'student': forms.HiddenInput()}
    

    在你看来:

    class SemesterCreate(CreateView):
        model = Semester
    
        def get_initial(self, **kwargs):
            # get current student from pk in url
            current_student = get_object_or_404(Student, 
                pk=self.kwargs.get('pk'))
            return { 'site': current_student }
    

    这里的诀窍是您必须在表单中将学生字段设置为隐藏。这样,它将保留您在视图中提供的初始化值,但对用户不可用。

    因此,当提交表单并调用 full_clean() 方法(然后将调用模型上的 clean() 方法)时,它将在那里,并且您在模型 clean() 中执行漂亮整洁的验证将工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2011-09-27
      相关资源
      最近更新 更多