在Django中只允许一个模型实例

时间:2016-09-09 13:42:43

标签: python django

我想使用数据库模型控制项目的一些配置设置。例如:

Apple provisioning profiles

此模型应该只有一个实例:

class JuicerBaseSettings(models.Model):
    max_rpm = model.IntegerField(default=10)
    min_rpm = model.IntegerField(default=0)

当然,如果有人意外地创建了一个新实例,那么它并不是世界末日。我可以做juicer_base = JuicerBaseSettings() juicer_base.save() 。但是,有没有办法将其锁定,以至于无法创建超过1个实例?

我在SO上发现了两个相关的问题。 This answer建议使用JuicerBaseSettings.objects.all().first()之类的第三方应用,但似乎并没有积极维护(git repo的最新更新是5年前)。 Another answer建议使用权限或django-singletons的组合。这两个答案都是2010-2011。

鉴于Django从那时起经历了很多变化,有没有任何标准方法可以解决这个问题?或者我应该使用OneToOneField并接受可能有重复的内容?

7 个答案:

答案 0 :(得分:9)

您可以覆盖save方法来控制实例数:

class JuicerBaseSettings(models.Model):

    def save(self, *args, **kwargs):
        if JuicerBaseSettings.objects.exists() and not self.pk:
        # if you'll not check for self.pk 
        # then error will also raised in update of exists model
            raise ValidationError('There is can be only one JuicerBaseSettings instance')
        return super(JuicerBaseSettings, self).save(*args, **kwargs)

答案 1 :(得分:3)

您可以使用pre_save信号

new SomeClass().foo()

答案 2 :(得分:2)

我不是专家,但我想你可以覆盖模型的save()方法,以便检查是否已经有一个实例,如果是这样,save()方法将只返回,否则它会调用super ().save()

答案 3 :(得分:2)

如果您的模型仅用于django-admin,您还可以为您的模型设置动态添加权限

# some imports here
from django.contrib import admin
from myapp import models

@admin.register(models.ExampleModel)
class ExampleModelAdmin(admin.ModelAdmin):

    # some code...

    def has_add_permission(self, request):
        # check if generally has add permission
        retVal = super().has_add_permission(request)
        # set add permission to False, if object already exists
        if retVal and models.ExampleModel.objects.exists():
            retVal = False
        return retVal

答案 4 :(得分:0)

我有点迟到了,但如果你想确保只创建一个对象实例,修改模型save()函数的另一种解决方案是始终指定ID为1创建实例 - 这样,如果实例已存在,则会引发完整性错误。 e.g。

JuicerBaseSettings.objects.create(id=1)

而不是:

JuicerBaseSettings.objects.create()

它不像修改保存功能那样干净,但它仍然可以解决问题。

答案 5 :(得分:0)

您可以覆盖保存并创建类函数SiteConfiguration.object()

class SiteConfiguration(models.Model):

    @classmethod
    def object(cls):
        return cls._default_manager.all().first() # Since only one item

    def save(self, *args, **kwargs):
        self.id = 1
        return super().save(*args, **kwargs)

=============或=========

只需使用django_solo

https://github.com/lazybird/django-solo

# models.py

from django.db import models
from solo.models import SingletonModel

class SiteConfiguration(SingletonModel):
    site_name = models.CharField(max_length=255, default='Site Name')
    maintenance_mode = models.BooleanField(default=False)

    def __unicode__(self):
        return u"Site Configuration"

    class Meta:
        verbose_name = "Site Configuration"
# admin.py

from django.contrib import admin
from solo.admin import SingletonModelAdmin
from config.models import SiteConfiguration

admin.site.register(SiteConfiguration, SingletonModelAdmin)

# There is only one item in the table, you can get it this way:
from .models import SiteConfiguration
config = SiteConfiguration.objects.get()

# get_solo will create the item if it does not already exist
config = SiteConfiguration.get_solo()

答案 6 :(得分:0)

我在管理员中做了类似的事情,所以除非没有对象存在,否则我将永远不会进入原始的add_new视图:

def add_view(self, request, form_url='', extra_context=None):
    obj = MyModel.objects.all().first()
    if obj:
        return self.change_view(request, object_id=str(obj.id) if obj else None)
    else:
        return super(type(self), self).add_view(request, form_url, extra_context)

def changelist_view(self, request, extra_context=None):
    return self.add_view(request)

仅当从管理员保存时有效