From af05c4fca22ffd99ca7bc1643aa4ced31d2791f0 Mon Sep 17 00:00:00 2001 From: realVinayak Date: Mon, 22 Jul 2024 08:25:05 -0500 Subject: [PATCH 1/2] Redo timestamp modified/created setting --- specifyweb/attachment_gw/models.py | 5 - specifyweb/specify/build_models.py | 35 +-- specifyweb/specify/model_extras.py | 6 +- specifyweb/specify/model_timestamp.py | 53 +--- specifyweb/specify/models.py | 360 +++++++++++++------------- specifyweb/specify/tree_extras.py | 2 +- specifyweb/workbench/models.py | 2 - 7 files changed, 199 insertions(+), 264 deletions(-) diff --git a/specifyweb/attachment_gw/models.py b/specifyweb/attachment_gw/models.py index f59cfd98d81..6d5573b4f4d 100644 --- a/specifyweb/attachment_gw/models.py +++ b/specifyweb/attachment_gw/models.py @@ -1,8 +1,4 @@ from django.db import models -from django.conf import settings -from django.db.models.deletion import CASCADE, SET_NULL -from django.utils import timezone -from model_utils import FieldTracker from functools import partialmethod from specifyweb.specify.models import datamodel, custom_save from ..workbench.models import Dataset @@ -15,7 +11,6 @@ class Spattachmentdataset(Dataset): class Meta: db_table = 'attachmentdataset' - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) save = partialmethod(custom_save) # from django.apps import apps diff --git a/specifyweb/specify/build_models.py b/specifyweb/specify/build_models.py index 2200a9dc9b0..60c5aff7f78 100644 --- a/specifyweb/specify/build_models.py +++ b/specifyweb/specify/build_models.py @@ -1,12 +1,9 @@ from django.db import models from django.db.models.signals import pre_delete -from model_utils import FieldTracker -from requests import get - from specifyweb.businessrules.exceptions import AbortSave from . import model_extras -from .model_timestamp import pre_save_auto_timestamp_field_with_override +from .model_timestamp import pre_save_auto_timestamp_field_with_override, timestamp_fields appname = __name__.split('.')[-2] @@ -62,8 +59,12 @@ class Meta: if 'rankid' in attrs: ordering += ('rankid', ) + has_timestamp_fields = any(field.lower() in timestamp_fields for field in table.fields) + def save(self, *args, **kwargs): try: + if has_timestamp_fields: + pre_save_auto_timestamp_field_with_override(self) return super(model, self).save(*args, **kwargs) except AbortSave: return @@ -76,40 +77,16 @@ def pre_constraints_delete(self): # This is not currently used, but is here for future use. pre_delete.send(sender=self.__class__, instance=self) - def save_timestamped(self, *args, **kwargs): - timestamp_override = kwargs.pop('timestamp_override', False) - pre_save_auto_timestamp_field_with_override(self, timestamp_override) - try: - super(model, self).save(*args, **kwargs) - except AbortSave: - return - - field_names = [field.name.lower() for field in table.fields] - timestamp_fields = ['timestampcreated', 'timestampmodified'] - has_timestamp_fields = any(field in field_names for field in timestamp_fields) - - if has_timestamp_fields: - tracked_fields = [field for field in timestamp_fields if field in field_names] - attrs['timestamptracker'] = FieldTracker(fields=tracked_fields) - for field in tracked_fields: - attrs[field] = models.DateTimeField(db_column=field) # default=timezone.now is handled in pre_save_auto_timestamp_field_with_override - attrs['Meta'] = Meta if table.django_name in tables_with_pre_constraints_delete: # This is not currently used, but is here for future use. attrs['pre_constraints_delete'] = pre_constraints_delete - if has_timestamp_fields: - attrs['save'] = save_timestamped - else: - attrs['save'] = save + attrs['save'] = save supercls = models.Model if hasattr(model_extras, table.django_name): supercls = getattr(model_extras, table.django_name) - elif has_timestamp_fields: - # FUTURE: supercls = SpTimestampedModel - pass model = type(table.django_name, (supercls,), attrs) return model diff --git a/specifyweb/specify/model_extras.py b/specifyweb/specify/model_extras.py index 0f3bea3cf96..017e7f1462d 100644 --- a/specifyweb/specify/model_extras.py +++ b/specifyweb/specify/model_extras.py @@ -5,7 +5,7 @@ from django.conf import settings from django.utils import timezone -from .model_timestamp import SpTimestampedModel, pre_save_auto_timestamp_field_with_override +from .model_timestamp import pre_save_auto_timestamp_field_with_override from .tree_extras import Tree, TreeRank if settings.AUTH_LDAP_SERVER_URI is not None: @@ -20,7 +20,7 @@ def create_user(self, name, password=None): def create_superuser(self, name, password=None): raise NotImplementedError() -class Specifyuser(models.Model): # FUTURE: class Specifyuser(SpTimestampedModel): +class Specifyuser(models.Model): USERNAME_FIELD = 'name' REQUIRED_FIELDS = [] is_active = True @@ -125,7 +125,7 @@ class Meta: -class Preparation(models.Model): # FUTURE: class Preparation(SpTimestampedModel): +class Preparation(models.Model): def isonloan(self): # TODO: needs unit tests from django.db import connection diff --git a/specifyweb/specify/model_timestamp.py b/specifyweb/specify/model_timestamp.py index 1acf5d0dbd7..55d064dfd24 100644 --- a/specifyweb/specify/model_timestamp.py +++ b/specifyweb/specify/model_timestamp.py @@ -2,50 +2,17 @@ from django.utils import timezone from django.conf import settings -from model_utils import FieldTracker -def pre_save_auto_timestamp_field_with_override(obj, timestamp_override=None): - # Normal behavior is to update the timestamps automatically when saving. - # If timestampcreated or timestampmodified have been edited, don't update them to the current time. - cur_time = timezone.now() - timestamp_override = ( - timestamp_override - if timestamp_override is not None - else getattr(settings, "TIMESTAMP_SAVE_OVERRIDE", False) - ) - timestamp_fields = ['timestampcreated', 'timestampmodified'] - for field in timestamp_fields: - if hasattr(obj, field) and hasattr(obj, 'timestamptracker'): - if not timestamp_override and field not in obj.timestamptracker.changed() and \ - (not obj.id or not getattr(obj, field)): - setattr(obj, field, cur_time) - elif timestamp_override and not getattr(obj, field): - setattr(obj, field, cur_time) - - avoid_null_timestamp_fields(obj) - -def avoid_null_timestamp_fields(obj): - cur_time = timezone.now() - if hasattr(obj, 'timestampcreated') and getattr(obj, 'timestampcreated') is None: - obj.timestampcreated = cur_time - if hasattr(obj, 'timestampmodified') and getattr(obj, 'timestampmodified') is None: - obj.timestampmodified = cur_time -# NOTE: This class is needed for when we get rid of dynamic model creation from Specify 6 datamodel.xml file. -# NOTE: Currently in sperate file to avoid circular import. -class SpTimestampedModel(models.Model): - """ - SpTimestampedModel(id, timestampcreated, timestampmodified) - """ +timestamp_fields = {'timestampmodified', 'timestampcreated'} - timestampcreated = models.DateTimeField(db_column='TimestampCreated', default=timezone.now) - timestampmodified = models.DateTimeField(db_column='TimestampModified', default=timezone.now) +def pre_save_auto_timestamp_field_with_override(obj): + # If object already is present, reset timestamps to null. - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) - - class Meta: - abstract = True - - def save(self, *args, **kwargs): - pre_save_auto_timestamp_field_with_override(self) - super().save(*args, **kwargs) + if obj.id is None: + return + + for field in timestamp_fields: + if not hasattr(obj, field): + continue + setattr(obj, field, None) \ No newline at end of file diff --git a/specifyweb/specify/models.py b/specifyweb/specify/models.py index 78355910e32..85a84322848 100644 --- a/specifyweb/specify/models.py +++ b/specifyweb/specify/models.py @@ -1,7 +1,6 @@ from functools import partialmethod from django.db import models from django.utils import timezone -from model_utils import FieldTracker from specifyweb.businessrules.exceptions import AbortSave from specifyweb.specify.model_timestamp import pre_save_auto_timestamp_field_with_override from specifyweb.specify import model_extras @@ -77,7 +76,7 @@ class Meta: # models.Index(fields=['DateAccessioned'], name='AccessionDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Accessionagent(models.Model): @@ -104,7 +103,7 @@ class Meta: db_table = 'accessionagent' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Accessionattachment(models.Model): @@ -130,7 +129,7 @@ class Meta: db_table = 'accessionattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Accessionauthorization(models.Model): @@ -156,7 +155,7 @@ class Meta: db_table = 'accessionauthorization' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Accessioncitation(models.Model): @@ -185,7 +184,7 @@ class Meta: db_table = 'accessioncitation' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Address(models.Model): @@ -230,7 +229,7 @@ class Meta: db_table = 'address' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Addressofrecord(models.Model): @@ -260,7 +259,7 @@ class Meta: db_table = 'addressofrecord' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Agent(models.Model): @@ -328,7 +327,7 @@ class Meta: # models.Index(fields=['Abbreviation'], name='AbbreviationIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Agentattachment(models.Model): @@ -354,7 +353,7 @@ class Meta: db_table = 'agentattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Agentgeography(models.Model): @@ -380,7 +379,7 @@ class Meta: db_table = 'agentgeography' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Agentidentifier(models.Model): @@ -420,7 +419,7 @@ class Meta: db_table = 'agentidentifier' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Agentspecialty(models.Model): @@ -445,7 +444,7 @@ class Meta: db_table = 'agentspecialty' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Agentvariant(models.Model): @@ -473,7 +472,7 @@ class Meta: db_table = 'agentvariant' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Appraisal(models.Model): @@ -506,7 +505,7 @@ class Meta: # models.Index(fields=['AppraisalDate'], name='AppraisalDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Attachment(models.Model): @@ -562,7 +561,7 @@ class Meta: # models.Index(fields=['GUID'], name='AttchmentGuidIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Attachmentimageattribute(models.Model): @@ -602,7 +601,7 @@ class Meta: db_table = 'attachmentimageattribute' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Attachmentmetadata(models.Model): @@ -627,7 +626,7 @@ class Meta: db_table = 'attachmentmetadata' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Attachmenttag(models.Model): @@ -651,7 +650,7 @@ class Meta: db_table = 'attachmenttag' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Attributedef(models.Model): @@ -678,7 +677,7 @@ class Meta: db_table = 'attributedef' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Author(models.Model): @@ -704,7 +703,7 @@ class Meta: db_table = 'author' ordering = ('ordernumber',) - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Autonumberingscheme(models.Model): @@ -734,7 +733,7 @@ class Meta: # models.Index(fields=['SchemeName'], name='SchemeNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Borrow(models.Model): @@ -781,7 +780,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='BorColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Borrowagent(models.Model): @@ -811,7 +810,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='BorColMemIDX2') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Borrowattachment(models.Model): @@ -837,7 +836,7 @@ class Meta: db_table = 'borrowattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Borrowmaterial(models.Model): @@ -875,7 +874,7 @@ class Meta: # models.Index(fields=['Description'], name='DescriptionIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Borrowreturnmaterial(models.Model): @@ -907,7 +906,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='BorrowReturnedColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingevent(models.Model): @@ -976,7 +975,7 @@ class Meta: # models.Index(fields=['GUID'], name='CEGuidIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingeventattachment(models.Model): @@ -1006,7 +1005,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='CEAColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingeventattr(models.Model): @@ -1036,7 +1035,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='COLEVATColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingeventattribute(models.Model): @@ -1109,7 +1108,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='COLEVATSDispIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingeventauthorization(models.Model): @@ -1134,7 +1133,7 @@ class Meta: db_table = 'collectingeventauthorization' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingtrip(models.Model): @@ -1195,7 +1194,7 @@ class Meta: # models.Index(fields=['StartDate'], name='COLTRPStartDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingtripattachment(models.Model): @@ -1225,7 +1224,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='CTAColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingtripattribute(models.Model): @@ -1297,7 +1296,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='COLTRPSDispIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectingtripauthorization(models.Model): @@ -1322,7 +1321,7 @@ class Meta: db_table = 'collectingtripauthorization' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collection(models.Model): @@ -1372,7 +1371,7 @@ class Meta: # models.Index(fields=['GUID'], name='CollectionGuidIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionobject(models.Model): @@ -1474,7 +1473,7 @@ class Meta: # models.Index(fields=['CollectionmemberID'], name='COColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionobjectattachment(models.Model): @@ -1504,7 +1503,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='COLOBJATTColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionobjectattr(models.Model): @@ -1534,7 +1533,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='COLOBJATRSColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionobjectattribute(models.Model): @@ -1681,7 +1680,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='COLOBJATTRSColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionobjectcitation(models.Model): @@ -1714,7 +1713,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='COCITColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionobjectproperty(models.Model): @@ -1903,7 +1902,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='COLOBJPROPColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionreltype(models.Model): @@ -1929,7 +1928,7 @@ class Meta: db_table = 'collectionreltype' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collectionrelationship(models.Model): @@ -1956,7 +1955,7 @@ class Meta: db_table = 'collectionrelationship' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Collector(models.Model): @@ -1991,7 +1990,7 @@ class Meta: # models.Index(fields=['DivisionID'], name='COLTRDivIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Commonnametx(models.Model): @@ -2023,7 +2022,7 @@ class Meta: # models.Index(fields=['Country'], name='CommonNameTxCountryIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Commonnametxcitation(models.Model): @@ -2058,7 +2057,7 @@ class Meta: db_table = 'commonnametxcitation' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Conservdescription(models.Model): @@ -2130,7 +2129,7 @@ class Meta: # models.Index(fields=['ShortDesc'], name='ConservDescShortDescIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Conservdescriptionattachment(models.Model): @@ -2156,7 +2155,7 @@ class Meta: db_table = 'conservdescriptionattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Conservevent(models.Model): @@ -2207,7 +2206,7 @@ class Meta: # models.Index(fields=['completedDate'], name='ConservCompletedDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Conserveventattachment(models.Model): @@ -2233,7 +2232,7 @@ class Meta: db_table = 'conserveventattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Container(models.Model): @@ -2266,7 +2265,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='ContainerMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Dnaprimer(models.Model): @@ -2316,7 +2315,7 @@ class Meta: # models.Index(fields=['PrimerDesignator'], name='DesignatorIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Dnasequence(models.Model): @@ -2376,7 +2375,7 @@ class Meta: # models.Index(fields=['BOLDSampleID'], name='BOLDSampleIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Dnasequenceattachment(models.Model): @@ -2402,7 +2401,7 @@ class Meta: db_table = 'dnasequenceattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Dnasequencingrun(models.Model): @@ -2458,7 +2457,7 @@ class Meta: db_table = 'dnasequencingrun' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Dnasequencingrunattachment(models.Model): @@ -2484,7 +2483,7 @@ class Meta: db_table = 'dnasequencerunattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Dnasequencingruncitation(models.Model): @@ -2519,7 +2518,7 @@ class Meta: db_table = 'dnasequencingruncitation' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Datatype(models.Model): @@ -2542,7 +2541,7 @@ class Meta: db_table = 'datatype' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Deaccession(models.Model): @@ -2597,7 +2596,7 @@ class Meta: # models.Index(fields=['DeaccessionDate'], name='DeaccessionDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Deaccessionagent(models.Model): @@ -2623,7 +2622,7 @@ class Meta: db_table = 'deaccessionagent' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Deaccessionattachment(models.Model): @@ -2649,7 +2648,7 @@ class Meta: db_table = 'deaccessionattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Determination(models.Model): @@ -2721,7 +2720,7 @@ class Meta: # models.Index(fields=['TypeStatusName'], name='TypeStatusNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Determinationcitation(models.Model): @@ -2754,7 +2753,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='DetCitColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Determiner(models.Model): @@ -2785,7 +2784,7 @@ class Meta: db_table = 'determiner' ordering = ('ordernumber',) - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Discipline(models.Model): @@ -2823,7 +2822,7 @@ class Meta: # models.Index(fields=['Name'], name='DisciplineNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Disposal(models.Model): @@ -2861,7 +2860,7 @@ class Meta: # models.Index(fields=['DisposalDate'], name='DisposalDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Disposalagent(models.Model): @@ -2887,7 +2886,7 @@ class Meta: db_table = 'disposalagent' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Disposalattachment(models.Model): @@ -2913,7 +2912,7 @@ class Meta: db_table = 'disposalattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Disposalpreparation(models.Model): @@ -2940,7 +2939,7 @@ class Meta: db_table = 'disposalpreparation' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Division(models.Model): @@ -2976,7 +2975,7 @@ class Meta: # models.Index(fields=['Name'], name='DivisionNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exchangein(models.Model): @@ -3020,7 +3019,7 @@ class Meta: # models.Index(fields=['DescriptionOfMaterial'], name='DescriptionOfMaterialIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exchangeinattachment(models.Model): @@ -3046,7 +3045,7 @@ class Meta: db_table = 'exchangeinattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exchangeinprep(models.Model): @@ -3080,7 +3079,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='ExchgInPrepDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exchangeout(models.Model): @@ -3126,7 +3125,7 @@ class Meta: # models.Index(fields=['ExchangeOutNumber'], name='ExchangeOutNumberIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exchangeoutattachment(models.Model): @@ -3152,7 +3151,7 @@ class Meta: db_table = 'exchangeoutattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exchangeoutprep(models.Model): @@ -3186,7 +3185,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='ExchgOutPrepDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exsiccata(models.Model): @@ -3212,7 +3211,7 @@ class Meta: db_table = 'exsiccata' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Exsiccataitem(models.Model): @@ -3238,7 +3237,7 @@ class Meta: db_table = 'exsiccataitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Extractor(models.Model): @@ -3268,7 +3267,7 @@ class Meta: db_table = 'extractor' ordering = ('ordernumber',) - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fieldnotebook(models.Model): @@ -3303,7 +3302,7 @@ class Meta: # models.Index(fields=['EndDate'], name='FNBEndDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fieldnotebookattachment(models.Model): @@ -3329,7 +3328,7 @@ class Meta: db_table = 'fieldnotebookattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fieldnotebookpage(models.Model): @@ -3360,7 +3359,7 @@ class Meta: # models.Index(fields=['ScanDate'], name='FNBPScanDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fieldnotebookpageattachment(models.Model): @@ -3386,7 +3385,7 @@ class Meta: db_table = 'fieldnotebookpageattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fieldnotebookpageset(models.Model): @@ -3420,7 +3419,7 @@ class Meta: # models.Index(fields=['EndDate'], name='FNBPSEndDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fieldnotebookpagesetattachment(models.Model): @@ -3446,7 +3445,7 @@ class Meta: db_table = 'fieldnotebookpagesetattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Fundingagent(models.Model): @@ -3478,7 +3477,7 @@ class Meta: # models.Index(fields=['DivisionID'], name='COLTRIPDivIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geocoorddetail(models.Model): @@ -3540,7 +3539,7 @@ class Meta: db_table = 'geocoorddetail' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geography(model_extras.Geography): @@ -3590,7 +3589,7 @@ class Meta: # models.Index(fields=['FullName'], name='GeoFullNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geographytreedef(models.Model): @@ -3615,7 +3614,7 @@ class Meta: db_table = 'geographytreedef' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geographytreedefitem(model_extras.Geographytreedefitem): @@ -3648,7 +3647,7 @@ class Meta: db_table = 'geographytreedefitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geologictimeperiod(model_extras.Geologictimeperiod): @@ -3695,7 +3694,7 @@ class Meta: # models.Index(fields=['GUID'], name='GTPGuidIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geologictimeperiodtreedef(models.Model): @@ -3720,7 +3719,7 @@ class Meta: db_table = 'geologictimeperiodtreedef' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Geologictimeperiodtreedefitem(model_extras.Geologictimeperiodtreedefitem): @@ -3753,7 +3752,7 @@ class Meta: db_table = 'geologictimeperiodtreedefitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Gift(models.Model): @@ -3809,7 +3808,7 @@ class Meta: # models.Index(fields=['GiftDate'], name='GiftDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Giftagent(models.Model): @@ -3840,7 +3839,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='GiftAgDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Giftattachment(models.Model): @@ -3866,7 +3865,7 @@ class Meta: db_table = 'giftattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Giftpreparation(models.Model): @@ -3904,7 +3903,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='GiftPrepDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Groupperson(models.Model): @@ -3931,7 +3930,7 @@ class Meta: db_table = 'groupperson' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Inforequest(models.Model): @@ -3966,7 +3965,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='IRColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Institution(models.Model): @@ -4020,7 +4019,7 @@ class Meta: # models.Index(fields=['GUID'], name='InstGuidIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Institutionnetwork(models.Model): @@ -4058,7 +4057,7 @@ class Meta: # models.Index(fields=['Name'], name='InstNetworkNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Journal(models.Model): @@ -4091,7 +4090,7 @@ class Meta: # models.Index(fields=['GUID'], name='JournalGUIDIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Latlonpolygon(models.Model): @@ -4118,7 +4117,7 @@ class Meta: db_table = 'latlonpolygon' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Latlonpolygonpnt(models.Model): @@ -4184,7 +4183,7 @@ class Meta: # models.Index(fields=['GUID'], name='LithoGuidIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Lithostrattreedef(models.Model): @@ -4209,7 +4208,7 @@ class Meta: db_table = 'lithostrattreedef' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Lithostrattreedefitem(model_extras.Lithostrattreedefitem): @@ -4242,7 +4241,7 @@ class Meta: db_table = 'lithostrattreedefitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Loan(models.Model): @@ -4301,7 +4300,7 @@ class Meta: # models.Index(fields=['CurrentDueDate'], name='CurrentDueDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Loanagent(models.Model): @@ -4331,7 +4330,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='LoanAgDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Loanattachment(models.Model): @@ -4357,7 +4356,7 @@ class Meta: db_table = 'loanattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Loanpreparation(models.Model): @@ -4398,7 +4397,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='LoanPrepDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Loanreturnpreparation(models.Model): @@ -4431,7 +4430,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='LoanRetPrepDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Locality(models.Model): @@ -4506,7 +4505,7 @@ class Meta: # models.Index(fields=['RelationToNamedPlace'], name='RelationToNamedPlaceIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Localityattachment(models.Model): @@ -4532,7 +4531,7 @@ class Meta: db_table = 'localityattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Localitycitation(models.Model): @@ -4565,7 +4564,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='LocCitDspMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Localitydetail(models.Model): @@ -4635,7 +4634,7 @@ class Meta: db_table = 'localitydetail' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Localitynamealias(models.Model): @@ -4664,7 +4663,7 @@ class Meta: # models.Index(fields=['Name'], name='LocalityNameAliasIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Materialsample(models.Model): @@ -4731,7 +4730,7 @@ class Meta: # models.Index(fields=['GGBNSampleDesignation'], name='DesignationIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Morphbankview(models.Model): @@ -4762,7 +4761,7 @@ class Meta: db_table = 'morphbankview' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Otheridentifier(models.Model): @@ -4808,7 +4807,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='OthIdColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Paleocontext(models.Model): @@ -4856,7 +4855,7 @@ class Meta: # models.Index(fields=['DisciplineID'], name='PaleoCxtDisciplineIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Pcrperson(models.Model): @@ -4886,7 +4885,7 @@ class Meta: db_table = 'pcrperson' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Permit(models.Model): @@ -4938,7 +4937,7 @@ class Meta: # models.Index(fields=['IssuedDate'], name='IssuedDateIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Permitattachment(models.Model): @@ -4964,7 +4963,7 @@ class Meta: db_table = 'permitattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Picklist(models.Model): @@ -5001,7 +5000,7 @@ class Meta: # models.Index(fields=['Name'], name='PickListNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Picklistitem(models.Model): @@ -5027,7 +5026,7 @@ class Meta: db_table = 'picklistitem' ordering = ('ordinal',) - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Preptype(models.Model): @@ -5052,7 +5051,7 @@ class Meta: db_table = 'preptype' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Preparation(model_extras.Preparation): @@ -5128,7 +5127,7 @@ class Meta: # models.Index(fields=['BarCode'], name='PrepBarCodeIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Preparationattachment(models.Model): @@ -5158,7 +5157,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='PrepAttColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Preparationattr(models.Model): @@ -5188,7 +5187,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='PrepAttrColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Preparationattribute(models.Model): @@ -5255,7 +5254,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='PrepAttrsColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Preparationproperty(models.Model): @@ -5444,7 +5443,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='PREPPROPColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Project(models.Model): @@ -5487,7 +5486,7 @@ class Meta: # models.Index(fields=['ProjectNumber'], name='ProjectNumberIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Recordset(models.Model): @@ -5523,7 +5522,7 @@ class Meta: # models.Index(fields=['name'], name='RecordSetNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Recordsetitem(models.Model): @@ -5594,7 +5593,7 @@ class Meta: # models.Index(fields=['ISBN'], name='ISBNIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Referenceworkattachment(models.Model): @@ -5620,7 +5619,7 @@ class Meta: db_table = 'referenceworkattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Repositoryagreement(models.Model): @@ -5662,7 +5661,7 @@ class Meta: # models.Index(fields=['StartDate'], name='RefWrkStartDate') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Repositoryagreementattachment(models.Model): @@ -5688,7 +5687,7 @@ class Meta: db_table = 'repositoryagreementattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Shipment(models.Model): @@ -5737,7 +5736,7 @@ class Meta: # models.Index(fields=['ShipmentMethod'], name='ShipmentMethodIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spappresource(models.Model): @@ -5772,8 +5771,7 @@ class Meta: # models.Index(fields=['Name'], name='SpAppResNameIDX'), # models.Index(fields=['MimeType'], name='SpAppResMimeTypeIDX') ] - - timestamptracker = FieldTracker(fields=['timestampcreated']) + save = partialmethod(custom_save) class Spappresourcedata(models.Model): @@ -5827,7 +5825,7 @@ def save_spappresourcedata(self, *args, **kwargs): # else: # self._data = value - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(save_spappresourcedata) class Spappresourcedir(models.Model): @@ -5858,7 +5856,7 @@ class Meta: # models.Index(fields=['DisciplineType'], name='SpAppResourceDirDispTypeIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spauditlog(models.Model): @@ -5891,7 +5889,7 @@ def save_spauditlog(self, *args, **kwargs): self.recordversion = 0 # or some other default value custom_save(self, *args, **kwargs) - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(save_spauditlog) class Spauditlogfield(models.Model): @@ -5917,7 +5915,7 @@ class Meta: db_table = 'spauditlogfield' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spexportschema(models.Model): @@ -5943,7 +5941,7 @@ class Meta: db_table = 'spexportschema' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spexportschemaitem(models.Model): @@ -5971,7 +5969,7 @@ class Meta: db_table = 'spexportschemaitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spexportschemaitemmapping(models.Model): @@ -6000,7 +5998,7 @@ class Meta: db_table = 'spexportschemaitemmapping' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spexportschemamapping(models.Model): @@ -6029,7 +6027,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='SPEXPSCHMMAPColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spfieldvaluedefault(models.Model): @@ -6059,7 +6057,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='SpFieldValueDefaultColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Splocalecontainer(models.Model): @@ -6095,7 +6093,7 @@ class Meta: # models.Index(fields=['Name'], name='SpLocaleContainerNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Splocalecontaineritem(models.Model): @@ -6130,7 +6128,7 @@ class Meta: # models.Index(fields=['Name'], name='SpLocaleContainerItemNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Splocaleitemstr(models.Model): @@ -6164,7 +6162,7 @@ class Meta: # models.Index(fields=['Country'], name='SpLocaleCountyIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Sppermission(models.Model): @@ -6210,7 +6208,7 @@ class Meta: db_table = 'spprincipal' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spquery(models.Model): @@ -6248,7 +6246,7 @@ class Meta: # models.Index(fields=['Name'], name='SpQueryNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spqueryfield(models.Model): @@ -6289,7 +6287,7 @@ class Meta: db_table = 'spqueryfield' ordering = ('position',) - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spreport(models.Model): @@ -6324,7 +6322,7 @@ class Meta: # models.Index(fields=['Name'], name='SpReportNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spsymbiotainstance(models.Model): @@ -6358,7 +6356,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='SPSYMINSTColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Sptasksemaphore(models.Model): @@ -6390,7 +6388,7 @@ class Meta: db_table = 'sptasksemaphore' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spversion(models.Model): @@ -6418,7 +6416,7 @@ class Meta: db_table = 'spversion' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spviewsetobj(models.Model): @@ -6449,7 +6447,7 @@ class Meta: # models.Index(fields=['Name'], name='SpViewObjNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Spvisualquery(models.Model): @@ -6477,7 +6475,7 @@ class Meta: # models.Index(fields=['Name'], name='SpVisualQueryNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Specifyuser(model_extras.Specifyuser): @@ -6509,7 +6507,7 @@ class Meta: db_table = 'specifyuser' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + # save = partialmethod(custom_save) class Storage(model_extras.Storage): @@ -6552,7 +6550,7 @@ class Meta: # models.Index(fields=['FullName'], name='StorFullNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Storageattachment(models.Model): @@ -6578,7 +6576,7 @@ class Meta: db_table = 'storageattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Storagetreedef(models.Model): @@ -6603,7 +6601,7 @@ class Meta: db_table = 'storagetreedef' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Storagetreedefitem(model_extras.Storagetreedefitem): @@ -6636,7 +6634,7 @@ class Meta: db_table = 'storagetreedefitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Taxon(model_extras.Taxon): @@ -6756,7 +6754,7 @@ class Meta: # models.Index(fields=['EnvironmentalProtectionStatus'], name='EnvironmentalProtectionStatusIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Taxonattachment(models.Model): @@ -6782,7 +6780,7 @@ class Meta: db_table = 'taxonattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Taxonattribute(models.Model): @@ -6968,7 +6966,7 @@ class Meta: db_table = 'taxonattribute' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Taxoncitation(models.Model): @@ -7003,7 +7001,7 @@ class Meta: db_table = 'taxoncitation' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Taxontreedef(models.Model): @@ -7030,7 +7028,7 @@ class Meta: db_table = 'taxontreedef' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Taxontreedefitem(model_extras.Taxontreedefitem): @@ -7064,7 +7062,7 @@ class Meta: db_table = 'taxontreedefitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Treatmentevent(models.Model): @@ -7122,7 +7120,7 @@ class Meta: # models.Index(fields=['TreatmentNumber'], name='TETreatmentNumberIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Treatmenteventattachment(models.Model): @@ -7148,7 +7146,7 @@ class Meta: db_table = 'treatmenteventattachment' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Voucherrelationship(models.Model): @@ -7192,7 +7190,7 @@ class Meta: # models.Index(fields=['CollectionMemberID'], name='VRXDATColMemIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Workbench(models.Model): @@ -7231,7 +7229,7 @@ class Meta: # models.Index(fields=['name'], name='WorkbenchNameIDX') ] - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Workbenchdataitem(models.Model): @@ -7344,7 +7342,7 @@ class Meta: db_table = 'workbenchrowexportedrelationship' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Workbenchrowimage(models.Model): @@ -7420,7 +7418,7 @@ class Meta: db_table = 'workbenchtemplate' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) class Workbenchtemplatemappingitem(models.Model): @@ -7460,5 +7458,5 @@ class Meta: db_table = 'workbenchtemplatemappingitem' ordering = () - timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) + save = partialmethod(custom_save) diff --git a/specifyweb/specify/tree_extras.py b/specifyweb/specify/tree_extras.py index 1658997eccc..f86db7de5e2 100644 --- a/specifyweb/specify/tree_extras.py +++ b/specifyweb/specify/tree_extras.py @@ -27,7 +27,7 @@ def validate_node_numbers(table, revalidate_after=True): if revalidate_after: validate_tree_numbering(table) -class Tree(models.Model): # FUTURE: class Tree(SpTimestampedModel): +class Tree(models.Model): class Meta: abstract = True diff --git a/specifyweb/workbench/models.py b/specifyweb/workbench/models.py index c055f0702aa..c74ea3f1f75 100644 --- a/specifyweb/workbench/models.py +++ b/specifyweb/workbench/models.py @@ -1,6 +1,5 @@ import json from functools import partialmethod -from model_utils import FieldTracker from django import http from django.core.exceptions import ObjectDoesNotExist @@ -100,7 +99,6 @@ class Spdataset(Dataset): class Meta: db_table = 'spdataset' - # timestamptracker = FieldTracker(fields=['timestampcreated', 'timestampmodified']) # save = partialmethod(custom_save) def get_dataset_as_dict(self): From ccd714ec88454a7d1a12ee3b9e70933d9459575a Mon Sep 17 00:00:00 2001 From: realVinayak Date: Mon, 22 Jul 2024 12:46:50 -0500 Subject: [PATCH 2/2] Use custom save logic --- specifyweb/specify/api.py | 26 +++----- specifyweb/specify/build_models.py | 8 +-- specifyweb/specify/model_extras.py | 5 +- specifyweb/specify/model_timestamp.py | 37 ++++++++--- specifyweb/specify/models.py | 5 +- specifyweb/specify/tests/test_api.py | 73 +-------------------- specifyweb/specify/tests/test_timestamps.py | 48 ++++++++++++++ specifyweb/specify/tree_extras.py | 5 +- 8 files changed, 93 insertions(+), 114 deletions(-) create mode 100644 specifyweb/specify/tests/test_timestamps.py diff --git a/specifyweb/specify/api.py b/specifyweb/specify/api.py index 4dfd4d4b88a..9008ddb4ba7 100644 --- a/specifyweb/specify/api.py +++ b/specifyweb/specify/api.py @@ -368,6 +368,10 @@ def set_field_if_exists(obj, field: str, value) -> None: if f.concrete: setattr(obj, field, value) +def _maybe_delete(data: Dict[str, Any], to_delete: str): + if to_delete in data: + del data[to_delete] + def cleanData(model, data: Dict[str, Any], agent) -> Dict[str, Any]: """Returns a copy of data with only fields that are part of model, removing metadata fields and warning on unexpected extra fields.""" @@ -400,30 +404,18 @@ def cleanData(model, data: Dict[str, Any], agent) -> Dict[str, Any]: if model is models.Agent: # setting user agents is part of the user management system. - try: - del cleaned['specifyuser'] - except KeyError: - pass + _maybe_delete(cleaned, 'specifyuser') # guid should only be updatable for taxon and geography if model not in (models.Taxon, models.Geography): - try: - del cleaned['guid'] - except KeyError: - pass + _maybe_delete(cleaned, 'guid') # timestampcreated should never be updated. - # ... well it is now ¯\_(ツ)_/¯ - # New requirments are for timestampcreated to be overridable. - try: - # del cleaned['timestampcreated'] - pass - except KeyError: - pass + # _maybe_delete(cleaned, 'timestampcreated') # Password should be set though the /api/set_password// endpoint - if model is models.Specifyuser and 'password' in cleaned: - del cleaned['password'] + if model is models.Specifyuser: + _maybe_delete(cleaned, 'password') return cleaned diff --git a/specifyweb/specify/build_models.py b/specifyweb/specify/build_models.py index 60c5aff7f78..0044585dec2 100644 --- a/specifyweb/specify/build_models.py +++ b/specifyweb/specify/build_models.py @@ -3,7 +3,7 @@ from specifyweb.businessrules.exceptions import AbortSave from . import model_extras -from .model_timestamp import pre_save_auto_timestamp_field_with_override, timestamp_fields +from .model_timestamp import save_auto_timestamp_field_with_override appname = __name__.split('.')[-2] @@ -59,13 +59,9 @@ class Meta: if 'rankid' in attrs: ordering += ('rankid', ) - has_timestamp_fields = any(field.lower() in timestamp_fields for field in table.fields) - def save(self, *args, **kwargs): try: - if has_timestamp_fields: - pre_save_auto_timestamp_field_with_override(self) - return super(model, self).save(*args, **kwargs) + return save_auto_timestamp_field_with_override(super(model, self).save, args, kwargs, self) except AbortSave: return diff --git a/specifyweb/specify/model_extras.py b/specifyweb/specify/model_extras.py index 017e7f1462d..3e505a70247 100644 --- a/specifyweb/specify/model_extras.py +++ b/specifyweb/specify/model_extras.py @@ -5,7 +5,7 @@ from django.conf import settings from django.utils import timezone -from .model_timestamp import pre_save_auto_timestamp_field_with_override +from .model_timestamp import save_auto_timestamp_field_with_override from .tree_extras import Tree, TreeRank if settings.AUTH_LDAP_SERVER_URI is not None: @@ -117,8 +117,7 @@ def save(self, *args, **kwargs): if self.id and self.usertype != 'Manager': self.clear_admin() - pre_save_auto_timestamp_field_with_override(self) - return super(Specifyuser, self).save(*args, **kwargs) + return save_auto_timestamp_field_with_override(super(Specifyuser, self).save, args, kwargs, self) class Meta: abstract = True diff --git a/specifyweb/specify/model_timestamp.py b/specifyweb/specify/model_timestamp.py index 55d064dfd24..0080174df18 100644 --- a/specifyweb/specify/model_timestamp.py +++ b/specifyweb/specify/model_timestamp.py @@ -1,18 +1,35 @@ -from django.db import models from django.utils import timezone -from django.conf import settings +from django.db.models import Model +timestamp_fields = [('timestampmodified', True), ('timestampcreated', False)] +fields_to_skip = [field[0] for field in timestamp_fields if not field[1]] -timestamp_fields = {'timestampmodified', 'timestampcreated'} - -def pre_save_auto_timestamp_field_with_override(obj): +def save_auto_timestamp_field_with_override(save_func, args, kwargs, obj): # If object already is present, reset timestamps to null. + model: Model = obj.__class__ + is_forced_insert = kwargs.get('force_insert', False) + fields_to_update = kwargs.get('update_fields', None) + if fields_to_update is None: + fields_to_update = [ + field.name for field in model._meta.get_fields(include_hidden=True) if field.concrete + and not field.primary_key + ] + + if obj.id is not None: + fields_to_update = [ + field for field in fields_to_update + if field not in fields_to_skip + ] + + current = timezone.now() + _set_if_empty(obj, timestamp_fields, current, obj.pk is not None) + new_kwargs = {**kwargs, 'update_fields': fields_to_update} if obj.pk is not None and not is_forced_insert else kwargs + return save_func(*args, **new_kwargs) - if obj.id is None: - return - - for field in timestamp_fields: +def _set_if_empty(obj, fields, default_value, override=False): + for field, can_override in fields: if not hasattr(obj, field): continue - setattr(obj, field, None) \ No newline at end of file + if (override and can_override) or getattr(obj, field) is None: + setattr(obj, field, default_value) \ No newline at end of file diff --git a/specifyweb/specify/models.py b/specifyweb/specify/models.py index 85a84322848..664407505f0 100644 --- a/specifyweb/specify/models.py +++ b/specifyweb/specify/models.py @@ -2,7 +2,7 @@ from django.db import models from django.utils import timezone from specifyweb.businessrules.exceptions import AbortSave -from specifyweb.specify.model_timestamp import pre_save_auto_timestamp_field_with_override +from specifyweb.specify.model_timestamp import save_auto_timestamp_field_with_override from specifyweb.specify import model_extras from .datamodel import datamodel import logging @@ -18,8 +18,7 @@ def protect_with_blockers(collector, field, sub_objs, using): def custom_save(self, *args, **kwargs): try: # Custom save logic here, if necessary - pre_save_auto_timestamp_field_with_override(self) - super(self.__class__, self).save(*args, **kwargs) + save_auto_timestamp_field_with_override(super(self.__class__, self).save, args, kwargs, self) except AbortSave as e: # Handle AbortSave exception as needed logger.error("Save operation aborted: %s", e) diff --git a/specifyweb/specify/tests/test_api.py b/specifyweb/specify/tests/test_api.py index b7568983f42..bdec47e896d 100644 --- a/specifyweb/specify/tests/test_api.py +++ b/specifyweb/specify/tests/test_api.py @@ -17,7 +17,6 @@ def get_table(name: str): return getattr(models, name.capitalize()) - class MainSetupTearDown: def setUp(self): disconnect_signal('pre_save', None, dispatch_uid=UNIQUENESS_DISPATCH_UID) @@ -94,6 +93,7 @@ class ApiTests(MainSetupTearDown, TestCase): pass skip_perms_check = lambda x: None class SimpleApiTests(ApiTests): + def test_get_collection(self): data = api.get_collection(self.collection, 'collectionobject', skip_perms_check) self.assertEqual(data['meta']['total_count'], len(self.collectionobjects)) @@ -132,77 +132,6 @@ def test_delete_object(self): api.delete_resource(self.collection, self.agent, 'collectionobject', obj.id, obj.version) self.assertEqual(models.Collectionobject.objects.filter(id=obj.id).count(), 0) - def test_timestamp_override_object(self): - manual_datetime_1 = datetime(1960, 1, 1, 0, 0, 0) - manual_datetime_2 = datetime(2020, 1, 1, 0, 0, 0) - cur_time = datetime.now() - - def timestamp_field_assert(obj, manual_datetime): - self.assertEqual(obj.timestampcreated, manual_datetime) - self.assertEqual(obj.timestampmodified, manual_datetime) - - # Test with api.create_obj - obj = api.create_obj(self.collection, self.agent, 'collectionobject', { - 'collection': api.uri_for_model('collection', self.collection.id), - 'catalognumber': 'foobar', - 'timestampcreated': manual_datetime_1, 'timestampmodified': manual_datetime_1}) - obj = models.Collectionobject.objects.get(id=obj.id) - timestamp_field_assert(obj, manual_datetime_1) - - # Test editing obj after creating with api.create_obj - data = api.get_resource('collection', self.collection.id, skip_perms_check) - data['timestampcreated'] = manual_datetime_2 - data['timestampmodified'] = manual_datetime_2 - api.update_obj(self.collection, self.agent, 'collection', - data['id'], data['version'], data) - obj = models.Collection.objects.get(id=self.collection.id) - timestamp_field_assert(obj, manual_datetime_2) - - # Test with direct object creation - CollectionObject = getattr(models, 'Collectionobject') - obj = CollectionObject( - timestampcreated=manual_datetime_1, - timestampmodified=manual_datetime_1, - collectionmemberid=1, - collection=self.collection) - obj.save() - timestamp_field_assert(obj, manual_datetime_1) - - # Test editing obj after creating with direct object creation - CollectionObject = getattr(models, 'Collectionobject') - obj = CollectionObject.objects.create( - timestampcreated=manual_datetime_2, - timestampmodified=manual_datetime_2, - collectionmemberid=1, - collection=self.collection) - obj.save() - timestamp_field_assert(obj, manual_datetime_2) - - # Test with objects.create - CollectionObject = getattr(models, 'Collectionobject') - obj = CollectionObject.objects.create( - timestampcreated=manual_datetime_1, - timestampmodified=manual_datetime_1, - collectionmemberid=1, - collection=self.collection) - obj.save() - timestamp_field_assert(obj, manual_datetime_1) - - # Test editing obj after creating with objects.create - obj.timestampcreated = manual_datetime_2 - obj.timestampmodified = manual_datetime_2 - obj.save() - timestamp_field_assert(obj, manual_datetime_2) - - # Test with current time - CollectionObject = getattr(models, 'Collectionobject') - obj = CollectionObject.objects.create( - collectionmemberid=1, - collection=self.collection) - obj.save() - self.assertGreaterEqual(obj.timestampcreated, cur_time) - self.assertGreaterEqual(obj.timestampmodified, cur_time) - class RecordSetTests(ApiTests): def setUp(self): super(RecordSetTests, self).setUp() diff --git a/specifyweb/specify/tests/test_timestamps.py b/specifyweb/specify/tests/test_timestamps.py new file mode 100644 index 00000000000..da5584af039 --- /dev/null +++ b/specifyweb/specify/tests/test_timestamps.py @@ -0,0 +1,48 @@ +""" +Tests for timestamps additional logic +""" +from datetime import datetime +from django.utils import timezone +from specifyweb.specify.tests.test_api import ApiTests, skip_perms_check +from specifyweb.specify import api +from specifyweb.specify.models import Collectionobject + +class TimeStampTests(ApiTests): + + def test_blank_timestamps(self): + cur_time = timezone.now() + + obj = Collectionobject.objects.create( + collectionmemberid=1, + collection=self.collection) + + self.assertGreaterEqual(obj.timestampcreated, cur_time) + self.assertGreaterEqual(obj.timestampmodified, cur_time) + + def test_can_override_new_timestamps_api(self): + datetime_1 = datetime(1960, 1, 1, 0, 0, 0) + datetime_2 = datetime(2020, 1, 1, 0, 0, 0) + + obj = api.create_obj(self.collection, self.agent, 'collectionobject', { + 'collection': api.uri_for_model('collection', self.collection.id), + 'catalognumber': 'foobar', + 'timestampcreated': datetime_1, 'timestampmodified': datetime_2}) + + self.assertEqual(datetime_1, obj.timestampcreated) + self.assertEqual(datetime_2, obj.timestampmodified) + + def test_cannot_override_old_timestamps_api(self): + datetime_1 = datetime(1960, 1, 1, 0, 0, 0) + datetime_2 = datetime(2020, 1, 1, 0, 0, 0) + current = timezone.now() + co_to_edit = self.collectionobjects[0] + data = api.get_resource('collectionobject', co_to_edit.id, skip_perms_check) + data['timestampcreated'] = datetime_1 + data['timestampmodified'] = datetime_2 + obj = api.update_obj(self.collection, self.agent, 'collectionobject', data['id'], data['version'], data) + + obj.refresh_from_db() + self.assertNotEqual(obj.timestampcreated, datetime_1, "Was able to override!") + self.assertNotEqual(obj.timestampmodified, datetime_2, "Was able to override!") + self.assertGreaterEqual(obj.timestampmodified, current, "Timestampmodified did not update correctly!") + self.assertGreater(current, obj.timestampcreated, "Timestampcreated should be at the past for this record!") \ No newline at end of file diff --git a/specifyweb/specify/tree_extras.py b/specifyweb/specify/tree_extras.py index f86db7de5e2..1a8296c44a2 100644 --- a/specifyweb/specify/tree_extras.py +++ b/specifyweb/specify/tree_extras.py @@ -4,7 +4,7 @@ from specifyweb.specify.tree_ranks import RankOperation, post_tree_rank_save, pre_tree_rank_deletion, \ verify_rank_parent_chain_integrity, pre_tree_rank_init, post_tree_rank_deletion -from specifyweb.specify.model_timestamp import pre_save_auto_timestamp_field_with_override +from specifyweb.specify.model_timestamp import save_auto_timestamp_field_with_override logger = logging.getLogger(__name__) @@ -33,8 +33,7 @@ class Meta: def save(self, *args, skip_tree_extras=False, **kwargs): def save(): - pre_save_auto_timestamp_field_with_override(self) - super(Tree, self).save(*args, **kwargs) + save_auto_timestamp_field_with_override(super(Tree, self).save, args, kwargs, self) if skip_tree_extras: return save()