models.py 2.52 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from sqlalchemy.ext.declarative import as_declarative
from sqlalchemy import Column, Integer, String, Float, JSON, ForeignKey, Enum
from sqlalchemy.orm import relationship, validates

from . import FieldType

@as_declarative()
class Base(object):
    pass


class Point(Base):
    """
    Represents actual instances of any and all points on the map.
    """
    __tablename__ = 'point'
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String, nullable=True)
    lat = Column(Float, nullable=False)
    lon = Column(Float, nullable=False)
    attributes = Column(JSON, nullable=False)

    # Relationships
    category_id = Column(Integer, ForeignKey('category.id'), nullable=False)
    category = relationship('Category')
    parent_id = Column(Integer, ForeignKey('point.id'), nullable=True)
    parent = relationship('Point', remote_side=[id])
    children = relationship('Point')

    def __init__(self, **kwargs):
        # Need to load category first or else attribute validation will fail
        if 'category' in kwargs:
            self.category = kwargs.pop('category')
        
        super(Point, self).__init__(**kwargs)

Zach Perkins's avatar
Zach Perkins committed
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    @validates('attributes')
    def validate_data(self, _, data):
        if data is None:
            return

        fields = self.category.fields

        for key in data:
            # Find Field object that corresponds to this key
            for field in fields:
                if field.slug == key:
                    break
            else:
                raise ValueError(f'extra data "{key}" must be a registered field')
            field.validate_data(data[key])
        return data


class Category(Base):
    """
    Represent a schema for a single category of objects (e.g. water fountain or bathroom)
    """
    __tablename__ = 'category'

    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String, nullable=False, unique=True)
    icon = Column(String, nullable=True)

    fields = relationship("Field")


class Field(Base):
    """
    Represents a single field in the Category schema.
    """
    __tablename__ = 'field'

    id = Column(Integer, primary_key=True, autoincrement=True)
    slug = Column(String, nullable=False)
    name = Column(String, nullable=False)
    type = Column(Enum(FieldType), nullable=False)

    # Relationship
    category_id = Column(Integer, ForeignKey('category.id'))

    def validate_data(self, data):
        """
        Verify that data is the correct type for this Field.
        """
        self.type.validate(data)