filters.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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, TopologyMap
  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 TopologyMapFilter(django_filters.FilterSet):
  82. site_id = django_filters.ModelMultipleChoiceFilter(
  83. field_name='site',
  84. queryset=Site.objects.all(),
  85. label='Site',
  86. )
  87. site = django_filters.ModelMultipleChoiceFilter(
  88. field_name='site__slug',
  89. queryset=Site.objects.all(),
  90. to_field_name='slug',
  91. label='Site (slug)',
  92. )
  93. class Meta:
  94. model = TopologyMap
  95. fields = ['name', 'slug']
  96. class ConfigContextFilter(django_filters.FilterSet):
  97. q = django_filters.CharFilter(
  98. method='search',
  99. label='Search',
  100. )
  101. region_id = django_filters.ModelMultipleChoiceFilter(
  102. field_name='regions',
  103. queryset=Region.objects.all(),
  104. label='Region',
  105. )
  106. region = django_filters.ModelMultipleChoiceFilter(
  107. field_name='regions__slug',
  108. queryset=Region.objects.all(),
  109. to_field_name='slug',
  110. label='Region (slug)',
  111. )
  112. site_id = django_filters.ModelMultipleChoiceFilter(
  113. field_name='sites',
  114. queryset=Site.objects.all(),
  115. label='Site',
  116. )
  117. site = django_filters.ModelMultipleChoiceFilter(
  118. field_name='sites__slug',
  119. queryset=Site.objects.all(),
  120. to_field_name='slug',
  121. label='Site (slug)',
  122. )
  123. role_id = django_filters.ModelMultipleChoiceFilter(
  124. field_name='roles',
  125. queryset=DeviceRole.objects.all(),
  126. label='Role',
  127. )
  128. role = django_filters.ModelMultipleChoiceFilter(
  129. field_name='roles__slug',
  130. queryset=DeviceRole.objects.all(),
  131. to_field_name='slug',
  132. label='Role (slug)',
  133. )
  134. platform_id = django_filters.ModelMultipleChoiceFilter(
  135. field_name='platforms',
  136. queryset=Platform.objects.all(),
  137. label='Platform',
  138. )
  139. platform = django_filters.ModelMultipleChoiceFilter(
  140. field_name='platforms__slug',
  141. queryset=Platform.objects.all(),
  142. to_field_name='slug',
  143. label='Platform (slug)',
  144. )
  145. tenant_group_id = django_filters.ModelMultipleChoiceFilter(
  146. field_name='tenant_groups',
  147. queryset=TenantGroup.objects.all(),
  148. label='Tenant group',
  149. )
  150. tenant_group = django_filters.ModelMultipleChoiceFilter(
  151. field_name='tenant_groups__slug',
  152. queryset=TenantGroup.objects.all(),
  153. to_field_name='slug',
  154. label='Tenant group (slug)',
  155. )
  156. tenant_id = django_filters.ModelMultipleChoiceFilter(
  157. field_name='tenants',
  158. queryset=Tenant.objects.all(),
  159. label='Tenant',
  160. )
  161. tenant = django_filters.ModelMultipleChoiceFilter(
  162. field_name='tenants__slug',
  163. queryset=Tenant.objects.all(),
  164. to_field_name='slug',
  165. label='Tenant (slug)',
  166. )
  167. class Meta:
  168. model = ConfigContext
  169. fields = ['name', 'is_active']
  170. def search(self, queryset, name, value):
  171. if not value.strip():
  172. return queryset
  173. return queryset.filter(
  174. Q(name__icontains=value) |
  175. Q(description__icontains=value) |
  176. Q(data__icontains=value)
  177. )
  178. #
  179. # Filter for Local Config Context Data
  180. #
  181. class LocalConfigContextFilter(django_filters.FilterSet):
  182. local_context_data = django_filters.BooleanFilter(
  183. method='_local_context_data',
  184. label='Has local config context data',
  185. )
  186. def _local_context_data(self, queryset, name, value):
  187. return queryset.exclude(local_context_data__isnull=value)
  188. class ObjectChangeFilter(django_filters.FilterSet):
  189. q = django_filters.CharFilter(
  190. method='search',
  191. label='Search',
  192. )
  193. time = django_filters.DateTimeFromToRangeFilter()
  194. class Meta:
  195. model = ObjectChange
  196. fields = ['user', 'user_name', 'request_id', 'action', 'changed_object_type', 'object_repr']
  197. def search(self, queryset, name, value):
  198. if not value.strip():
  199. return queryset
  200. return queryset.filter(
  201. Q(user_name__icontains=value) |
  202. Q(object_repr__icontains=value)
  203. )