Skip to content

Instantly share code, notes, and snippets.

@dminkovsky
Forked from mariocesar/models.py
Created February 2, 2013 08:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dminkovsky/4696559 to your computer and use it in GitHub Desktop.
Save dminkovsky/4696559 to your computer and use it in GitHub Desktop.
from django.db import models
from django.utils.decorators import wraps
from django.core.exceptions import ValidationError
def constraint(func):
@wraps(func)
def inner(*args, **kwargs):
func._is_constraint = True
return func(*args, **kwargs)
return inner
class ConstraintModel(models.Model):
class Meta:
abstract = True
def _fill_constraints_cache(self):
cache = []
for prop in dir(self):
_method = callable(getattr(self, prop))
if getattr(_method, '_is_constraint', False):
cache.append(_method)
self._constraints_cache = tuple(cache)
def _constraints(self):
try:
self._constraints_cache
except AttributeError:
self._fill_constraints_cache()
return self._constraints_cache
constraints = property(_constraints)
def clean(self):
for constraint in self.constraints:
res = constraint()
if isinstance(basestring, res):
raise ValidationError('%s' % res)
self._cleaned = True
def save(self, *args, **kwargs):
# Do not run clean twice if was already done, but make sure it executes
# if it have not been called
if not hasattr(self, '_cleaned'):
self.clean()
super(ResourceModel, self).save(*args, **kwargs)
"""
Example:
- Create a model and extend from ConstraintModel
- Using the @constraint decorator register what tests to check
- Return a string to raise a validation Error
"""
class Loan(ConstraintModel):
user = models.ForeignKey('auth.User')
amount = models.IntegerField()
@constraint
def check_amount(self):
# Do not not lend less than 100
if self.amount < 100:
# Return an error.
return 'Unable to lend less than 100$'
@constraint
def check_unpaid_loans(self):
# Do not not lend less than 100
if self.user.total_unpaid_loans() > 1000:
return 'Not allowed, the user has already more than 1000$ in unpaid loans.'
@constraint
def check_luckyness(self):
import random
lucky = lambda p, m: True if m() < p else False
if not lucky(0.2, random.random):
return 'We are sorry, but after a detailed review we are unable to lend money to this user'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment