【问题标题】:Return error message after saving a model with custom save() method on Django Admin在 Django Admin 上使用自定义 save() 方法保存模型后返回错误消息
【发布时间】:2014-05-21 15:50:07
【问题描述】:

我有两个模型。一个是Subscriber,为了节省时间,我会根​​据已分配给该池的订阅者数量来分配一个池:

class Subscriber(models.Model):
    pool = models.ForeignKey(Pool)
    interface = models.CharField(max_length=30)


class Pool(models.Model):
    name = models.CharField(max_length=50)

假设只有 4 个Subscribers 可以分配给一个池。这就是为什么我要覆盖订阅者的 save() 方法:

def save(self, *args, **kwargs):
    if not self.pk:
        #Look for a free Pool among the available ones
        for pool in Pool.objects.all():
            if pool.subscriber_set.count() < 4:
                self.pool = pool
                print "Assigned pool: %s" % pool.name
                super(Subscriber, self).save(*args, **kwargs)

它运行良好,直到我用完池(所有池都分配了 4 个用户)。我应该如何从 Django 管理员处处理这个问题?理想情况下,我想向用户显示一些错误消息,以便他可以创建更多池。

我宁愿不将池分配代码移动到表单的 clean() 方法,因为可能还会从不同的界面创建用户,而不是管理 GUI。

有什么想法吗?

非常感谢!

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

    我建议同时验证 clean()save()。前者将在管理员中为您提供一个很好的错误消息和工作流程,而后者将确保 save() 本身给出错误消息,无论您如何创建实例。

    首先验证:

    class Subscriber(models.Model):
    
        def clean(self)
            if not self.pk:
                if not Pool.objects.annotate(num_subscribers=Count('subscriber'))
                                   .filter(num_subscribers__lt=4)
                                   .exists():
                    raise ValidationError('The pools are all full.')
    

    这将由管理员自动调用(请参阅ModelForm validation 上的文档)。或者 - 如果您不清理 ModelForm 验证之外的内容 - 您可以在表单的 clean() 方法中提供此逻辑。

    然后在save() 方法中做同样的事情。

        def save(self, *args, **kwargs):
            if not self.pk:
                try:
                    self.pool = Pool.objects.annotate(num_subscribers=Count('subscriber'))
                                            .filter(num_subscribers__lt=4)[0]
                    super(Subscriber, self).save(*args, **kwargs)
                except IndexError:
                    raise ValidationError(..)
    

    您可以改为在 save() 中调用 self.full_clean() 来进行验证,但这个版本似乎更简单(而且肯定更有效)。

    【讨论】:

    • 谢谢!那效果很好。我只需要将 self.full_clean() 放在池分配之后:根据文档,Model.clean_fields 被称为 full_clean 的一部分,并且由于需要池字段,因此必须先分配值。非常感谢!
    • @IgnacioVerona:好点。我刚刚编辑了答案以反映这一点,并更有效地分配池(特别是不循环)。如您所见,有几种实现此目的的好方法...
    • 为什么效率更高?通过 ModelForm 保存时不会调用 clean 吗?那么同样的检查会进行两次吗?
    • @GonzaloA:在此示例中,OP 不仅要验证,还要设置一个值 (self.pool)。因此,如果您调用full_clean() 进行验证,您仍然需要再次访问数据库才能实际设置值。我上面建议的方法将在一个数据库调用中同时进行设置和验证。
    猜你喜欢
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    • 2019-10-18
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 2015-09-17
    相关资源
    最近更新 更多