Ограничить количество создаваемых экземпляров модели - django - PullRequest
23 голосов
/ 26 января 2010

У меня есть модель, из которой я хочу создать только один экземпляр, и больше экземпляров не должно быть разрешено.

Возможно ли это? У меня такое ощущение, что я где-то видел, что это сделано, но, к сожалению, я не могу его найти.

EDIT: Мне это нужно для тупо простой CMS. У меня есть абстрактный класс, для которого наследуются классы FrontPage и Page. Я хочу иметь возможность создавать только один объект Frontpage.

Разница между объектом FrontPage и объектами Page заключается в том, что они должны иметь немного разные поля и шаблоны, и, как уже упоминалось, должен быть создан только один FrontPage.

Ответы [ 5 ]

30 голосов
/ 22 июня 2011

Я хотел сделать что-то подобное сам и обнаружил, что проверка модели 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 фактически сообщит, что создание не удалось , и причиной было «Может создать только 1 пример» (тогда как ранний возврат в документах не указано, почему сохранение не сработало).

23 голосов
/ 18 сентября 2012

Если вы просто хотите запретить пользователям, использующим административный интерфейс, создавать дополнительные объекты модели, вы можете изменить метод has_add_permission класса ModelAdmin модели:

# 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). Конечно, программные дополнения все еще будут возможны.

3 голосов
/ 26 января 2010

Вы можете сделать что-то вроде этого, из документации Django :

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

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

      super(ModelWithOnlyOneInstance, self).save(*args, **kwargs)
2 голосов
/ 09 октября 2014

@ ncoghlan ваше решение работает нормально, но не очень удобно: пользователь имеет доступ к форме создания и будет думать, что он / она может ее использовать, даже если он / она никогда не сможет ее сохранить.

На самом деле это можно объединить с решением Брендана, которое скроет кнопку «Добавить». Использование 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
0 голосов
/ 26 января 2010

Я бы переопределил метод create () в менеджере по умолчанию, но, как указано выше, это не гарантирует ничего в многопоточной среде.

...