From 2de79230526f8ddbdf9f03c17f2c06d7756268b8 Mon Sep 17 00:00:00 2001 From: Hanne Moa Date: Mon, 27 Jan 2025 13:19:23 +0100 Subject: [PATCH] Make LegacyGenericforeignKey work correctly on Django > 1.8.x --- python/nav/models/fields.py | 56 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/python/nav/models/fields.py b/python/nav/models/fields.py index cabecc557d..d02d1c8415 100644 --- a/python/nav/models/fields.py +++ b/python/nav/models/fields.py @@ -25,6 +25,7 @@ from django import forms from django.db import models from django.db.models import signals +from django.db.models.fields.mixins import FieldCacheMixin from django.core import exceptions from django.db.models import Q from django.apps import apps @@ -146,7 +147,7 @@ def formfield(self, **kwargs): # this interfaces with Django model protocols, which generates unnecessary # pylint violations: # pylint: disable=W0201,W0212 -class LegacyGenericForeignKey(object): +class LegacyGenericForeignKey(FieldCacheMixin): """Generic foreign key for legacy NAV database. Some legacy tables in NAV have generic foreign keys that look very much @@ -155,16 +156,25 @@ class LegacyGenericForeignKey(object): """ + # Field flags + auto_created = False + concrete = False + editable = False + hidden = False + + is_relation = True + many_to_many = False + many_to_one = True + one_to_one = False + related_model = None + remote_field = None + def __init__(self, model_name_field, model_fk_field, for_concrete_model=True): self.mn_field = model_name_field self.fk_field = model_fk_field - self.is_relation = True - self.many_to_many = False self.one_to_many = True - self.related_model = None - self.auto_created = False - self.for_concrete_model = for_concrete_model self.editable = False + self.for_concrete_model = for_concrete_model def __str__(self): modelname = getattr(self, 'mn_field') @@ -175,7 +185,6 @@ def contribute_to_class(self, cls, name): """Add things to the model class using this descriptor""" self.name = name self.model = cls - self.cache_attr = "_%s_cache" % name cls._meta.private_fields.append(self) if not cls._meta.abstract: @@ -183,6 +192,9 @@ def contribute_to_class(self, cls, name): setattr(cls, name, self) + def get_cache_name(self): + return self.name + def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): """ Handles initializing an object with the generic FK instead of @@ -197,28 +209,22 @@ def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): kwargs[self.mn_field] = None kwargs[self.fk_field] = None - def is_cached(self, instance): - return hasattr(instance, self.cache_attr) - def __get__(self, instance, instance_type=None): if instance is None: return self - try: - return getattr(instance, self.cache_attr) - except AttributeError: - rel_obj = None + rel_obj = self.get_cached_value(instance, default=None) - field = self.model._meta.get_field(self.mn_field) - table_name = getattr(instance, field.get_attname(), None) - rel_model = self.get_model_class(table_name) - if rel_model: - try: - rel_obj = rel_model.objects.get(id=getattr(instance, self.fk_field)) - except exceptions.ObjectDoesNotExist: - pass - setattr(instance, self.cache_attr, rel_obj) - return rel_obj + field = self.model._meta.get_field(self.mn_field) + table_name = getattr(instance, field.get_attname(), None) + rel_model = self.get_model_class(table_name) + if rel_model: + try: + rel_obj = rel_model.objects.get(id=getattr(instance, self.fk_field)) + except exceptions.ObjectDoesNotExist: + pass + self.set_cached_value(instance, rel_obj) + return rel_obj def __set__(self, instance, value): if instance is None: @@ -232,7 +238,7 @@ def __set__(self, instance, value): setattr(instance, self.mn_field, table_name) setattr(instance, self.fk_field, fkey) - setattr(instance, self.cache_attr, value) + self.set_cached_value(instance, value) @staticmethod def get_model_name(obj) -> str: