filters.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import django_filters
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.db.models import Q
  4. from dcim.models import DeviceRole, Platform, Region, Site
  5. from tenancy.models import Tenant, TenantGroup
  6. from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT
  7. from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, Tag
  8. class CustomFieldFilter(django_filters.Filter):
  9. """
  10. Filter objects by the presence of a CustomFieldValue. The filter's name is used as the CustomField name.
  11. """
  12. def __init__(self, custom_field, *args, **kwargs):
  13. self.cf_type = custom_field.type
  14. self.filter_logic = custom_field.filter_logic
  15. super().__init__(*args, **kwargs)
  16. def filter(self, queryset, value):
  17. # Skip filter on empty value
  18. if value is None or not value.strip():
  19. return queryset
  20. # Selection fields get special treatment (values must be integers)
  21. if self.cf_type == CF_TYPE_SELECT:
  22. try:
  23. # Treat 0 as None
  24. if int(value) == 0:
  25. return queryset.exclude(
  26. custom_field_values__field__name=self.field_name,
  27. )
  28. # Match on exact CustomFieldChoice PK
  29. else:
  30. return queryset.filter(
  31. custom_field_values__field__name=self.field_name,
  32. custom_field_values__serialized_value=value,
  33. )
  34. except ValueError:
  35. return queryset.none()
  36. # Apply the assigned filter logic (exact or loose)
  37. if self.cf_type == CF_TYPE_BOOLEAN or self.filter_logic == CF_FILTER_EXACT:
  38. queryset = queryset.filter(
  39. custom_field_values__field__name=self.field_name,
  40. custom_field_values__serialized_value=value
  41. )
  42. else:
  43. queryset = queryset.filter(
  44. custom_field_values__field__name=self.field_name,
  45. custom_field_values__serialized_value__icontains=value
  46. )
  47. return queryset
  48. class CustomFieldFilterSet(django_filters.FilterSet):
  49. """
  50. Dynamically add a Filter for each CustomField applicable to the parent model.
  51. """
  52. def __init__(self, *args, **kwargs):
  53. super().__init__(*args, **kwargs)
  54. obj_type = ContentType.objects.get_for_model(self._meta.model)
  55. custom_fields = CustomField.objects.filter(obj_type=obj_type).exclude(filter_logic=CF_FILTER_DISABLED)
  56. for cf in custom_fields:
  57. self.filters['cf_{}'.format(cf.name)] = CustomFieldFilter(field_name=cf.name, custom_field=cf)
  58. class GraphFilter(django_filters.FilterSet):
  59. class Meta:
  60. model = Graph
  61. fields = ['type', 'name']
  62. class ExportTemplateFilter(django_filters.FilterSet):
  63. class Meta:
  64. model = ExportTemplate
  65. fields = ['content_type', 'name', 'template_language']
  66. class TagFilter(django_filters.FilterSet):
  67. q = django_filters.CharFilter(
  68. method='search',
  69. label='Search',
  70. )
  71. class Meta:
  72. model = Tag
  73. fields = ['name', 'slug']
  74. def search(self, queryset, name, value):
  75. if not value.strip():
  76. return queryset
  77. return queryset.filter(
  78. Q(name__icontains=value) |
  79. Q(slug__icontains=value)
  80. )
  81. class ConfigContextFilter(django_filters.FilterSet):
  82. q = django_filters.CharFilter(
  83. method='search',
  84. label='Search',
  85. )
  86. region_id = django_filters.ModelMultipleChoiceFilter(
  87. field_name='regions',
  88. queryset=Region.objects.all(),
  89. label='Region',
  90. )
  91. region = django_filters.ModelMultipleChoiceFilter(
  92. field_name='regions__slug',
  93. queryset=Region.objects.all(),
  94. to_field_name='slug',
  95. label='Region (slug)',
  96. )
  97. site_id = django_filters.ModelMultipleChoiceFilter(
  98. field_name='sites',
  99. queryset=Site.objects.all(),
  100. label='Site',
  101. )
  102. site = django_filters.ModelMultipleChoiceFilter(
  103. field_name='sites__slug',
  104. queryset=Site.objects.all(),
  105. to_field_name='slug',
  106. label='Site (slug)',
  107. )
  108. role_id = django_filters.ModelMultipleChoiceFilter(
  109. field_name='roles',
  110. queryset=DeviceRole.objects.all(),
  111. label='Role',
  112. )
  113. role = django_filters.ModelMultipleChoiceFilter(
  114. field_name='roles__slug',
  115. queryset=DeviceRole.objects.all(),
  116. to_field_name='slug',
  117. label='Role (slug)',
  118. )
  119. platform_id = django_filters.ModelMultipleChoiceFilter(
  120. field_name='platforms',
  121. queryset=Platform.objects.all(),
  122. label='Platform',
  123. )
  124. platform = django_filters.ModelMultipleChoiceFilter(
  125. field_name='platforms__slug',
  126. queryset=Platform.objects.all(),
  127. to_field_name='slug',
  128. label='Platform (slug)',
  129. )
  130. tenant_group_id = django_filters.ModelMultipleChoiceFilter(
  131. field_name='tenant_groups',
  132. queryset=TenantGroup.objects.all(),
  133. label='Tenant group',
  134. )
  135. tenant_group = django_filters.ModelMultipleChoiceFilter(
  136. field_name='tenant_groups__slug',
  137. queryset=TenantGroup.objects.all(),
  138. to_field_name='slug',
  139. label='Tenant group (slug)',
  140. )
  141. tenant_id = django_filters.ModelMultipleChoiceFilter(
  142. field_name='tenants',
  143. queryset=Tenant.objects.all(),
  144. label='Tenant',
  145. )
  146. tenant = django_filters.ModelMultipleChoiceFilter(
  147. field_name='tenants__slug',
  148. queryset=Tenant.objects.all(),
  149. to_field_name='slug',
  150. label='Tenant (slug)',
  151. )
  152. class Meta:
  153. model = ConfigContext
  154. fields = ['name', 'is_active']
  155. def search(self, queryset, name, value):
  156. if not value.strip():
  157. return queryset
  158. return queryset.filter(
  159. Q(name__icontains=value) |
  160. Q(description__icontains=value) |
  161. Q(data__icontains=value)
  162. )
  163. class ObjectChangeFilter(django_filters.FilterSet):
  164. q = django_filters.CharFilter(
  165. method='search',
  166. label='Search',
  167. )
  168. time = django_filters.DateTimeFromToRangeFilter()
  169. class Meta:
  170. model = ObjectChange
  171. fields = ['user', 'user_name', 'request_id', 'action', 'changed_object_type', 'object_repr']
  172. def search(self, queryset, name, value):
  173. if not value.strip():
  174. return queryset
  175. return queryset.filter(
  176. Q(user_name__icontains=value) |
  177. Q(object_repr__icontains=value)
  178. )