[Django]-Allow only one instance of a model in Django

34πŸ‘

βœ…

You can override save method to control a 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 be raised in the update of exists model
            raise ValidationError('There is can be only one JuicerBaseSettings instance')
        return super(JuicerBaseSettings, self).save(*args, **kwargs)

12πŸ‘

Either you can override save and create a class function JuicerBaseSettings.object()

class JuicerBaseSettings(models.Model):

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

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

============= OR =============

Simply, Use django_solo.

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

Snippet Courtsy: django-solo-documentation.

# 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()
πŸ‘€jerinisready

8πŸ‘

If your model is used in django-admin only, you additionally can set dynamic add permission for your model:

# 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
πŸ‘€Max M

3πŸ‘

i am not an expert but i guess you can overwrite the model’s save() method so that it will check if there has already been a instance , if so the save() method will just return , otherwise it will call the super().save()

πŸ‘€beast

3πŸ‘

You could use a pre_save signal

@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')
πŸ‘€Jack Evans

1πŸ‘

I’m a bit late to the party but if you want to ensure that only one instance of an object is created, an alternative solution to modifying a models save() function would be to always specify an ID of 1 when creating an instance – that way, if an instance already exists, an integrity error will be raised.
e.g.

JuicerBaseSettings.objects.create(id=1)

instead of:

JuicerBaseSettings.objects.create()

It’s not as clean of a solution as modifying the save function but it still does the trick.

1πŸ‘

I did something like this in my admin so that I won’t ever go to original add_new view at all unless there’s no object already present:

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)

Works only when saving from admin

πŸ‘€yashas123

Leave a comment