Skip to content

Commit

Permalink
Add ReprMixin and various getter mixins
Browse files Browse the repository at this point in the history
  • Loading branch information
JanLikar committed Nov 16, 2016
1 parent 69b131f commit 55a62d5
Showing 1 changed file with 132 additions and 2 deletions.
134 changes: 132 additions & 2 deletions src/pyramid_basemodel/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
'PolymorphicBaseMixin',
'PolymorphicMixin',
'TouchMixin',
'GetByAttrMixin,',
'GetByIdMixin',
'GetBySlugMixin',
'GetByTitleMixin',
'GetByNameMixin',
'GetByEmailMixin',
'ReprMixin',
]

import logging
Expand Down Expand Up @@ -50,7 +57,6 @@ def __mapper_args__(self):
"""Set the ``polymorphic_identity`` value to the lower case class name."""

return {'polymorphic_identity': self.__class__.__name__.lower()}



class TouchMixin(object):
Expand Down Expand Up @@ -80,5 +86,129 @@ def touch(self, propagate=True, now=None, save=None):
# Call propagate touch.
if propagate:
self.propagate_touch()



sentinel = object()


class GetByAttrMixin(object):
"""A mixin for adding ``by_attr`` helper to models."""

def by_attr(cls, attr, value):
"""Get a Model object by one of its attributes.
Return ``None`` if the object is not found.
"""
return cls.query.filter(getattr(cls, attr)==value).first()

class GetByIdMixin(GetByAttrMixin):
"""A mixin for adding ``by_id`` helper to models."""

@classmethod
def by_id(cls, id, default=sentinel):
"""Get a Model object by its id.
Return ``None`` if the object is not found.
On error raise exception or return `default`, if it is set.
"""
try:
id = int(id)
return cls.by_attr('id', id)
except (ValueError, TypeError) as exc:
if default == sentinel:
raise exc
else:
return default


class GetBySlugMixin(GetByAttrMixin):
"""A mixin for adding ``by_slug`` helper to models."""

@classmethod
def by_slug(cls, slug, default=sentinel):
"""Get a Model object by its slug.
Return ``None`` if the object is not found.
On Unicode error raise exception or return `default`, if it is set.
"""
try:
str(slug).decode('ascii')
return cls.by_attr('slug', slug)
except UnicodeDecodeError as exc:
if default == sentinel:
raise exc
else:
return default


class GetByTitleMixin(GetByAttrMixin):
"""A mixin for adding ``by_title`` helper to models."""

@classmethod
def by_title(cls, title, default=sentinel):
"""Get a Model object by its title.
Return ``None`` if the object is not found.
On Unicode error raise exception or return `default`, if it is set.
"""
try:
str(title).decode('ascii')
return cls.by_attr('title', title)
except UnicodeDecodeError as exc:
if default == sentinel:
raise exc
else:
return default


class GetByNameMixin(GetByAttrMixin):
"""A mixin for adding by_name method to models."""

@classmethod
def by_name(cls, name):
"""Get a Model object by name.
Return ``None`` if the object is not found.
"""

return cls.by_attr('name', name)


class GetByEmailMixin(GetByAttrMixin):
"""A mixin for adding ``by_email`` helper to models."""

@classmethod
def by_email(cls, email):
"""Get a Model object by its email.
Return ``None`` if the object is not found.
"""
return cls.by_attr('email', email)


class ReprMixin(object):
"""Provides a generic ``__repr__`` implementation."""

def __repr__(self):
"""Return a generic string representation of a class.
The following format is used: '<class_name:id (field=value...)>.
Only name, title, slug, email fields are included in the string.
"""
cls = type(self)
fields = ['name', 'title', 'slug', 'email']
formatted_fields = []
class_name = cls.__name__
id = cls.__dict__.get('id', None)

for field_name in fields:
try:
field_value = cls.__dict__[field_name]
except KeyError:
continue
if field_value:
field_value = field_value.encode('utf-8')
formatted_fields.append('{}={}, '.format(field_name, field_value))
concat_fields = ''.join(formatted_fields)[:-2]

return '<{}:{} ({})>'.format(class_name, id, concat_fields)

0 comments on commit 55a62d5

Please sign in to comment.