CASCADE fixes in data model

This commit is contained in:
Philip Sargent 2023-03-06 16:37:38 +00:00
parent ccfc44a423
commit 94e145adce
5 changed files with 56 additions and 24 deletions

View File

@ -59,6 +59,9 @@ class Area(TroggleModel):
class CaveAndEntrance(models.Model): class CaveAndEntrance(models.Model):
"""CASCADE means that if the cave or the entrance is deleted, then this CaveAndEntrance
is deleted too
"""
cave = models.ForeignKey("Cave", on_delete=models.CASCADE) cave = models.ForeignKey("Cave", on_delete=models.CASCADE)
entrance = models.ForeignKey("Entrance", on_delete=models.CASCADE) entrance = models.ForeignKey("Entrance", on_delete=models.CASCADE)
entrance_letter = models.CharField(max_length=20, blank=True, null=True) entrance_letter = models.CharField(max_length=20, blank=True, null=True)
@ -240,6 +243,8 @@ class Cave(TroggleModel):
class EntranceSlug(models.Model): class EntranceSlug(models.Model):
"""If the Entrance is deleted, then this EntranceSlug is deleted too
"""
entrance = models.ForeignKey("Entrance", on_delete=models.CASCADE) entrance = models.ForeignKey("Entrance", on_delete=models.CASCADE)
slug = models.SlugField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True)
primary = models.BooleanField(default=False) primary = models.BooleanField(default=False)

View File

