Validation of quota

The model of plans introduced in this application make use of quota, which are just some arbitrarily given limits. Quota definition is quite flexible and allows to define many different types of restrictions. Conceptually you may need one of following types of quota in your system.

  • limiting resources - limiting number of some entities on user account (typically an entities is a single django model instance); e.g. an image gallery system could limit number of images per account as one of the system parameter. Each image is represented by one instance of e.g. UploadedImage model.
  • limiting states - limiting that some entities on user account can be in a given state (typically some model instance attributes can have specific values)
  • limiting actions - limiting if user can perform some kind of action (typically an action is a specific POST request which creates/updates/delete a model instance)

Note

Presented list of quota types is only a conceptual classification. It may not be directly addressed in django-plans API, however django-plans aims to support those kind of limitations.

Account complete validation

Complete account validation is needed when user is switching a plan (or in a general - activating a plan). The reason is that user account can be in the state that exhausting limits of new plan (e.g. on downgrade). Plan should not be activated on the user account until user will not remove over limit resources until the account could validate in limits of the new plan.

In django-plans there is a common validation mechanism which requires defining PLANS_VALIDATORS variable in settings.py.

The format of PLANS_VALIDATORS variable is given as a dict:

PLANS_VALIDATORS = {
    '<QUOTA_CODE_NAME>' :  '<full.python.path.to.validator.class>',
    [...]
}

First of all this variable defines all quota that should be validated on any plan activation.

Note

Please note that the only quota that can be added to PLANS_VALIDATORS are “limiting resources quota” and “limiting states” quota. Those are the kind of quota that conceptually can be validated within the database state. The third kind of quota (“limiting actions quota”) are to be checked on to go when user is just using it’s account and performing certain actions.

Secondly each quota has a specific validator defined that is custom to your need of validations.

Quota validators

Each validator should inherit from plans.validators.QuotaValidator.

class plans.validators.QuotaValidator[source]

Base class for all Quota validators needed for account activation

code
default_quota_value = None
get_error_message(quota_value, **kwargs)[source]
get_quota_value(user, quota_dict=None)[source]

Returns quota value for a given user

on_activation(user, quota_dict=None, **kwargs)[source]

Hook for any action that validator needs to do while successful activation of the plan Most useful for validators not required to activate, e.g. some “option” is turned ON for user but when user downgrade plan this option should be turned OFF automatically rather than stops account activation

required_to_activate = True

Validator should have defined __call__(self, user, **kwargs) method which should raise django.core.exceptions.ValidationError if account does not meet limits requirement.

Model count validator

Currently django-plans is shipped with one handy validator. It can easily validate number of instances of any model for a given user.

class plans.validators.ModelCountValidator[source]

Validator that checks if there is no more than quota number of objects given model

get_error_message(quota_value, **kwargs)[source]
get_queryset(user)[source]
model

We recommend to create validators.py in your application path with your own custom validators.

E.g. this limits number of Foo instances in the example project, in foo/validators.py:

from example.foo.models import Foo
from plans.validators import ModelCountValidator


class MaxFoosValidator(ModelCountValidator):
    code = 'MAX_FOO_COUNT'
    model = Foo

    def get_queryset(self, user):
        return super(MaxFoosValidator, self).get_queryset(user).filter(user=user)

max_foos_validator = MaxFoosValidator()

You can easily re-use it also in create model form for this object to check if user can add a number of instances regarding his quota, in foo/forms.py:

from django.forms import ModelForm, HiddenInput
from example.foo.models import Foo
from example.foo.validators import max_foos_validator


class FooForm(ModelForm):
    class Meta:
        model = Foo
        widgets = {'user' : HiddenInput,}

    def clean(self):
        cleaned_data = super(FooForm, self).clean()
        max_foos_validator(cleaned_data['user'], add=1)
        return cleaned_data

Model attribute validator

This validator can validate that every object returned from a queryset have correct value of attribute.

class plans.validators.ModelAttributeValidator[source]

Validator checks if every obj.attribute value for a given model satisfy condition provided in check_attribute_value() method.

Warning

ModelAttributeValidator requires get_absolute_url() method on provided model.

attribute
check_attribute_value(attribute_value, quota_value)[source]
get_error_message(quota_value, **kwargs)[source]

E.g.:

from example.foo.models import Foo
from plans.validators import ModelCountValidator


class MaxFooSizeValidator(ModelAttributeValidator):
    code = 'MAX_FOO_SIZE'
    model = Foo
    attribute = 'size'

    def get_queryset(self, user):
        return super(MaxFoosValidator, self).get_queryset(user).filter(user=user)

max_foo_size_validator = MaxFooSizeValidator()

This validator will ensure that user does not have any object with attribute ‘size’ which is greater then the quota. If you need to provide any custom comparison other than “greater than” just override method check_attribute_value(attribute_value, quota_value).