from django.db import models
from django.db.models import Field, ManyToOneRel
from random import choice, sample
[docs]
class QuerySet(models.QuerySet):
"""Extends the base QuerySey with random() and sample() methods
"""
[docs]
def random(self):
ids = self.values_list('id', flat=True)
if not ids:
raise self.model.DoesNotExist(f"{self.model.__name__} is empty")
return self.get(id=choice(ids))
[docs]
def sample(self, n):
ids = self.values_list('id', flat=True)
if len(ids) < n:
raise self.model.DoesNotExist(f"Requested {n} but {self.model.__name__} only contains {len(ids)} objects.")
return self.filter(id__in=sample([i for i in ids], n))
[docs]
def serialize(obj):
"""Safely converts an object to a dict,
regardless of whether it implements banjo's `to_dict` interface.
"""
if hasattr(obj, 'to_dict'):
try:
return obj.to_dict(with_related=False)
except TypeError:
return obj.to_dict()
else:
return {'id': obj.id}
[docs]
class Model(models.Model):
objects = models.Manager.from_queryset(QuerySet)()
[docs]
@classmethod
def from_dict(cls, props):
"""Tries to create an instance of Model from props.
"""
if 'id' in props:
del props['id']
return cls(**props)
[docs]
def to_dict(self, with_related=True):
"""Returns a json representation of the Model.
"""
d = {}
for field in self._meta.get_fields():
if with_related and isinstance(field, models.ForeignKey):
related_object = getattr(self, field.name)
d[field.name] = serialize(related_object)
elif isinstance(field, Field):
d[field.name] = field.value_from_object(self)
elif with_related and isinstance(field, ManyToOneRel):
related_objects = getattr(self, field.get_accessor_name()).all()
d[field.get_accessor_name()] = [serialize(obj) for obj in related_objects]
return d
def __str__(self):
return "<{} {}>".format(self.__class__.__name__, self.to_dict())
def __repr__(self):
return self.__str__()
[docs]
class BooleanField(models.BooleanField):
"""A database column which stores a boolean.
The default value is False.
"""
def __init__(self, *args, **kwargs):
kwargs['default'] = False
models.BooleanField.__init__(self, *args, **kwargs)
[docs]
class IntegerField(models.IntegerField):
"""A database column which stores an integer.
The default value is 0.
"""
def __init__(self, *args, **kwargs):
kwargs['default'] = 0
models.IntegerField.__init__(self, *args, **kwargs)
[docs]
class FloatField(models.FloatField):
"""A database column which stores a float.
The default value is 0.0.
"""
def __init__(self, *args, **kwargs):
kwargs['default'] = 0.0
models.FloatField.__init__(self, *args, **kwargs)
[docs]
class StringField(models.TextField):
"""A database column which stores a string.
The default value is '', the empty string.
"""
def __init__(self, *args, **kwargs):
kwargs['default'] = ''
models.TextField.__init__(self, *args, **kwargs)
[docs]
class ForeignKey(models.ForeignKey):
"""A database column which links a model to another model.
"""
def __init__(self, *args, **kwargs):
kwargs['on_delete'] = models.CASCADE
models.ForeignKey.__init__(self, *args, **kwargs)