开发者

Limit a single record in model for django app?

开发者 https://www.devze.com 2022-12-17 09:52 出处:网络
I want use a model to save the system setting for a django app, So I want to limit the model can only have one 开发者_JAVA技巧record, how to do the limit?Try this:

I want use a model to save the system setting for a django app, So I want to limit the model can only have one 开发者_JAVA技巧record, how to do the limit?


Try this:

class MyModel(models.Model):
    onefield = models.CharField('The field', max_length=100)

class MyModelAdmin(admin.ModelAdmin):
  def has_add_permission(self, request):
    # if there's already an entry, do not allow adding
    count = MyModel.objects.all().count()
    if count == 0:
      return True

    return False


An easy way is to use the setting's name as the primary key in the settings table. There can't be more than one record with the same primary key, so that will allow both Django and the database to guarantee integrity.


William is right, but I guess this is the best practice

def has_add_permission(self, *args, **kwargs):
    return not MyModel.objects.exists()

As reported in the official Django Documentation:

Note: Don’t use this if all you want to do is determine if at least one result exists. It’s more efficient to use exists().

https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated


Overwriting has_add_permission works, but in the given examples it breaks the permissions system in Django(staff without necessary permissions can add settings). Here's a one that doesn't break it:

class SettingAdmin(admin.ModelAdmin):
    def has_add_permission(self, request):
        base_add_permission = super(SettingAdmin, self).has_add_permission(request)
        if base_add_permission:
            # if there's already an entry, do not allow adding
            count = Setting.objects.all().count()
            if count == 0:
                return True
        return False


A model with a single allowed row is nothing more than a perverted form of a "persisted object" -- maybe even a "persisted singleton"? Don't do that, that's not how models work.

Check out https://github.com/danielroseman/django-dbsettings


It looks like Ali Reza's answer but you can update the existed records and return the error message to any form that uses this model. I believe it is reliable and much easy to control.

class MyModel(models.Model):
    ...

    def clean(self):
        super().clean()
        if not self.id and MyModel.objects.exists():
            raise ValidationError('You cannot add more somethings.')


The following is a class I have created which can be used as a singleton class.

from django.db import models
class SingletonModel(models.Model):
    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        self.__class__.objects.exclude(id=self.id).delete()
        super(SingletonModel, self).save(*args, **kwargs)

    @classmethod
    def load(cls):
        try:
            return cls.objects.get()
        except cls.DoesNotExist:
            return cls()

From the above SingletonModel we can create multiple models, all of which will be having only one record

class ProjectSettings(SingletonModel):
    max_tickets = models.IntegerField(default=15)
    min_tickets = models.IntegerField(default=2)
    ...

We can access the only object of the settings model as follows

ProjectSettings.load().max_tickets

It is also possible to register ProjectSettings to django admin

@admin.register(ProjectSettings)
class ProjectSettingsAdmin(admin.ModelAdmin):
    list_display = [field.name for field in ProjectSettings._meta.get_fields()]
    def has_delete_permission(self, request, obj=None):
        # Nobody is allowed to delete
        return False


You can rewrite the save method on your model. Whenever the user wants to register a new record, you can check the existence then, either save the record or, reject the request.

class MyModel(models.Model):
    title = models.CharField(...)
    body = models.TextField(...)
    
    def save(self, *args, **kwargs):
        if MyModel.objects.exists():
            raise ValueError("This model has already its record.")
        else:
            super().save(*args, **kwargs)

You can also use validators. I prefer to use this method. Hope you find it useful.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号