Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure second GIS database and router, add example model and skeleton models for remainder of quercus schema #70

Merged
merged 7 commits into from
Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 190 additions & 1 deletion asset_dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def total_score(self):
return total_score

def add_score_to_queryset(self):
'''we'll need to add the total scores to the queryset'''
"""we'll need to add the total scores to the queryset"""

def __str__(self):
return self.project.name
Expand Down Expand Up @@ -248,3 +248,192 @@ class DummyProject(models.Model):
def __str__(self):
""""String for representing the model object"""
return self.name


class GISModel(models.Model):
"""
Migrations will not be created for models inheriting from this base class
because of allow_migrate() method in the GISRouter.
"""

class Meta:
abstract = True
managed = False
app_label = 'asset_dashboard_gis'


class NaturePreserves(GISModel):

class Meta(GISModel.Meta):
db_table = '"quercus"."nature_preserves"'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed to quote the schema and table names for this to work. Not sure why, also lost StackOverflow reference, sorry!


id = models.AutoField(primary_key=True, db_column='nature_preserves_id')
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a primary key isn't explicitly specified, Django tries to use id. There isn't an id column in many of these tables, so you have to specify it in db_column.

site_name = models.CharField(max_length=254)

def __str__(self):
return self.site_name


class Buildings(GISModel):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smcalilly roughed in models for the remaining tables. The db_table attribute needs to be updated to quote the schema and table names, and the columns we need to use need to be defined as attributes.


class Meta(GISModel.Meta):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh cool, i was wondering how to inherit so i didn't have to add managed = False to every model.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! The class-on-class Meta inheritance is a little wonky, but it's buried down there in the docs. (I had to Google it 😅)

db_table = 'quercus.buildings'


class Holdings(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.holdings'


class LicenseIGA(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.license_iga'


class MowAreaDB(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.mow_area_db'


class MwrdFpdLease(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.mwrd_fpd_lease'


class Names(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.names'


class ParkingEntrance(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.parking_entrance'


class ParkingEntranceInfo(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.parking_entrance_info'


class ParkingEval17(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.parking_eval17'


class ParkingLots(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.parking_lots'


class PicnicGroves(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.picnicgroves'


class PoiAmenity(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.poi_amenity'


class PoiDesc(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.poi_desc'


class PoiInfo(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.poi_info'


class PoiToTrails(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.poi_to_trails'


class PointsOfInterest(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.pointsofinterest'


class Regions(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.regions'


class Signage(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.signage'


class TrailSubsystemLu(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.trail_subsystem_lu'


class Trails(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.trails'


class TrailsAmenity(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.trails_amenity'


class TrailsDesc(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.trails_desc'


class TrailsInfo(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.trails_info'


class TrailsMaintenance(GISModel):

class Meta(GISModel.Meta):
db_table = 'quercus.trails_maintenace'


class Zones(GISModel):
class Meta(GISModel.Meta):
db_table = 'quercus.zones'

# fpdcc=# \d quercus.zones
# Table "quercus.zones"
# Column | Type | Collation | Nullable | Default
# --------------+-----------------------------+-----------+----------+------------------------------------------------
# zone_id | integer | | not null | nextval('quercus.zones_zone_id_seq'::regclass)
# zone | character varying(10) | | |
# abbreviation | character varying(2) | | |
# geom | geometry(MultiPolygon,3435) | | |
# Indexes:
# "zones_pkey" PRIMARY KEY, btree (zone_id)

# zones_pk = models.IntegerField(primary_key=True)
zone_id = models.IntegerField(primary_key=True)
zone = models.CharField(max_length=10)
geom = models.MultiPolygonField(srid=3435) # TODO: i think this was srid? `geom | geometry(MultiPolygon,3435)`
52 changes: 52 additions & 0 deletions asset_dashboard/routers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class GISRouter:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tells Django which DB to route ORM queries to. See: https://docs.djangoproject.com/en/3.2/topics/db/multi-db/#automatic-database-routing

"""
Route queries for tables in the specied schemas to the "fp_postgis" database.
"""
GIS_DB_ALIAS = 'fp_postgis'
GIS_APP_LABEL = 'asset_dashboard_gis'

def db_for_read(self, model, **hints):
if model._meta.app_label == self.GIS_APP_LABEL:
return self.GIS_DB_ALIAS
return None

def db_for_write(self, model, **hints):
"""
Specify where to write GIS data so we can create test data using the
ORM.

N.b., we probably should not modify CCFP's data. This method assumes
that they've granted us read-only access, i.e., the database will raise
an exception for any write requests against the live server.
"""
if model._meta.app_label == self.GIS_APP_LABEL:
return self.GIS_DB_ALIAS
return None

def allow_relation(self, obj1, obj2, **hints):
"""
From the Django docs:

> If no router has an opinion (i.e. all routers return None), only
relations within the same database are allowed.

https://docs.djangoproject.com/en/3.2/topics/db/multi-db/#allow_relation

Thus, this should disallow the relation of tables in the specified
schemas to objects in the default database. This should be ok because
we will copy necessary GIS data to the default database for subsequent
relation and manipulation.
"""
return None

def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Only create and run migrations for models that are not marked by the
GIS_APP_LABEL. This prevents the creation of migrations for GIS models
and stops Django from trying to create a django_migrations table in the
GIS_DB_ALIAS database, since we won't have write access. Reference:
https://stackoverflow.com/a/43553063
"""
if app_label == self.GIS_APP_LABEL:
return False
return db == 'default'
22 changes: 15 additions & 7 deletions asset_dashboard/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,22 @@
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases

DATABASES = {}
DATABASES = {
'default': dj_database_url.parse(
os.getenv('DATABASE_URL', 'postgis://postgres:postgres@postgres:5432/database'),
conn_max_age=600,
ssl_require=True if os.getenv('POSTGRES_REQUIRE_SSL') else False,
engine='django.contrib.gis.db.backends.postgis'
),
'fp_postgis': dj_database_url.parse(
os.getenv('GIS_DATABASE_URL', 'postgis://postgres:postgres@fp-postgis:5432/fpdcc'),
conn_max_age=600,
ssl_require=True if os.getenv('POSTGRES_REQUIRE_SSL') else False,
engine='django.contrib.gis.db.backends.postgis'
),
}

DATABASES['default'] = dj_database_url.parse(
os.getenv('DATABASE_URL', 'postgis://postgres:postgres@postgres:5432/database'),
conn_max_age=600,
ssl_require=True if os.getenv('POSTGRES_REQUIRE_SSL') else False,
engine='django.contrib.gis.db.backends.postgis'
)
DATABASE_ROUTERS = ['asset_dashboard.routers.GISRouter']

# Caching
# https://docs.djangoproject.com/en/3.0/topics/cache/
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ services:
depends_on:
postgres:
condition: service_healthy
fp-postgis:
condition: service_healthy
volumes:
# Mount the development directory as a volume into the container, so
# Docker automatically recognizes your changes.
Expand Down
Loading