2015-07-22 27 views
2

我有一個很好的驗證模型,我在模型內使用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

回答

0

顯然,你需要調用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

0

其實有一個乾淨的集合,我會添加一個自定義的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

0

在面對與我的項目完全相同的問題後,我昨天遇到了這個問題。

你已經發布了這個版本已經有幾年了,但是我會發布我的解決方案來幫助其他任何可能遇到此問題的人解決問題。

我碰到的解決方案是使用自定義的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()方法)時,它會在那裏,並且您在模型中執行的漂亮整齊驗證清理) 將工作。