Keep FK & M2M fields pointing to ContentType

This commit is contained in:
Jeremy Stretch 2025-07-25 09:42:38 -04:00
parent 5fe4c96ecf
commit 56955868ce
13 changed files with 28 additions and 107 deletions

View File

@ -18,13 +18,13 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='customfield', model_name='customfield',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='custom_fields', to='core.objecttype'), field=models.ManyToManyField(related_name='custom_fields', to='contenttypes.contenttype'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='customfield', model_name='customfield',
name='object_type', name='object_type',
field=models.ForeignKey( field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype' blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype'
), ),
), ),
migrations.RunSQL(( migrations.RunSQL((
@ -45,7 +45,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='customlink', model_name='customlink',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='custom_links', to='core.objecttype'), field=models.ManyToManyField(related_name='custom_links', to='contenttypes.contenttype'),
), ),
migrations.RunSQL( migrations.RunSQL(
'ALTER TABLE extras_customlink_content_types_id_seq RENAME TO extras_customlink_object_types_id_seq' 'ALTER TABLE extras_customlink_content_types_id_seq RENAME TO extras_customlink_object_types_id_seq'
@ -59,7 +59,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='eventrule', model_name='eventrule',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='event_rules', to='core.objecttype'), field=models.ManyToManyField(related_name='event_rules', to='contenttypes.contenttype'),
), ),
migrations.RunSQL( migrations.RunSQL(
'ALTER TABLE extras_eventrule_content_types_id_seq RENAME TO extras_eventrule_object_types_id_seq' 'ALTER TABLE extras_eventrule_content_types_id_seq RENAME TO extras_eventrule_object_types_id_seq'
@ -73,7 +73,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='exporttemplate', model_name='exporttemplate',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='export_templates', to='core.objecttype'), field=models.ManyToManyField(related_name='export_templates', to='contenttypes.contenttype'),
), ),
migrations.RunSQL( migrations.RunSQL(
'ALTER TABLE extras_exporttemplate_content_types_id_seq RENAME TO extras_exporttemplate_object_types_id_seq' 'ALTER TABLE extras_exporttemplate_content_types_id_seq RENAME TO extras_exporttemplate_object_types_id_seq'
@ -87,7 +87,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='savedfilter', model_name='savedfilter',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='saved_filters', to='core.objecttype'), field=models.ManyToManyField(related_name='saved_filters', to='contenttypes.contenttype'),
), ),
migrations.RunSQL( migrations.RunSQL(
'ALTER TABLE extras_savedfilter_content_types_id_seq RENAME TO extras_savedfilter_object_types_id_seq' 'ALTER TABLE extras_savedfilter_content_types_id_seq RENAME TO extras_savedfilter_object_types_id_seq'

View File

@ -11,6 +11,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='tag', model_name='tag',
name='object_types', name='object_types',
field=models.ManyToManyField(blank=True, related_name='+', to='core.objecttype'), field=models.ManyToManyField(blank=True, related_name='+', to='contenttypes.contenttype'),
), ),
] ]

View File

@ -37,7 +37,9 @@ class Migration(migrations.Migration):
( (
'object_type', 'object_type',
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name='table_configs', to='core.objecttype' on_delete=django.db.models.deletion.CASCADE,
related_name='table_configs',
to='contenttypes.contenttype'
), ),
), ),
( (

View File

@ -1,56 +0,0 @@
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0017_concrete_objecttype'),
('extras', '0130_imageattachment_description'),
]
operations = [
migrations.AlterField(
model_name='Tag',
name='object_types',
field=models.ManyToManyField(blank=True, related_name='+', to='core.objecttype'),
),
migrations.AlterField(
model_name='CustomField',
name='related_object_type',
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.objecttype'
),
),
migrations.AlterField(
model_name='CustomField',
name='object_types',
field=models.ManyToManyField(related_name='custom_fields', to='core.objecttype'),
),
migrations.AlterField(
model_name='EventRule',
name='object_types',
field=models.ManyToManyField(related_name='event_rules', to='core.objecttype'),
),
migrations.AlterField(
model_name='CustomLink',
name='object_types',
field=models.ManyToManyField(related_name='custom_links', to='core.objecttype'),
),
migrations.AlterField(
model_name='ExportTemplate',
name='object_types',
field=models.ManyToManyField(related_name='export_templates', to='core.objecttype'),
),
migrations.AlterField(
model_name='SavedFilter',
name='object_types',
field=models.ManyToManyField(related_name='saved_filters', to='core.objecttype'),
),
migrations.AlterField(
model_name='TableConfig',
name='object_type',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name='table_configs', to='core.objecttype'
),
),
]

