Remove FeatureQuery

This commit is contained in:
Jeremy Stretch 2023-11-06 08:44:28 -05:00
parent 7d5c36c573
commit b692b146cb
8 changed files with 15 additions and 52 deletions

View File

@ -4,7 +4,6 @@ from django.conf import settings
import django.core.validators import django.core.validators
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import extras.utils
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -30,7 +29,7 @@ class Migration(migrations.Migration):
('status', models.CharField(default='pending', max_length=30)), ('status', models.CharField(default='pending', max_length=30)),
('data', models.JSONField(blank=True, null=True)), ('data', models.JSONField(blank=True, null=True)),
('job_id', models.UUIDField(unique=True)), ('job_id', models.UUIDField(unique=True)),
('object_type', models.ForeignKey(limit_choices_to=extras.utils.FeatureQuery('jobs'), on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='contenttypes.contenttype')), ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='contenttypes.contenttype')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
], ],
options={ options={

View File

@ -12,7 +12,6 @@ from django.utils.translation import gettext as _
from core.choices import JobStatusChoices from core.choices import JobStatusChoices
from extras.constants import EVENT_JOB_END, EVENT_JOB_START from extras.constants import EVENT_JOB_END, EVENT_JOB_START
from extras.utils import FeatureQuery
from netbox.config import get_config from netbox.config import get_config
from netbox.constants import RQ_QUEUE_DEFAULT from netbox.constants import RQ_QUEUE_DEFAULT
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
@ -28,9 +27,8 @@ class Job(models.Model):
Tracks the lifecycle of a job which represents a background task (e.g. the execution of a custom script). Tracks the lifecycle of a job which represents a background task (e.g. the execution of a custom script).
""" """
object_type = models.ForeignKey( object_type = models.ForeignKey(
to=ContentType, to='contenttypes.ContentType',
related_name='jobs', related_name='jobs',
limit_choices_to=FeatureQuery('jobs'),
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
object_id = models.PositiveBigIntegerField( object_id = models.PositiveBigIntegerField(

View File

@ -88,7 +88,7 @@ class Migration(migrations.Migration):
('secret', models.CharField(blank=True, max_length=255)), ('secret', models.CharField(blank=True, max_length=255)),
('ssl_verification', models.BooleanField(default=True)), ('ssl_verification', models.BooleanField(default=True)),
('ca_file_path', models.CharField(blank=True, max_length=4096, null=True)), ('ca_file_path', models.CharField(blank=True, max_length=4096, null=True)),
('content_types', models.ManyToManyField(limit_choices_to=extras.utils.FeatureQuery('webhooks'), related_name='webhooks', to='contenttypes.ContentType')), ('content_types', models.ManyToManyField(related_name='webhooks', to='contenttypes.ContentType')),
], ],
options={ options={
'ordering': ('name',), 'ordering': ('name',),
@ -151,7 +151,7 @@ class Migration(migrations.Migration):
('status', models.CharField(default='pending', max_length=30)), ('status', models.CharField(default='pending', max_length=30)),
('data', models.JSONField(blank=True, null=True)), ('data', models.JSONField(blank=True, null=True)),
('job_id', models.UUIDField(unique=True)), ('job_id', models.UUIDField(unique=True)),
('obj_type', models.ForeignKey(limit_choices_to=extras.utils.FeatureQuery('jobs'), on_delete=django.db.models.deletion.CASCADE, related_name='job_results', to='contenttypes.contenttype')), ('obj_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='job_results', to='contenttypes.contenttype')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
], ],
options={ options={
@ -184,7 +184,7 @@ class Migration(migrations.Migration):
('mime_type', models.CharField(blank=True, max_length=50)), ('mime_type', models.CharField(blank=True, max_length=50)),
('file_extension', models.CharField(blank=True, max_length=15)), ('file_extension', models.CharField(blank=True, max_length=15)),
('as_attachment', models.BooleanField(default=True)), ('as_attachment', models.BooleanField(default=True)),
('content_type', models.ForeignKey(limit_choices_to=extras.utils.FeatureQuery('export_templates'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
], ],
options={ options={
'ordering': ['content_type', 'name'], 'ordering': ['content_type', 'name'],
@ -201,7 +201,7 @@ class Migration(migrations.Migration):
('group_name', models.CharField(blank=True, max_length=50)), ('group_name', models.CharField(blank=True, max_length=50)),
('button_class', models.CharField(default='default', max_length=30)), ('button_class', models.CharField(default='default', max_length=30)),
('new_window', models.BooleanField(default=False)), ('new_window', models.BooleanField(default=False)),
('content_type', models.ForeignKey(limit_choices_to=extras.utils.FeatureQuery('custom_links'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
], ],
options={ options={
'ordering': ['group_name', 'weight', 'name'], 'ordering': ['group_name', 'weight', 'name'],
@ -223,7 +223,7 @@ class Migration(migrations.Migration):
('validation_maximum', models.PositiveIntegerField(blank=True, null=True)), ('validation_maximum', models.PositiveIntegerField(blank=True, null=True)),
('validation_regex', models.CharField(blank=True, max_length=500, validators=[utilities.validators.validate_regex])), ('validation_regex', models.CharField(blank=True, max_length=500, validators=[utilities.validators.validate_regex])),
('choices', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), blank=True, null=True, size=None)), ('choices', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), blank=True, null=True, size=None)),
('content_types', models.ManyToManyField(limit_choices_to=extras.utils.FeatureQuery('custom_fields'), related_name='custom_fields', to='contenttypes.ContentType')), ('content_types', models.ManyToManyField(related_name='custom_fields', to='contenttypes.ContentType')),
], ],
options={ options={
'ordering': ['weight', 'name'], 'ordering': ['weight', 'name'],

View File

@ -1,5 +1,4 @@
from django.db import migrations, models from django.db import migrations, models
import extras.utils
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -13,7 +12,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='tag', model_name='tag',
name='object_types', name='object_types',
field=models.ManyToManyField(blank=True, limit_choices_to=extras.utils.FeatureQuery('tags'), related_name='+', to='contenttypes.contenttype'), field=models.ManyToManyField(blank=True, related_name='+', to='contenttypes.contenttype'),
), ),
migrations.RenameIndex( migrations.RenameIndex(
model_name='taggeditem', model_name='taggeditem',

View File

@ -10,13 +10,11 @@ from django.contrib.postgres.fields import ArrayField
from django.core.validators import RegexValidator, ValidationError from django.core.validators import RegexValidator, ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from extras.choices import * from extras.choices import *
from extras.data import CHOICE_SETS from extras.data import CHOICE_SETS
from extras.utils import FeatureQuery
from netbox.models import ChangeLoggedModel from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin from netbox.models.features import CloningMixin, ExportTemplatesMixin
from netbox.search import FieldTypes from netbox.search import FieldTypes
@ -60,9 +58,8 @@ class CustomFieldManager(models.Manager.from_queryset(RestrictedQuerySet)):
class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel): class CustomField(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel):
content_types = models.ManyToManyField( content_types = models.ManyToManyField(
to=ContentType, to='contenttypes.ContentType',
related_name='custom_fields', related_name='custom_fields',
limit_choices_to=FeatureQuery('custom_fields'),
help_text=_('The object(s) to which this field applies.') help_text=_('The object(s) to which this field applies.')
) )
type = models.CharField( type = models.CharField(
@ -73,7 +70,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')
) )
object_type = models.ForeignKey( object_type = models.ForeignKey(
to=ContentType, to='contenttypes.ContentType',
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True, blank=True,
null=True, null=True,

View File

@ -3,7 +3,6 @@ import urllib.parse
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache from django.core.cache import cache
from django.core.validators import ValidationError from django.core.validators import ValidationError
from django.db import models from django.db import models
@ -14,10 +13,11 @@ from django.utils.formats import date_format
from django.utils.translation import gettext, gettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from rest_framework.utils.encoders import JSONEncoder from rest_framework.utils.encoders import JSONEncoder
from core.models import ContentType
from extras.choices import * from extras.choices import *
from extras.conditions import ConditionSet from extras.conditions import ConditionSet
from extras.constants import * from extras.constants import *
from extras.utils import FeatureQuery, image_upload from extras.utils import image_upload
from netbox.config import get_config from netbox.config import get_config
from netbox.models import ChangeLoggedModel from netbox.models import ChangeLoggedModel
from netbox.models.features import ( from netbox.models.features import (
@ -45,10 +45,9 @@ class Webhook(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, ChangeLoggedMo
Each Webhook can be limited to firing only on certain actions or certain object types. Each Webhook can be limited to firing only on certain actions or certain object types.
""" """
content_types = models.ManyToManyField( content_types = models.ManyToManyField(
to=ContentType, to='contenttypes.ContentType',
related_name='webhooks', related_name='webhooks',
verbose_name=_('object types'), verbose_name=_('object types'),
limit_choices_to=FeatureQuery('webhooks'),
help_text=_("The object(s) to which this Webhook applies.") help_text=_("The object(s) to which this Webhook applies.")
) )
name = models.CharField( name = models.CharField(
@ -645,7 +644,7 @@ class JournalEntry(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ExportTemplat
super().clean() super().clean()
# Prevent the creation of journal entries on unsupported models # Prevent the creation of journal entries on unsupported models
permitted_types = ContentType.objects.filter(FeatureQuery('journaling').get_query()) permitted_types = ContentType.objects.with_feature('journaling')
if self.assigned_object_type not in permitted_types: if self.assigned_object_type not in permitted_types:
raise ValidationError( raise ValidationError(
_("Journaling is not supported for this object type ({type}).").format(type=self.assigned_object_type) _("Journaling is not supported for this object type ({type}).").format(type=self.assigned_object_type)

View File

@ -1,13 +1,10 @@
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from taggit.models import TagBase, GenericTaggedItemBase from taggit.models import TagBase, GenericTaggedItemBase
from extras.utils import FeatureQuery
from netbox.models import ChangeLoggedModel from netbox.models import ChangeLoggedModel
from netbox.models.features import CloningMixin, ExportTemplatesMixin from netbox.models.features import CloningMixin, ExportTemplatesMixin
from utilities.choices import ColorChoices from utilities.choices import ColorChoices
@ -37,9 +34,8 @@ class Tag(CloningMixin, ExportTemplatesMixin, ChangeLoggedModel, TagBase):
blank=True, blank=True,
) )
object_types = models.ManyToManyField( object_types = models.ManyToManyField(
to=ContentType, to='contenttypes.ContentType',
related_name='+', related_name='+',
limit_choices_to=FeatureQuery('tags'),
blank=True, blank=True,
help_text=_("The object type(s) to which this this tag can be applied.") help_text=_("The object type(s) to which this this tag can be applied.")
) )

View File

@ -1,5 +1,3 @@
from django.db.models import Q
from django.utils.deconstruct import deconstructible
from taggit.managers import _TaggableManager from taggit.managers import _TaggableManager
from netbox.registry import registry from netbox.registry import registry
@ -31,29 +29,6 @@ def image_upload(instance, filename):
return '{}{}_{}_{}'.format(path, instance.content_type.name, instance.object_id, filename) return '{}{}_{}_{}'.format(path, instance.content_type.name, instance.object_id, filename)
@deconstructible
class FeatureQuery:
"""
Helper class that delays evaluation of the registry contents for the functionality store
until it has been populated.
"""
def __init__(self, feature):
self.feature = feature
def __call__(self):
return self.get_query()
def get_query(self):
"""
Given an extras feature, return a Q object for content type lookup
"""
query = Q()
for app_label, models in registry['model_features'][self.feature].items():
query |= Q(app_label=app_label, model__in=models)
return query
def register_features(model, features): def register_features(model, features):
""" """
Register model features in the application registry. Register model features in the application registry.