Source code for rattail.db.model.core

# -*- coding: utf-8; -*-
################################################################################
#
#  Rattail -- Retail Software Framework
#  Copyright © 2010-2023 Lance Edgar
#
#  This file is part of Rattail.
#
#  Rattail is free software: you can redistribute it and/or modify it under the
#  terms of the GNU General Public License as published by the Free Software
#  Foundation, either version 3 of the License, or (at your option) any later
#  version.
#
#  Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
#  details.
#
#  You should have received a copy of the GNU General Public License along with
#  Rattail.  If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Core Data Models
"""

import re

import sqlalchemy as sa
from sqlalchemy import orm
try:
    from sqlalchemy.orm import declarative_base
except ImportError:
    from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy

from rattail.core import Object
from rattail.time import make_utc

# These are imported because most sibling modules import them *from* here (if
# that makes sense).  Probably need to think harder about the "best" place for
# these things to live.
from rattail.db.core import uuid_column, getset_factory
from rattail.db.types import GPCType


[docs] class ModelBase(Object): """ Base class for all data models. """ def __repr__(self): mapper = orm.object_mapper(self) key_props = [] for prop in mapper.column_attrs: primary = True for column in prop.columns: if column not in mapper.primary_key: primary = False break if primary: key_props.append(prop) keys = ['{}={}'.format(prop.key, repr(getattr(self, prop.key))) for prop in key_props] return "{0}({1})".format(self.__class__.__name__, ', '.join(keys)) def get_data_dict(self): """ Return a simple data dict containing field values for all table columns present for the object (row). :returns: Dict of field data. """ mapper = orm.object_mapper(self) return dict([(prop.key, getattr(self, prop.key)) for prop in mapper.column_attrs]) @classmethod def get_model_title(cls): """ Return a "humanized" version of the model name, for display in templates etc. """ if hasattr(cls, 'model_title'): return cls.model_title # convert "CamelCase" to "Camel Case" return re.sub(r'([a-z])([A-Z])', r'\g<1> \g<2>', cls.__name__) @classmethod def get_model_title_plural(cls): """ Return a "humanized" version of the plural model name, for display in templates etc. """ if hasattr(cls, 'model_title_plural'): return cls.model_title_plural return '{}s'.format(cls.get_model_title()) @classmethod def make_proxy(cls, main_class, extension, name, proxy_name=None): setattr(main_class, proxy_name or name, association_proxy( extension, name, creator=lambda value: cls(**{name: value}), getset_factory=getset_factory))
Base = declarative_base(cls=ModelBase)
[docs] class Setting(Base): """ Represents a "raw" config setting stored within the database. """ __tablename__ = 'setting' name = sa.Column(sa.String(length=255), primary_key=True) value = sa.Column(sa.Text()) def __str__(self): return self.name or ''
[docs] class Change(Base): """ Represents a changed (or deleted) record, which is pending synchronization to another database. """ __tablename__ = 'change' uuid = uuid_column() class_name = sa.Column(sa.String(length=40), nullable=False) # TODO: this needs to be renamed to e.g. object_key? now that we sometimes # need to (mis)use it for non-UUID keys instance_uuid = sa.Column(sa.String(length=255), nullable=False, doc=""" Key for the object which was changed/etc. In most cases this will simply be the object's UUID value, hence the column name. However in some cases it may contain something else; this is often needed in order to properly sync deletions to non-rattail systems, where the UUID is meaningless. """) object_key = orm.synonym('instance_uuid', doc=""" Synonym to the :attr:`instance_uuid` column. New code should perhaps reference the attribute by this (``object_key``) name instead, as the ``instance_uuid`` column may be renamed at some point, to this name. """) deleted = sa.Column(sa.Boolean(), nullable=False) def __repr__(self): return "Change(class_name={}, instance_uuid={}, deleted={})".format( repr(self.class_name), repr(self.instance_uuid), repr(self.deleted))
class Note(Base): """ Arbitrary note attached to a supported object. """ __tablename__ = 'note' __table_args__ = ( sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name='note_fk_created_by'), sa.Index('note_ix_parent', 'parent_type', 'parent_uuid'), ) __versioned__= {} uuid = uuid_column() parent_type = sa.Column(sa.String(length=20), nullable=False) parent_uuid = sa.Column(sa.String(length=32), nullable=False) type = sa.Column(sa.String(length=15)) subject = sa.Column(sa.String(length=255), nullable=True) text = sa.Column(sa.Text(), nullable=False) created = sa.Column(sa.DateTime(), nullable=False, default=make_utc) created_by_uuid = sa.Column(sa.String(length=32), nullable=False) created_by = orm.relationship('User') __mapper_args__ = {'polymorphic_on': parent_type} def __str__(self): return str(self.text)