【问题标题】:Django 1.3 CreateView/ModelForm: unique_together validation with one field excluded from formDjango 1.3 CreateView/ModelForm:unique_together 验证,其中一个字段从表单中排除
【发布时间】:2011-10-17 01:20:34
【问题描述】:

我正在寻找这个常见问题的简单示例。到目前为止,我找到的答案对于我们这些初学者来说都遗漏了关键点。

我有一个应用程序,其中几乎每个模型都有一个外键到用户,并且有一个 unique_together 约束,其中一个字段始终是“用户”。

例如:

class SubscriberList(models.Model):

user = models.ForeignKey(User)
name = models.CharField(max_length=70)
date_created = models.DateTimeField(auto_now_add=True)

class Meta:
    unique_together = (
        ('user', 'name',),
        )   

def __unicode__(self):
    return self.name

订阅者列表始终由登录用户创建,因此在创建订阅者列表的表单中,我排除了用户字段并在保存表单时为其赋予 self.request.user 值,如下所示:

class SubscriberListCreateView(AuthCreateView):
model = SubscriberList
template_name = "forms/app.html"
form_class = SubscriberListForm
success_url = "/app/lists/"

def form_valid(self, form):
    self.object = form.save(commit=False)
    self.object.user = self.request.user
    return super(SubscriberListCreateView, self).form_valid(form) 

这是随附的表格:

class SubscriberListForm(ModelForm):
class Meta:
    model = SubscriberList
    exclude = ('user')

使用此代码,有效数据就可以了。当我提交 not unique_together 的数据时,我从数据库中收到一个完整性错误。原因对我来说很清楚 - Django 不验证 unique_together 因为“用户”字段被排除在外。

如何更改我现有的代码,仍然使用 CreateView,以便提交的不是 unique_together 的数据会引发表单验证错误,而不是来自 db 的完整性错误。

【问题讨论】:

    标签: django-views django-forms django-1.3


    【解决方案1】:

    Yehonatan 的示例将我带到了那里,但我必须从 form_valid 的 ValidationError 中调用消息,而不是单独的 form_invalid 函数。

    这行得通:

    class SubscriberCreateView(AuthCreateView):
        model = Subscriber
        template_name = "forms/app.html"
        form_class = SubscriberForm
        success_url = "/app/subscribers/"
    
        def form_valid(self, form):
            self.object = form.save(commit=False)
            self.object.user = self.request.user
    
            try:
                self.object.full_clean()
            except ValidationError:
                #raise ValidationError("No can do, you have used this name before!")
                #return self.form_invalid(form)
                from django.forms.util import ErrorList
                form._errors["email"] = ErrorList([u"You already have an email with that name man."])
                return super(SubscriberCreateView, self).form_invalid(form)
    
            return super(SubscriberCreateView, self).form_valid(form) 
    

    【讨论】:

    • AuthCreateView 的引用是什么?试图理解为什么在 veiw 上调用 super。我以前没见过。干杯
    【解决方案2】:

    取自以下文档: https://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs#validating-objects

    如果您打算自己处理验证错误,或者如果您从 ModelForm 中排除了需要验证的字段,则只需要调用模型的 full_clean() 方法。

    取自以下文档: https://docs.djangoproject.com/en/dev/ref/class-based-views/#formmixin

    混合 FormMixin 的视图必须提供 form_valid() 和 form_invalid() 的实现。

    这意味着为了查看错误(与表单无关),您需要实现自己的 form_invalid,在此处添加特殊错误消息,然后返回。

    因此,在您的对象上运行 full_clean() 应该会引发 unique_together 错误,因此您的代码可能如下所示:

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        # validate unique_together constraint
        try:
            self.object.full_clean()
        except ValidationError:
            # here you can return the same view with error messages
            # e.g.
            return self.form_invalid(form)
        return super(SubscriberListCreateView, self).form_valid(form)
    
    def form_invalid(self, form):
        # using messages
        # from django.contrib import messages
        # messages.error('You already have a list with that name')
        # or adding a custom error
        from django.forms.util import ErrorList
        form._errors["name"] = ErrorList([u"You already have a list with that name"])
        return super(SubscriberListCreateView, self).form_invalid(form)
    

    HTH

    【讨论】:

      【解决方案3】:

      添加另一个对新手来说可能更容易的示例。

      forms.py

      class GroupItemForm(ModelForm):
          def form_valid(self):
              self.object = self.save(commit=False)
              try:
                  self.object.full_clean()
              except ValidationError:
                  # here you can return the same view with error messages
                  # e.g. field level error or...
                  self._errors["sku"] = self.error_class([u"You already have an email with that name."])
                  # ... form level error
                  self.errors['__all__'] = self.error_class(["error msg"]
                  return False
              return True
      

      views.py

      def add_stock_item_detail(request, item_id, form_class=GroupItemForm, template_name="myapp/mytemplate.html"):
          item = get_object_or_404(Item, pk=item_id)
          product = Product(item=item)
          if request.method == 'POST':
              form = form_class(request.POST, instance=product)
              if form.is_valid() and form.form_valid():
                  form.save()
                  return HttpResponseRedirect('someurl')
          else:
              form = form_class(instance=product)
      
          ctx.update({
              "form" : form,
           })
      
          return render_to_response(template_name, RequestContext(request, ctx))
      

      【讨论】:

        猜你喜欢
        • 2010-12-11
        • 2013-10-21
        • 2014-06-14
        • 2023-03-15
        • 1970-01-01
        • 2013-07-18
        • 2011-10-16
        • 2015-06-30
        • 1970-01-01
        相关资源
        最近更新 更多