【问题标题】:Limit number of model instances to be created - django限制要创建的模型实例的数量 - django
【发布时间】:2011-01-09 10:51:29
【问题描述】:

我有一个模型,我只想从中创建一个实例,并且不应允许更多实例。

这可能吗?我有一种感觉,我在某个地方看到过这种情况,但不幸的是我找不到它。

编辑: 我需要这个用于愚蠢的简单 CMS。我有一个 FrontPage 和 Page 类继承的抽象类。我只希望能够创建一个首页对象。

FrontPage 对象和 Page 对象之间的区别在于它们应该具有稍​​微不同的字段和模板,并且如上所述只创建一个 FrontPage。

【问题讨论】:

  • 如果有人试图创建第二个实例会发生什么?
  • 好问题。也许最简单的方法是在尝试保存第二个实例时提供某种错误消息。
  • 你想做什么?限制模型的实例化听起来很脆弱。
  • 我正在尝试一个原始的 cms 解决方案。我有一个抽象页面类和继承它的 FrontPage 类。然后我只想允许一个首页对象。
  • 创建一个唯一的 Frontpage 对象可能是不可取的。你能把这个概念贴在 frontpage 对象后面吗?

标签: django django-models django-admin


【解决方案1】:

你可以这样做,from the Django docs:

class ModelWithOnlyOneInstance(models.Model):
    ... fields ...

    def save(self, *args, **kwargs):
      if ModelWithOnlyOneInstance.objects.count() > 1:
        return

      super(ModelWithOnlyOneInstance, self).save(*args, **kwargs)

【讨论】:

  • 这并不能保证最多创建一个实例——除非只有一个线程/进程。
  • 确实如此——如果你真的那么担心的话,我认为你需要对post_save 信号做一些事情,然后开始使用线程同步。
【解决方案2】:

我会覆盖默认管理器上的 create() 方法,但如上所述,这不能保证多线程环境中的任何内容。

【讨论】:

    【解决方案3】:

    我自己也想做类似的事情,发现 Django 的模型验证提供了一个方便的执行钩子:

    from django.db import models
    from django.core.exceptions import ValidationError
    
    def validate_only_one_instance(obj):
        model = obj.__class__
        if (model.objects.count() > 0 and
                obj.id != model.objects.get().id):
            raise ValidationError("Can only create 1 %s instance" % model.__name__)
    
    class Example(models.Model):
    
        def clean(self):
            validate_only_one_instance(self)
    

    这不仅阻止了新实例的创建,而且 Django 管理 UI 实际上会报告创建失败,原因是“只能创建 1 个示例实例”(而提前返回的方法在文档中没有说明为什么保存不起作用)。

    【讨论】:

    • 此建议的初始版本存在一个缺陷,该缺陷还阻止在创建允许的实例后编辑属性。当前版本没有这个问题。
    • @jeverling 在提议的编辑中指出,通过适当调整条件和错误消息,可以将其扩展为处理任意数量的实例。不过,最好将其作为单独的补充答案发布。
    • 最好使用pk 而不是id: obj.pk != model.objects.get().pk
    【解决方案4】:

    如果您只想阻止使用管理界面的用户创建额外的模型对象,您可以修改模型的 ModelAdmin 类的“has_add_permission”方法:

    # admin.py
    from django.contrib import admin
    from example.models import Example
    
    class ExampleAdmin(admin.ModelAdmin):
      def has_add_permission(self, request):
        num_objects = self.model.objects.count()
        if num_objects >= 1:
          return False
        else:
          return True
    
    admin.site.register(Example, ExampleAdmin)
    

    这将删除管理界面中的“添加”按钮,甚至阻止用户尝试创建超过指定数量(在本例中为 1)。当然,程序化添加仍然是可能的。

    【讨论】:

      【解决方案5】:

      @ncoghlan 您的解决方案运行良好,但对用户不太友好:用户可以访问创建表单并认为他/她可以使用它,即使他/她永远无法保存它。

      实际上可以将它与 Brendan 的解决方案结合使用,该解决方案将隐藏“添加”按钮。使用 Mixins 方便重用:

      # models.py
      from django.db import models
      from django.core.exceptions import ValidationError
      
      class SingleInstanceMixin(object):
          """Makes sure that no more than one instance of a given model is created."""
      
          def clean(self):
              model = self.__class__
              if (model.objects.count() > 0 and self.id != model.objects.get().id):
                  raise ValidationError("Can only create 1 %s instance" % model.__name__)
              super(SingleInstanceMixin, self).clean()
      
      class Example(SingleInstanceMixin, models.Model):
          pass
      
      
      # admin.py
      from django.contrib import admin
      from example.models import Example
      
      class SingleInstanceAdminMixin(object):
          """Hides the "Add" button when there is already an instance."""
          def has_add_permission(self, request):
              num_objects = self.model.objects.count()
              if num_objects >= 1:
                  return False
              return super(SingleInstanceAdminMixin, self).has_add_permission(request)
      
      class ExampleAdmin(SingleInstanceAdminMixin, admin.ModelAdmin):
           model = Example
      

      【讨论】:

        【解决方案6】:

        如果您只想要一个模型实例,也许“有一个应用程序可以做到这一点!”

        您可以查看 django-solo 女巫是否是您需要的完整解决方案。

        这是一个链接 -> https://github.com/lazybird/django-solo

        它带有一个用于单例模型的类和一个用于管理单例模型的类,女巫不会在模型名称的末尾添加 s,排除中间屏幕女巫列出所有对象并删除“添加”按钮和“保存并添加另一个”按钮。

        它还附带了一些其他蓬松而有用的东西,比如直接在你的模板上导入单例等等......

        【讨论】:

          猜你喜欢
          • 2018-02-07
          • 2013-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-09
          • 2018-07-15
          • 2021-06-19
          • 2013-03-25
          相关资源
          最近更新 更多