Add ORM models for the pixel grids

- add Grid, Pixel, and AddressPixelAssociation ORM models
- each Grid belongs to a City an is characterized by the side_length
  of all the square Pixels contained in it
- Pixels aggregate Addresses => many-to-many relationship (that is
  modeled with SQLAlchemy's Association Pattern to implement a couple
  of constraints)
This commit is contained in:
Alexander Hess 2021-01-03 19:33:36 +01:00
commit f996376b13
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
15 changed files with 665 additions and 36 deletions

View file

@ -0,0 +1,56 @@
"""Model for the many-to-many relationship between `Address` and `Pixel` objects."""
import sqlalchemy as sa
from sqlalchemy import orm
from urban_meal_delivery.db import meta
class AddressPixelAssociation(meta.Base):
"""Association pattern between `Address` and `Pixel`.
This approach is needed here mainly because it implicitly
updates the `_city_id` and `_grid_id` columns.
Further info:
https://docs.sqlalchemy.org/en/stable/orm/basic_relationships.html#association-object # noqa:E501
"""
__tablename__ = 'addresses_pixels'
# Columns
_address_id = sa.Column('address_id', sa.Integer, primary_key=True)
_city_id = sa.Column('city_id', sa.SmallInteger, nullable=False)
_grid_id = sa.Column('grid_id', sa.SmallInteger, nullable=False)
_pixel_id = sa.Column('pixel_id', sa.Integer, primary_key=True)
# Constraints
__table_args__ = (
# An `Address` can only be on a `Grid` ...
sa.ForeignKeyConstraint(
['address_id', 'city_id'],
['addresses.id', 'addresses.city_id'],
onupdate='RESTRICT',
ondelete='RESTRICT',
),
# ... if their `.city` attributes match.
sa.ForeignKeyConstraint(
['grid_id', 'city_id'],
['grids.id', 'grids.city_id'],
onupdate='RESTRICT',
ondelete='RESTRICT',
),
# Each `Address` can only be on a `Grid` once.
sa.UniqueConstraint('address_id', 'grid_id'),
# An association must reference an existing `Grid`-`Pixel` pair.
sa.ForeignKeyConstraint(
['pixel_id', 'grid_id'],
['pixels.id', 'pixels.grid_id'],
onupdate='RESTRICT',
ondelete='RESTRICT',
),
)
# Relationships
address = orm.relationship('Address', back_populates='pixels')
pixel = orm.relationship('Pixel', back_populates='addresses')