View File

@ -72,7 +72,7 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='custom_fields', related_name='custom_fields',
help_text=_('The object(s) to which this field applies.') help_text=_('The object(s) to which this field applies.')
) )
@ -84,7 +84,7 @@ class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
help_text=_('The type of data this custom field holds') help_text=_('The type of data this custom field holds')
) )
related_object_type = models.ForeignKey( related_object_type = models.ForeignKey(
to='core.ObjectType', to='contenttypes.ContentType',
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True, blank=True,
null=True, null=True,

View File

@ -49,7 +49,7 @@ class EventRule(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLogged
webhook or executing a custom script. webhook or executing a custom script.
""" """
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='event_rules', related_name='event_rules',
verbose_name=_('object types'), verbose_name=_('object types'),
help_text=_("The object(s) to which this rule applies.") help_text=_("The object(s) to which this rule applies.")
@ -298,7 +298,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
code to be rendered with an object as context. code to be rendered with an object as context.
""" """
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='custom_links', related_name='custom_links',
help_text=_('The object type(s) to which this link applies.') help_text=_('The object type(s) to which this link applies.')
) )
@ -394,7 +394,7 @@ class CustomLink(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, RenderTemplateMixin): class ExportTemplate(SyncedDataMixin, CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, RenderTemplateMixin):
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='export_templates', related_name='export_templates',
help_text=_('The object type(s) to which this template applies.') help_text=_('The object type(s) to which this template applies.')
) )
@ -459,7 +459,7 @@ class SavedFilter(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
A set of predefined keyword parameters that can be reused to filter for specific objects. A set of predefined keyword parameters that can be reused to filter for specific objects.
""" """
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='saved_filters', related_name='saved_filters',
help_text=_('The object type(s) to which this filter applies.') help_text=_('The object type(s) to which this filter applies.')
) )
@ -539,7 +539,7 @@ class TableConfig(CloningMixin, ChangeLoggedModel):
A saved configuration of columns and ordering which applies to a specific table. A saved configuration of columns and ordering which applies to a specific table.
""" """
object_type = models.ForeignKey( object_type = models.ForeignKey(
to='core.ObjectType', to='contenttypes.ContentType',
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='table_configs', related_name='table_configs',
help_text=_("The table's object type"), help_text=_("The table's object type"),

View File

@ -35,7 +35,7 @@ class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
blank=True, blank=True,
) )
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='+', related_name='+',
blank=True, blank=True,
help_text=_("The object type(s) to which this tag can be applied.") help_text=_("The object type(s) to which this tag can be applied.")

View File

@ -3,7 +3,6 @@ from django.db.models.signals import m2m_changed, post_save, pre_delete
from django.dispatch import receiver from django.dispatch import receiver
from core.events import * from core.events import *
from core.models import ObjectType
from core.signals import job_end, job_start from core.signals import job_end, job_start
from extras.events import process_event_rules from extras.events import process_event_rules
from extras.models import EventRule, Notification, Subscription from extras.models import EventRule, Notification, Subscription
@ -82,7 +81,7 @@ def validate_assigned_tags(sender, instance, action, model, pk_set, **kwargs):
""" """
if action != 'pre_add': if action != 'pre_add':
return return
ct = ObjectType.objects.get_for_model(instance) ct = ContentType.objects.get_for_model(instance)
# Retrieve any applied Tags that are restricted to certain object types # Retrieve any applied Tags that are restricted to certain object types
for tag in model.objects.filter(pk__in=pk_set, object_types__isnull=False).prefetch_related('object_types'): for tag in model.objects.filter(pk__in=pk_set, object_types__isnull=False).prefetch_related('object_types'):
if ct not in tag.object_types.all(): if ct not in tag.object_types.all():