@ -17,7 +17,9 @@ todo = """
class CaveSlug(models.Model): class CaveSlug(models.Model):
"""Moved here to avoid nasty cyclic import error""" """Moved here to avoid nasty cyclic import error
CASCADE means that if the Cave is deleted, this is too
"""
cave = models.ForeignKey("Cave", on_delete=models.CASCADE) cave = models.ForeignKey("Cave", on_delete=models.CASCADE)
slug = models.SlugField(max_length=50, unique=True) slug = models.SlugField(max_length=50, unique=True)
@ -25,12 +27,13 @@ class CaveSlug(models.Model):
class LogbookEntry(TroggleModel): class LogbookEntry(TroggleModel):
"""Single parsed entry from Logbook""" """Single parsed entry from Logbook
Gets deleted if the Expedition gets deleted"""
date = ( date = (
models.DateField() models.DateField()
) # MJG wants to turn this into a datetime such that multiple Logbook entries on the same day can be ordered.ld() ) # MJG wants to turn this into a datetime such that multiple Logbook entries on the same day can be ordered.ld()
expedition = models.ForeignKey(Expedition, blank=True, null=True, on_delete=models.SET_NULL) # yes this is double- expedition = models.ForeignKey(Expedition, blank=True, null=True, on_delete=models.CASCADE) # yes this is double-
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
cave_slug = models.SlugField(max_length=50, blank=True, null=True) cave_slug = models.SlugField(max_length=50, blank=True, null=True)
place = models.CharField( place = models.CharField(
@ -84,6 +87,9 @@ class LogbookEntry(TroggleModel):
class PersonLogEntry(TroggleModel): class PersonLogEntry(TroggleModel):
"""Single Person going on a trip, which may or may not be written up. """Single Person going on a trip, which may or may not be written up.
It could account for different T/U for people in same logbook entry. It could account for different T/U for people in same logbook entry.
CASCADE means that if the personexpedition or the logbookentry is deleted,
then this PersonLogEntry is deleted too
""" """
personexpedition = models.ForeignKey("PersonExpedition", null=True, on_delete=models.CASCADE) personexpedition = models.ForeignKey("PersonExpedition", null=True, on_delete=models.CASCADE)

View File

@ -12,6 +12,10 @@ from django.urls import reverse
class SurvexDirectory(models.Model): class SurvexDirectory(models.Model):
"""This relates a Cave to the primary SurvexFile which is the 'head' of the survex tree for
that cave. Surely this could just be a property of Cave ? No. Several subdirectories
all relate to the same Cave
"""
path = models.CharField(max_length=200) path = models.CharField(max_length=200)
cave = models.ForeignKey("Cave", blank=True, null=True, on_delete=models.SET_NULL) cave = models.ForeignKey("Cave", blank=True, null=True, on_delete=models.SET_NULL)
primarysurvexfile = models.ForeignKey( primarysurvexfile = models.ForeignKey(
@ -66,7 +70,10 @@ class SurvexFile(models.Model):
class SurvexStationLookUpManager(models.Manager): class SurvexStationLookUpManager(models.Manager):
"""Don't know what this does, """Don't know what this does,
https://docs.djangoproject.com/en/dev/topics/db/managers/""" https://docs.djangoproject.com/en/dev/topics/db/managers/
This changes the .objects thinggy to use a case-insensitive match name__iexact
so that now SurvexStation.objects.lookup() works as a case-insensitive match
"""
def lookup(self, name): def lookup(self, name):
blocknames, sep, stationname = name.rpartition(".") blocknames, sep, stationname = name.rpartition(".")
return self.get(block=SurvexBlock.objects.lookup(blocknames), name__iexact=stationname) return self.get(block=SurvexBlock.objects.lookup(blocknames), name__iexact=stationname)
@ -75,7 +82,7 @@ class SurvexStationLookUpManager(models.Manager):
class SurvexStation(models.Model): class SurvexStation(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
block = models.ForeignKey("SurvexBlock", null=True, on_delete=models.SET_NULL) block = models.ForeignKey("SurvexBlock", null=True, on_delete=models.SET_NULL)
objects = SurvexStationLookUpManager() objects = SurvexStationLookUpManager() # overwrites SurvexStation.objects
x = models.FloatField(blank=True, null=True) x = models.FloatField(blank=True, null=True)
y = models.FloatField(blank=True, null=True) y = models.FloatField(blank=True, null=True)
z = models.FloatField(blank=True, null=True) z = models.FloatField(blank=True, null=True)
@ -101,18 +108,22 @@ class SurvexStation(models.Model):
# #
# Single SurvexBlock # Single SurvexBlock
# #
class SurvexBlockLookUpManager(models.Manager): # class SurvexBlockLookUpManager(models.Manager):
"""Don't know what this does, # """Don't know what this does,
https://docs.djangoproject.com/en/dev/topics/db/managers/ """ # https://docs.djangoproject.com/en/dev/topics/db/managers/
def lookup(self, name):
if name == "": # This changes the .objects_set thinggy to use a case-insensitive match name__iexact
blocknames = [] # so that now SurvexBlock.objects.lookup() works as a case-insensitive match UNUSED
else: # """
blocknames = name.split(".") # def lookup(self, name):
block = SurvexBlock.objects.get(parent=None, survexfile__path=settings.SURVEX_TOPNAME) # if name == "":
for blockname in blocknames: # blocknames = []
block = SurvexBlock.objects.get(parent=block, name__iexact=blockname) # else:
return block # blocknames = name.split(".")
# # block = SurvexBlock.objects.get(parent=None, survexfile__path=settings.SURVEX_TOPNAME)
# for blockname in blocknames:
# block = SurvexBlock.objects.get(parent=block, name__iexact=blockname)
# return block
class SurvexBlock(models.Model): class SurvexBlock(models.Model):
@ -120,7 +131,7 @@ class SurvexBlock(models.Model):
Multiple anonymous survex blocks are possible within the same surfex file Multiple anonymous survex blocks are possible within the same surfex file
""" """
objects = SurvexBlockLookUpManager() # objects = SurvexBlockLookUpManager()
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
parent = models.ForeignKey("SurvexBlock", blank=True, null=True, on_delete=models.SET_NULL) parent = models.ForeignKey("SurvexBlock", blank=True, null=True, on_delete=models.SET_NULL)
@ -163,10 +174,13 @@ class SurvexBlock(models.Model):
class SurvexPersonRole(models.Model): class SurvexPersonRole(models.Model):
"""The CASCADE means that if a SurvexBlock or a Person is deleted, then the SurvexPersonRole
is deleted too
"""
survexblock = models.ForeignKey("SurvexBlock", on_delete=models.CASCADE) survexblock = models.ForeignKey("SurvexBlock", on_delete=models.CASCADE)
# increasing levels of precision, Surely we only need survexblock and person now that we have no link to a logbook entry? # increasing levels of precision, Surely we only need survexblock and person now that we have no link to a logbook entry?
personname = models.CharField(max_length=100) personname = models.CharField(max_length=100)
person = models.ForeignKey("Person", blank=True, null=True, on_delete=models.SET_NULL) # not needed person = models.ForeignKey("Person", blank=True, null=True, on_delete=models.CASCADE) # not needed
personexpedition = models.ForeignKey("PersonExpedition", blank=True, null=True, on_delete=models.SET_NULL) personexpedition = models.ForeignKey("PersonExpedition", blank=True, null=True, on_delete=models.SET_NULL)
def __str__(self): def __str__(self):

