【问题标题】:Validate a condition that relies on foreign keys in Django admin在 Django admin 中验证依赖于外键的条件
【发布时间】:2017-05-18 04:38:37
【问题描述】:

我有两个模型,BookPage,其中PageBook 有外键关系。在 admin.py 中,修改书籍的网页包含 Page 对象的内联元素。

models.py:

class Book(models.Model):
    # ...

class Page(models.Model):
    book = models.ForeignKey(Book, editable=False)
    number = models.IntegerField('Page number')
    # ...

admin.py:

class PageAdminInline(admin.TabularInline):
    model = Page
    extra = 1

class BookAdmin(admin.ModelAdmin):
    inlines = [PageAdminInline]

admin.site.register(Book, BookAdmin)

在内联表单中,字段允许设置每页的页码。使用此配置,我如何验证一本书的所有页码应该不同并且编号从一到与该书关联的Page 对象数的全局条件?

我想我必须在某处覆盖与书籍模型或表单相关的clean() 方法,但我不知道如何从那里访问与用户输入的页面相关的数据。

编辑

根据 Emett 的建议,我尝试重写 Page 模型的 clean() 方法:

class Page(models.Model):
    book = models.ForeignKey(Book, editable=False)
    number = models.IntegerField('Page number')

    def clean():
        book_pages = Page.objects.filter(book=self.book)
        # ... [apply condition on the book_pages QuerySet]
        super(Page, self).clean()

这不起作用:如果我修改一本书的管理站点中所有页面的页码,book_pages 将包含具有旧页码的对象。

另外,在Page中检查条件也意味着如果我有n个页面,它将被测试n次,而只检查一次就足够了。

【问题讨论】:

  • 这需要在您的models.py 中完成。能否提供模型?
  • @EmettSpeer 你确定吗?在models.py 中,我需要访问Page 对象的Book 方法实例,这些对象在验证时未保存到数据库中。
  • 当您在数据库中保存新页面时,您需要该页面附有书籍。这将是让系统执行此逻辑的正确时机。 unique-together 似乎是解决这个问题的正确选择。 docs.djangoproject.com/en/1.10/ref/models/options/…
  • 谢谢,我会看看是否可以通过更改 Page 模型使其工作。 unique-together 看起来是一个非常好的建议。然而,书页问题只是一个例子,我要验证的条件比只有不同的页码更复杂!

标签: django django-admin django-validation


【解决方案1】:

一个更简单的解决方案是将 unique_together 放入 django 模型中。浏览器

class Page(models.Model):
    book = models.ForeignKey(Book, editable=False)
    number = models.IntegerField('Page number')

    class Meta:
        app_label = 'page'
        db_table = 'pages'
        verbose_name = 'Page'
        verbose_name_plural = 'Pages'
        unique_together = (('book', 'number'),)

如果您不想使用unique_together,则另一种解决方法,然后创建一个表单,内联使用它,即:

class PageForm(forms.ModelForm):
   class Meta:
     model = Page
     fields ='__all__'

   def clean():
       cleaned_data = self.cleaned_data
       book = cleaned_data.get('book')
       number = cleaned_data.get('number')
       page_qset = Page.objects.filter(book=book, number=number)

       if len(page_qset) > 0:  # inefficient solution, using it for forcibly executing query
             raise forms.ValidationError('Already exists')
       return super().clean()



class PageAdminInline(admin.TabularInline):
    form = PageForm

【讨论】:

  • 独特检查内置于模型表单中。所以你不需要那种定制的清洁方法
  • 如果模型没有unique_together,这是一种解决方法:)
  • 啊,再次阅读您的答案,我明白您的意思了。您的第一个建议是一起使用独特的,这是一个很好的建议。约束应始终在数据库级别实现。原始问题和您的表格中的方法实际上是一个非常糟糕的主意。它开始重新发明轮子。除了我们拥有的车轮是带有低调轮胎的合金车轮。而且它对解决竞争条件没有任何作用
  • @e4c5 我同意。 unique_together 是最好的解决方案
  • @ruddra 我刚刚尝试了您的解决方案。我遇到了与我在问题的编辑部分中尝试过的问题相同的问题。在您的Page.objects.filter(book=book, number=number).exists() 行,QuerySet 包含具有旧页码的 Page 对象,而不是我在表单中输入的新页码。
猜你喜欢
  • 2012-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
  • 1970-01-01
  • 2013-12-26
  • 2015-04-02
  • 2014-06-19
相关资源
最近更新 更多