Skip to content

Commit 5322caf

Browse files
committed
Reformat special deletion rules
1 parent 212be10 commit 5322caf

File tree

4 files changed

+45
-46
lines changed

4 files changed

+45
-46
lines changed

specifyweb/specify/build_models.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .datamodel import Datamodel, Table, Relationship, Field
88
from .deletion_rules import SPECIAL_DELETION_RULES, ADDITIONAL_DELETE_BLOCKERS
99

10+
# this moduel's parent directory, 'specify'
1011
appname = __name__.split('.')[-2]
1112

1213
orderings = {
@@ -18,7 +19,7 @@
1819
'Collector': ('ordernumber',),
1920
}
2021

21-
def make_model(module: str, table: Table, datamodel: Datamodel) -> models.base.ModelBase:
22+
def make_model(module: str, table: Table, datamodel: Datamodel) -> models.Model:
2223
"""Returns a Django model class based on the
2324
definition of a Specify table.
2425
"""
@@ -96,18 +97,23 @@ def make_relationship(modelname : str, rel: Relationship, datamodel: Datamodel)
9697
return None
9798

9899
try:
99-
on_delete = SPECIAL_DELETION_RULES[f"{rel.name.capitalize()}"][f"{modelname.lower()}"]
100+
on_delete = SPECIAL_DELETION_RULES[f"{modelname}"][f"{rel.name.lower()}"]
100101
except KeyError:
101102
reverse = datamodel.reverse_relationship(rel)
102103

103104
if reverse is not None and reverse.dependent:
105+
106+
# Example: Current `rel` is Accessionagent.accession,
107+
# Reverse (Accession.accessionAgents) is flagged as dependent
108+
# (see dependent_fields in .load_datamodel.py), so Cascade Accession Agent
109+
# when deleting Accession
104110
on_delete = models.CASCADE
105111
elif modelname in ADDITIONAL_DELETE_BLOCKERS.keys():
106112
on_delete = protect
107113
else:
108114
on_delete = protect
109115

110-
def make_to_one(Field: type) -> models.ForeignKey or models.OneToOneField:
116+
def make_to_one(Field: (models.ForeignKey or models.OneToOneField)) -> models.ForeignKey or models.OneToOneField:
111117
"""Setup a field of the given 'Field' type which can be either
112118
ForeignKey (many-to-one) or OneToOneField.
113119
"""
@@ -259,7 +265,7 @@ def make_args(cls, fld: Field):
259265

260266
# Build the table information as Django models
261267
# See .models.py
262-
def build_models(module : str, datamodel: Datamodel):
268+
def build_models(module : str, datamodel: Datamodel) -> Dict[int, models.Model]:
263269
return { model.specify_model.tableId: model
264270
for table in datamodel.tables
265271
for model in [ make_model(module, table, datamodel) ]}

specifyweb/specify/deletion_rules.py

+29-40
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22