View File

@ -11,7 +11,7 @@ import settings
"""This file declares TroggleModel which inherits from django.db.models.Model """This file declares TroggleModel which inherits from django.db.models.Model
All TroggleModel and models.Model subclasses inherit persistence in the django relational database. This is known as All TroggleModel and models.Model subclasses inherit persistence in the django relational database. This is known as
the django Object Relational Mapping (ORM). the django Object Relational Mapping (ORM).
There are more subclasses define in models_caves.py models_survex.py etc. There are more subclasses defined in models/caves.py models/survex.py etc.
""" """
@ -33,7 +33,7 @@ class TroggleModel(models.Model):
class DataIssue(TroggleModel): class DataIssue(TroggleModel):
"""When importing cave data any validation problems produce a message which is """When importing cave data any validation problems produce a message which is
recorded as a DataIssue. The django admin system automatically prodiuces a page listing recorded as a DataIssue. The django admin system automatically produces a page listing
these at /admin/core/dataissue/ these at /admin/core/dataissue/
This is a use of the NOTIFICATION pattern: This is a use of the NOTIFICATION pattern:
https://martinfowler.com/eaaDev/Notification.html https://martinfowler.com/eaaDev/Notification.html
@ -55,7 +55,6 @@ class DataIssue(TroggleModel):
def __str__(self): def __str__(self):
return f"{self.parser} - {self.message}" return f"{self.parser} - {self.message}"
# #
# single Expedition, usually seen by year # single Expedition, usually seen by year
# #
@ -150,7 +149,10 @@ class Person(TroggleModel):
class PersonExpedition(TroggleModel): class PersonExpedition(TroggleModel):
"""Person's attendance to one Expo""" """Person's attendance to one Expo
CASCADE means that if an expedition or a person is deleted, the PersonExpedition
is deleted too
"""
expedition = models.ForeignKey(Expedition, on_delete=models.CASCADE) expedition = models.ForeignKey(Expedition, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE) person = models.ForeignKey(Person, on_delete=models.CASCADE)

View File

@ -212,6 +212,11 @@ def LoadPositions():
# But why are we doing this? Why do we need the survexblock id for each of these ? # But why are we doing this? Why do we need the survexblock id for each of these ?
# ..because mostly they don't actually appear in any SVX file. We should match them up # ..because mostly they don't actually appear in any SVX file. We should match them up
# via the cave data, not by this half-arsed syntactic match which almost never works. PMS. # via the cave data, not by this half-arsed syntactic match which almost never works. PMS.
# It is pointless linking them all to the root survexblock, they don't need it.
# If there is a link to a survexblock it should be the one the station appears in !
# But we are reading the .pos file so we only know the SurvexFile not the SurvexBlock..
# ghastly.
if False: if False:
try: try:
sbqs = SurvexBlock.objects.filter(survexpath=blockpath) sbqs = SurvexBlock.objects.filter(survexpath=blockpath)