Allow only one model instance in Django

I would like to control some configuration options for my project using the database model. For instance:

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

There should be only one instance of this model:

juicer_base = JuicerBaseSettings()
juicer_base.save()

Of course, if someone accidentally creates new instances, this is not the end of the world. I could just do it JuicerBaseSettings.objects.all().first(). However, is there a way to lock it in such a way that it is impossible to create more than one instance?

I found two related questions about SO. This answer suggests using third-party applications, such as django-singletonsone that doesn't seem to be supported (last update for the git repository 5 years ago). Another answer suggests using a combination of permissions or OneToOneField. Both answers relate to 2010-2011.

Given that Django has changed a lot since then, are there standard ways to solve this problem? Or should I just use .first()and acknowledge that there may be duplicates?

+7
source share
6 answers

You can override the method saveto control the number of instances:

class JuicerBaseSettings(models.Model):

    def save(self, *args, **kwargs):
        if not self.pk and JuicerBaseSettings.objects.exists():
        # 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)
+10

, , save() , , , save() , ().save()

+3

pre_save

@receiver(pre_save, sender=JuicerBaseSettings)
def check_no_conflicting_juicer(sender, instance, *args, **kwargs):
    # If another JuicerBaseSettings object exists a ValidationError will be raised
    if JuicerBaseSettings.objects.exclude(pk=instance.pk).exists():
        raise ValidationError('A JuiceBaseSettings object already exists')
+3

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
+2

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()
+2

I'm a little late to the party, but if you want to make sure that only one instance of the object is created, the alternative solution to change the save () function will always indicate identifier 1 when creating the instance - this way, if the instance already exists, the integrity error will be fixed . eg.

JuicerBaseSettings.objects.create(id=1)

instead:

JuicerBaseSettings.objects.create()

This is not as clean a solution as changing the save function, but still does the trick.

+1
source

Source: https://habr.com/ru/post/1654137/


All Articles