View File

@ -13,6 +13,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='objectpermission', model_name='objectpermission',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='object_permissions', to='core.objecttype'), field=models.ManyToManyField(related_name='object_permissions', to='contenttypes.contenttype'),
), ),
] ]

View File

@ -27,6 +27,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='objectpermission', model_name='objectpermission',
name='object_types', name='object_types',
field=models.ManyToManyField(related_name='object_permissions', to='core.objecttype'), field=models.ManyToManyField(related_name='object_permissions', to='contenttypes.contenttype'),
), ),
] ]

View File

@ -1,17 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0017_concrete_objecttype'),
('users', '0009_update_group_perms'),
]
operations = [
migrations.AlterField(
model_name='ObjectPermission',
name='object_types',
field=models.ManyToManyField(related_name='object_permissions', to='core.objecttype'),
),
]

View File

@ -29,7 +29,7 @@ class ObjectPermission(models.Model):
default=True default=True
) )
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to='core.ObjectType', to='contenttypes.ContentType',
related_name='object_permissions' related_name='object_permissions'
) )
actions = ArrayField( actions = ArrayField(

View File

@ -1,19 +1,17 @@
import django_filters
from datetime import datetime, timezone from datetime import datetime, timezone
from itertools import chain from itertools import chain
from mptt.models import MPTTModel
import django_filters
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel, OneToOneRel from django.db.models import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel, OneToOneRel
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from mptt.models import MPTTModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from extras.filters import TagFilter from extras.filters import TagFilter
from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter
from core.models import ObjectType
__all__ = ( __all__ = (
'BaseFilterSetTests', 'BaseFilterSetTests',
'ChangeLoggedFilterSetTests', 'ChangeLoggedFilterSetTests',
@ -61,13 +59,6 @@ class BaseFilterSetTests:
if field.related_model is ContentType: if field.related_model is ContentType:
return [(None, None)] return [(None, None)]
# ForeignKeys to ObjectType need two filters: 'app.model' & PK
if field.related_model is ObjectType:
return [
(filter_name, ContentTypeFilter),
(f'{filter_name}_id', django_filters.ModelMultipleChoiceFilter),
]
# ForeignKey to an MPTT-enabled model # ForeignKey to an MPTT-enabled model
if issubclass(field.related_model, MPTTModel) and field.model is not field.related_model: if issubclass(field.related_model, MPTTModel) and field.model is not field.related_model:
return [(f'{filter_name}_id', TreeNodeMultipleChoiceFilter)] return [(f'{filter_name}_id', TreeNodeMultipleChoiceFilter)]
@ -79,8 +70,10 @@ class BaseFilterSetTests:
filter_name = self.get_m2m_filter_name(field) filter_name = self.get_m2m_filter_name(field)
filter_name = self.filter_name_map.get(filter_name, filter_name) filter_name = self.filter_name_map.get(filter_name, filter_name)
# ManyToManyFields to ObjectType need two filters: 'app.model' & PK # ManyToManyFields to ContentType need two filters: 'app.model' & PK
if field.related_model is ObjectType: if field.related_model is ContentType:
# Standardize on object_type for filter name even though it's technically a ContentType
filter_name = 'object_type'
return [ return [
(filter_name, ContentTypeFilter), (filter_name, ContentTypeFilter),
(f'{filter_name}_id', django_filters.ModelMultipleChoiceFilter), (f'{filter_name}_id', django_filters.ModelMultipleChoiceFilter),