Revert "implemented registry for extras model functionality"

This reverts commit 235d99021b.
This commit is contained in:
John Anderson 2020-03-15 00:25:46 -04:00
parent 6ea15cec6f
commit 2dc31c0edd
5 changed files with 173 additions and 249 deletions

View File

@ -21,7 +21,6 @@ from dcim.constants import *
from dcim.fields import ASNField from dcim.fields import ASNField
from dcim.elevations import RackElevationSVG from dcim.elevations import RackElevationSVG
from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_functionality
from utilities.fields import ColorField, NaturalOrderingField from utilities.fields import ColorField, NaturalOrderingField
from utilities.models import ChangeLoggedModel from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object, to_meters from utilities.utils import serialize_object, to_meters
@ -1221,7 +1220,6 @@ class Platform(ChangeLoggedModel):
) )
@extras_functionality(['webhooks', 'custom_fields', 'export_templates', 'custom_links', 'graphs'])
class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel): class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
""" """
A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType, A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType,

View File

@ -1,6 +1,5 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from drf_yasg.utils import swagger_serializer_method from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers from rest_framework import serializers
@ -14,7 +13,6 @@ from extras.constants import *
from extras.models import ( from extras.models import (
ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag, ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag,
) )
from extras.utils import FunctionalityQueryset
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from users.api.nested_serializers import NestedUserSerializer from users.api.nested_serializers import NestedUserSerializer
@ -33,7 +31,7 @@ from .nested_serializers import *
class GraphSerializer(ValidatedModelSerializer): class GraphSerializer(ValidatedModelSerializer):
type = ContentTypeField( type = ContentTypeField(
queryset=ContentType.objects.filter(FunctionalityQueryset('graphs').get_queryset()), queryset=ContentType.objects.filter(GRAPH_MODELS),
) )
class Meta: class Meta:
@ -69,7 +67,7 @@ class RenderedGraphSerializer(serializers.ModelSerializer):
class ExportTemplateSerializer(ValidatedModelSerializer): class ExportTemplateSerializer(ValidatedModelSerializer):
content_type = ContentTypeField( content_type = ContentTypeField(
queryset=ContentType.objects.filter(Q(FunctionalityQueryset('export_templates').get_queryset())), queryset=ContentType.objects.filter(EXPORTTEMPLATE_MODELS),
) )
template_language = ChoiceField( template_language = ChoiceField(
choices=TemplateLanguageChoices, choices=TemplateLanguageChoices,

View File

@ -2,127 +2,127 @@ from django.db.models import Q
# Models which support custom fields # Models which support custom fields
#CUSTOMFIELD_MODELS = Q( CUSTOMFIELD_MODELS = Q(
# Q(app_label='circuits', model__in=[ Q(app_label='circuits', model__in=[
# 'circuit', 'circuit',
# 'provider', 'provider',
# ]) | ]) |
# Q(app_label='dcim', model__in=[ Q(app_label='dcim', model__in=[
# 'device', 'device',
# 'devicetype', 'devicetype',
# 'powerfeed', 'powerfeed',
# 'rack', 'rack',
# 'site', 'site',
# ]) | ]) |
# Q(app_label='ipam', model__in=[ Q(app_label='ipam', model__in=[
# 'aggregate', 'aggregate',
# 'ipaddress', 'ipaddress',
# 'prefix', 'prefix',
# 'service', 'service',
# 'vlan', 'vlan',
# 'vrf', 'vrf',
# ]) | ]) |
# Q(app_label='secrets', model__in=[ Q(app_label='secrets', model__in=[
# 'secret', 'secret',
# ]) | ]) |
# Q(app_label='tenancy', model__in=[ Q(app_label='tenancy', model__in=[
# 'tenant', 'tenant',
# ]) | ]) |
# Q(app_label='virtualization', model__in=[ Q(app_label='virtualization', model__in=[
# 'cluster', 'cluster',
# 'virtualmachine', 'virtualmachine',
# ]) ])
#) )
#
## Custom links # Custom links
#CUSTOMLINK_MODELS = Q( CUSTOMLINK_MODELS = Q(
# Q(app_label='circuits', model__in=[ Q(app_label='circuits', model__in=[
# 'circuit', 'circuit',
# 'provider', 'provider',
# ]) | ]) |
# Q(app_label='dcim', model__in=[ Q(app_label='dcim', model__in=[
# 'cable', 'cable',
# 'device', 'device',
# 'devicetype', 'devicetype',
# 'powerpanel', 'powerpanel',
# 'powerfeed', 'powerfeed',
# 'rack', 'rack',
# 'site', 'site',
# ]) | ]) |
# Q(app_label='ipam', model__in=[ Q(app_label='ipam', model__in=[
# 'aggregate', 'aggregate',
# 'ipaddress', 'ipaddress',
# 'prefix', 'prefix',
# 'service', 'service',
# 'vlan', 'vlan',
# 'vrf', 'vrf',
# ]) | ]) |
# Q(app_label='secrets', model__in=[ Q(app_label='secrets', model__in=[
# 'secret', 'secret',
# ]) | ]) |
# Q(app_label='tenancy', model__in=[ Q(app_label='tenancy', model__in=[
# 'tenant', 'tenant',
# ]) | ]) |
# Q(app_label='virtualization', model__in=[ Q(app_label='virtualization', model__in=[
# 'cluster', 'cluster',
# 'virtualmachine', 'virtualmachine',
# ]) ])
#) )
#
## Models which can have Graphs associated with them # Models which can have Graphs associated with them
#GRAPH_MODELS = Q( GRAPH_MODELS = Q(
# Q(app_label='circuits', model__in=[ Q(app_label='circuits', model__in=[
# 'provider', 'provider',
# ]) | ]) |
# Q(app_label='dcim', model__in=[ Q(app_label='dcim', model__in=[
# 'device', 'device',
# 'interface', 'interface',
# 'site', 'site',
# ]) ])
#) )
#
## Models which support export templates # Models which support export templates
#EXPORTTEMPLATE_MODELS = Q( EXPORTTEMPLATE_MODELS = Q(
# Q(app_label='circuits', model__in=[ Q(app_label='circuits', model__in=[
# 'circuit', 'circuit',
# 'provider', 'provider',
# ]) | ]) |
# Q(app_label='dcim', model__in=[ Q(app_label='dcim', model__in=[
# 'cable', 'cable',
# 'consoleport', 'consoleport',
# 'device', 'device',
# 'devicetype', 'devicetype',
# 'interface', 'interface',
# 'inventoryitem', 'inventoryitem',
# 'manufacturer', 'manufacturer',
# 'powerpanel', 'powerpanel',
# 'powerport', 'powerport',
# 'powerfeed', 'powerfeed',
# 'rack', 'rack',
# 'rackgroup', 'rackgroup',
# 'region', 'region',
# 'site', 'site',
# 'virtualchassis', 'virtualchassis',
# ]) | ]) |
# Q(app_label='ipam', model__in=[ Q(app_label='ipam', model__in=[
# 'aggregate', 'aggregate',
# 'ipaddress', 'ipaddress',
# 'prefix', 'prefix',
# 'service', 'service',
# 'vlan', 'vlan',
# 'vrf', 'vrf',
# ]) | ]) |
# Q(app_label='secrets', model__in=[ Q(app_label='secrets', model__in=[
# 'secret', 'secret',
# ]) | ]) |
# Q(app_label='tenancy', model__in=[ Q(app_label='tenancy', model__in=[
# 'tenant', 'tenant',
# ]) | ]) |
# Q(app_label='virtualization', model__in=[ Q(app_label='virtualization', model__in=[
# 'cluster', 'cluster',
# 'virtualmachine', 'virtualmachine',
# ]) ])
#) )
# Report logging levels # Report logging levels
LOG_DEFAULT = 0 LOG_DEFAULT = 0
@ -141,58 +141,48 @@ LOG_LEVEL_CODES = {
HTTP_CONTENT_TYPE_JSON = 'application/json' HTTP_CONTENT_TYPE_JSON = 'application/json'
# Models which support registered webhooks # Models which support registered webhooks
#WEBHOOK_MODELS = Q( WEBHOOK_MODELS = Q(
# Q(app_label='circuits', model__in=[ Q(app_label='circuits', model__in=[
# 'circuit', 'circuit',
# 'provider', 'provider',
# ]) | ]) |
# Q(app_label='dcim', model__in=[ Q(app_label='dcim', model__in=[
# 'cable', 'cable',
# 'consoleport', 'consoleport',
# 'consoleserverport', 'consoleserverport',
# 'device', 'device',
# 'devicebay', 'devicebay',
# 'devicetype', 'devicetype',
# 'frontport', 'frontport',
# 'interface', 'interface',
# 'inventoryitem', 'inventoryitem',
# 'manufacturer', 'manufacturer',
# 'poweroutlet', 'poweroutlet',
# 'powerpanel', 'powerpanel',
# 'powerport', 'powerport',
# 'powerfeed', 'powerfeed',
# 'rack', 'rack',
# 'rearport', 'rearport',
# 'region', 'region',
# 'site', 'site',
# 'virtualchassis', 'virtualchassis',
# ]) | ]) |
# Q(app_label='ipam', model__in=[ Q(app_label='ipam', model__in=[
# 'aggregate', 'aggregate',
# 'ipaddress', 'ipaddress',
# 'prefix', 'prefix',
# 'service', 'service',
# 'vlan', 'vlan',
# 'vrf', 'vrf',
# ]) | ]) |
# Q(app_label='secrets', model__in=[ Q(app_label='secrets', model__in=[
# 'secret', 'secret',
# ]) | ]) |
# Q(app_label='tenancy', model__in=[ Q(app_label='tenancy', model__in=[
# 'tenant', 'tenant',
# ]) | ]) |
# Q(app_label='virtualization', model__in=[ Q(app_label='virtualization', model__in=[
# 'cluster', 'cluster',
# 'virtualmachine', 'virtualmachine',
# ]) ])
#) )
# Registerable extras functionalities
EXTRAS_FUNCTIONALITIES = [
'custom_fields',
'custom_links',
'graphs',
'export_templates',
'webhooks'
]

View File

@ -22,7 +22,6 @@ from utilities.utils import deepmerge, render_jinja2
from .choices import * from .choices import *
from .constants import * from .constants import *
from .querysets import ConfigContextQuerySet from .querysets import ConfigContextQuerySet
from .utils import FunctionalityQueryset
__all__ = ( __all__ = (
@ -59,7 +58,7 @@ class Webhook(models.Model):
to=ContentType, to=ContentType,
related_name='webhooks', related_name='webhooks',
verbose_name='Object types', verbose_name='Object types',
limit_choices_to=FunctionalityQueryset('webhooks'), limit_choices_to=WEBHOOK_MODELS,
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(
@ -224,7 +223,7 @@ class CustomField(models.Model):
to=ContentType, to=ContentType,
related_name='custom_fields', related_name='custom_fields',
verbose_name='Object(s)', verbose_name='Object(s)',
limit_choices_to=FunctionalityQueryset('custom_fields'), limit_choices_to=CUSTOMFIELD_MODELS,
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(
@ -471,7 +470,7 @@ class CustomLink(models.Model):
content_type = models.ForeignKey( content_type = models.ForeignKey(
to=ContentType, to=ContentType,
on_delete=models.CASCADE, on_delete=models.CASCADE,
limit_choices_to=FunctionalityQueryset('custom_links') limit_choices_to=CUSTOMLINK_MODELS
) )
name = models.CharField( name = models.CharField(
max_length=100, max_length=100,
@ -519,7 +518,7 @@ class Graph(models.Model):
type = models.ForeignKey( type = models.ForeignKey(
to=ContentType, to=ContentType,
on_delete=models.CASCADE, on_delete=models.CASCADE,
limit_choices_to=FunctionalityQueryset('graphs') limit_choices_to=GRAPH_MODELS
) )
weight = models.PositiveSmallIntegerField( weight = models.PositiveSmallIntegerField(
default=1000 default=1000
@ -582,7 +581,7 @@ class ExportTemplate(models.Model):
content_type = models.ForeignKey( content_type = models.ForeignKey(
to=ContentType, to=ContentType,
on_delete=models.CASCADE, on_delete=models.CASCADE,
limit_choices_to=FunctionalityQueryset('export_templates') limit_choices_to=EXPORTTEMPLATE_MODELS
) )
name = models.CharField( name = models.CharField(
max_length=100 max_length=100

View File

@ -1,11 +1,6 @@
import collections
from django.db.models import Q
from taggit.managers import _TaggableManager from taggit.managers import _TaggableManager
from utilities.querysets import DummyQuerySet from utilities.querysets import DummyQuerySet
from extras.constants import EXTRAS_FUNCTIONALITIES
def is_taggable(obj): def is_taggable(obj):
""" """
@ -18,59 +13,3 @@ def is_taggable(obj):
if isinstance(obj.tags, DummyQuerySet): if isinstance(obj.tags, DummyQuerySet):
return True return True
return False return False
class Registry:
"""
Singleton object used to store important data
"""
instance = None
def __new__(cls):
if cls.instance is not None:
return cls.instance
else:
cls.instance = super().__new__(cls)
cls.model_functionality_store = {f: collections.defaultdict(list) for f in EXTRAS_FUNCTIONALITIES}
return cls.instance
class FunctionalityQueryset:
"""
Helper class that delays evaluation of the registry contents for the functionaility store
until it has been populated.
"""
def __init__(self, functionality):
self.functionality = functionality
def __call__(self):
return self.get_queryset()
def get_queryset(self):
"""
Given an extras functionality, return a Q object for content type lookup
"""
query = Q()
registry = Registry()
for app_label, models in registry.model_functionality_store[self.functionality].items():
query |= Q(app_label=app_label, model__in=models)
return query
def extras_functionality(functionalities):
"""
Decorator used to register extras provided functionalities to a model
"""
def wrapper(model_class):
if isinstance(functionalities, list) and functionalities:
registry = Registry()
model_class._extras_functionality = []
for functionality in functionalities:
if functionality in EXTRAS_FUNCTIONALITIES:
model_class._extras_functionality.append(functionality)
app_label, model_name = model_class._meta.label_lower.split('.')
registry.model_functionality_store[functionality][app_label].append(model_name)
return model_class
return wrapper