Limit number of model instances to be created - dj

2019-01-11 13:15发布

I have model from which I only want to create one instance, and no more instances should be allowed.

Is this possible? I've got a feeling that I've seen this done somewhere, but unfortunately I'm unable to locate it.

EDIT: I need this for a stupidly simple CMS. I have an abstract class for which FrontPage and Page classes inherits. I only want to be able to create one frontpage object.

The difference between the FrontPage object and the Page objects are that they're supposed to have slightly different fields and templates, and as mentioned only one FrontPage is to be created.

5条回答
太酷不给撩
2楼-- · 2019-01-11 13:27

I would override create() method on default manager, but as stated above, this won't guarantee anything in multi-threaded environment.

查看更多
疯言疯语
3楼-- · 2019-01-11 13:31

You can do something like this, 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)
查看更多
smile是对你的礼貌
4楼-- · 2019-01-11 13:35

If you just want to prevent users using the administrative interface from creating extra model objects you could modify the "has_add_permission" method of the model's ModelAdmin class:

# 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)

This will remove the "add" button in the administrative interface preventing users from even attempting to create more than the specified number (in this case 1). Of course programatic additions will still be possible.

查看更多
Rolldiameter
5楼-- · 2019-01-11 13:38

I wanted to do something similar myself, and found that Django's model validation provided a convenient hook for enforcement:

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)

That not only prevents the creation of new instances, but the Django admin UI will actually report that the creation failed and the reason was "Can only create 1 Example instance"(whereas the early return approach in the docs gives no indication as to why the save didn't work).

查看更多
家丑人穷心不美
6楼-- · 2019-01-11 13:42

@ncoghlan your solution is working fine, but not very user-friendly: the user has access to the creation form and will think he/she can use it, even though he/she will never be able to save it.

It's actually possible to combine it with Brendan's solution, which will hide the 'Add' button. Using Mixins for easy reuse:

# 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
查看更多
登录 后发表回答