33
""" Special Deletion Rules for table relationships
44
5-
Of the form: base_table : { field_name: action}
5+
Of the form: 'relatedTable.relName' : action,
6+
where relName is the field name of the relationship in relatedTable
7+
8+
For example, 'Accessionagent.accession' : models.CASCADE
9+
would delete the associated Accession Agent when Accession is deleted
10+
11+
The field and relationship names can be viewed at
12+
https://files.specifysoftware.org/schema/version/2.10/
613
714
Possible Django actions (w/ django 2.2) are:
815
CASCADE, PROTECT, SET_NULL, SET_DEFAULT, SET(...), DO_NOTHING
@@ -13,49 +20,31 @@
1320
See .build_models.py for uses
1421
"""
1522
SPECIAL_DELETION_RULES = {
16-
'Spappresource' : {'spreport': models.CASCADE},
17-
18-
'Recordset' : {'recordsetitem' : models.CASCADE},
19-
20-
'Specifyuser' : {
21-
'agent' : models.SET_NULL,
22-
'spAppResourceDir' : models.CASCADE,
23-
'spAppResource': models.CASCADE,
24-
'spPrincipal': models.CASCADE,
25-
},
26-
27-
# Handle workbench deletion using raw sql in business rules.
28-
'Workbench' : {'workbenchrow': models.DO_NOTHING},
29-
'Workbenchrow' : {
30-
'workbenchdataitem' : models.DO_NOTHING,
31-
'workbenchrowimage' : models.DO_NOTHING,
32-
'workbenchrowexportedrelationship' : models.DO_NOTHING,
33-
},
34-
35-
# System Tables
36-
37-
'Spauditlog' : {'field' : models.CASCADE},
38-
39-
'Spappresource' : {'spAppResourceData': models.CASCADE},
40-
'Spappresourcedir' : {
41-
'spPersistedAppResource': models.CASCADE,
42-
'spPersistedViewSet' : models.CASCADE,
43-
},
44-
'Spviewsetobj' : {'spAppResourceData' : models.CASCADE},
45-
46-
'Splocalecontainer' : {
47-
'item' : models.CASCADE,
48-
'name' : models.CASCADE
49-
},
50-
'Splocalecontaineritem' : {
51-
'name' : models.CASCADE
52-
},
53-
'SpQuery' : {"field" : models.CASCADE},
23+
'Agent.specifyuser' : models.SET_NULL,
24+
25+
# Handle workbench deletion using raw sql in business rules.
26+
'Workbenchrow.workbench': models.DO_NOTHING,
27+
'Workbenchdataitem.workbenchrow': models.DO_NOTHING,
28+
'Workbenchrowimage.workbenchrow': models.DO_NOTHING,
29+
'Workbenchrowexportedrelationship.workbenchrow': models.DO_NOTHING,
30+
31+
# These fields are not marked as dependent because the relationship
32+
# can be null
33+
'Spappresourcedir.specifyuser': models.CASCADE,
34+
'Spappresource.specifyuser': models.CASCADE,
35+
'Spappresourcedata.spappresource': models.CASCADE,
36+
'Spappresourcedata.spviewsetobj': models.CASCADE,
37+
38+
# In addition to these rules, Specify Cascades relationships tagged as dependent
39+
# For the complete list, see the global variable called
40+
# dependent_fields in .load_datamodel.py
41+
42+
# If the field/relationship is not dependent and not defined here, then it is
43+
# protected
5444
}
5545

5646
""" Any additional desired delete blockers
5747
Of the form 'base_table': ['field_1_name', 'field_2_name', ...]
58-
Use the django attributes from the 'base_table' for field names
5948
6049
See .build_models.py and .views.py for uses
6150
"""

specifyweb/specify/load_datamodel.py

+4
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,15 @@ def flag_system_tables(datamodel: Datamodel) -> None:
342342
'Preparation.preparationattrs',
343343
'Preparation.preparationproperties',
344344
'Preptype.attributedefs',
345+
'Recordset.recordsetitems',
345346
'Referencework.authors',
346347
'Repositoryagreement.addressofrecord',
347348
'Repositoryagreement.repositoryagreementagents',
348349
'Repositoryagreement.repositoryagreementauthorizations',
350+
'Spappresource.spAppResourceDir',
351+
'Spauditlog.fields',
349352
'Spquery.fields',
353+
'Spreport.appResource',
350354
'Taxon.commonnames',
351355
'Taxon.taxoncitations',
352356
'Taxon.taxonattribute',

specifyweb/specify/models.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Sets up Django ORM with the Specify datamodel
33
"""
44
from typing import Dict
5-
from django.db.models.base import ModelBase
5+
from django.db.models import Model
66

77
from .build_models import build_models
88
from .check_versions import check_versions
@@ -11,7 +11,7 @@
1111
# Returns a dictonary with the table's TableId as keys and the reated django models as values
1212
# The values (class paths) are constructed with this Module's name followed by the table name
1313
# Example: {7: <class 'specifyweb.specify.models.Accession'>}
14-
models_by_tableid : Dict[int, ModelBase] = build_models(__name__, datamodel)
14+
models_by_tableid : Dict[int, Model] = build_models(__name__, datamodel)
1515

1616
# inject the model definitions into this module's namespace
1717
globals().update((model.__name__, model)

0 commit comments

Comments
 (0)