将对象传递给modelForm之前CreateView form_valid方法

问题描述:

我有一个很好的验证模型,我在模型内使用clean方法。问题是当我验证我正在使用的对象没有被设置在引发异常的对象不存在的形式中。将对象传递给modelForm之前CreateView form_valid方法

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

下面是一个类似的例子。

主要模式

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

显然,你需要调用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

+0

但是,表单实例在验证之前没有学生对象。如果你在form_valid中,那么你传递了'model.clean()',这是在没有学生对象集的情况下不可能发生的。尽管如此,感谢您提供404 hint – Othman

+0

hm,我已经彻底地读过您的问题了......对于快速猜测:D在您的情况下,我建议使用事务处理...将代码从'clean'移动到'save'方法,然后如果某些条件使用'rollback'(也可以引发ValidationError:D)https://docs.djangoproject.com/en/1.8/topics/db/transactions/#django.db.transaction.savepoint_rollback ... PS我会明天编辑答案,或者您可能会更快地编辑答案:D – madzohan

其实有一个干净的集合,我会添加一个自定义的ModelForm。我也使用CreateView,这是我如何使用它。

首先添加自定义的ModelForm(我personnaly加在我的应用程序一个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中删除字段和模型参数。

您也可以在您的Custom ModelForm中覆盖form_valid。

现在CreateView将调用form_valid函数调用所有清理函数,如果全部通过,它将返回并保存学期。

+0

但是,表单不会包含'student'字段,该字段对于清理'start_date'字段是强制性的。此外,为模型字段提供验证将反映到ModelForm中。 – Othman

+0

如果你想,或者像名字(不知道结构),你可以让学生字段在表单中,并通过执行Student.objects.get(id = id)将其加载到保存函数中,其中id是从表单中清除数据 – Bestasttung

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

你已经发布了这个版本已经有几年了,但是我会发布我的解决方案来帮助其他任何可能遇到此问题的人解决问题。

我碰到的解决方案是使用自定义的ModelForm:

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()方法)时,它会在那里,并且您在模型中执行的漂亮整齐验证清理) 将工作。