mirror of
https://github.com/ansible/awx.git
synced 2026-02-26 07:26:03 -03:30
Initial pass at related search fields.
This commit is contained in:
@@ -77,7 +77,7 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
|
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
|
||||||
'startswith', 'istartswith', 'endswith', 'iendswith',
|
'startswith', 'istartswith', 'endswith', 'iendswith',
|
||||||
'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'in',
|
'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'in',
|
||||||
'isnull')
|
'isnull', 'search')
|
||||||
|
|
||||||
def get_field_from_lookup(self, model, lookup):
|
def get_field_from_lookup(self, model, lookup):
|
||||||
field = None
|
field = None
|
||||||
@@ -148,6 +148,15 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
re.compile(value)
|
re.compile(value)
|
||||||
except re.error as e:
|
except re.error as e:
|
||||||
raise ValueError(e.args[0])
|
raise ValueError(e.args[0])
|
||||||
|
elif new_lookup.endswith('__search'):
|
||||||
|
related_model = getattr(field, 'related_model', None)
|
||||||
|
if not related_model:
|
||||||
|
raise ValueError('%s is not searchable' % new_lookup[:-8])
|
||||||
|
new_lookups = []
|
||||||
|
for rm_field in related_model._meta.fields:
|
||||||
|
if rm_field.name in ('username', 'first_name', 'last_name', 'email', 'name', 'description'):
|
||||||
|
new_lookups.append('{}__{}__icontains'.format(new_lookup[:-8], rm_field.name))
|
||||||
|
return value, new_lookups
|
||||||
else:
|
else:
|
||||||
value = self.value_to_python_for_field(field, value)
|
value = self.value_to_python_for_field(field, value)
|
||||||
return value, new_lookup
|
return value, new_lookup
|
||||||
@@ -160,6 +169,7 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
or_filters = []
|
or_filters = []
|
||||||
chain_filters = []
|
chain_filters = []
|
||||||
role_filters = []
|
role_filters = []
|
||||||
|
search_filters = []
|
||||||
for key, values in request.query_params.lists():
|
for key, values in request.query_params.lists():
|
||||||
if key in self.RESERVED_NAMES:
|
if key in self.RESERVED_NAMES:
|
||||||
continue
|
continue
|
||||||
@@ -181,6 +191,16 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
role_filters.append(values[0])
|
role_filters.append(values[0])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Search across related objects.
|
||||||
|
if key.endswith('__search'):
|
||||||
|
for value in values:
|
||||||
|
for search_term in force_text(value).replace(',', ' ').split():
|
||||||
|
search_value, new_keys = self.value_to_python(queryset.model, key, search_term)
|
||||||
|
assert isinstance(new_keys, list)
|
||||||
|
for new_key in new_keys:
|
||||||
|
search_filters.append((new_key, search_value))
|
||||||
|
continue
|
||||||
|
|
||||||
# Custom chain__ and or__ filters, mutually exclusive (both can
|
# Custom chain__ and or__ filters, mutually exclusive (both can
|
||||||
# precede not__).
|
# precede not__).
|
||||||
q_chain = False
|
q_chain = False
|
||||||
@@ -211,7 +231,7 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
and_filters.append((q_not, new_key, value))
|
and_filters.append((q_not, new_key, value))
|
||||||
|
|
||||||
# Now build Q objects for database query filter.
|
# Now build Q objects for database query filter.
|
||||||
if and_filters or or_filters or chain_filters or role_filters:
|
if and_filters or or_filters or chain_filters or role_filters or search_filters:
|
||||||
args = []
|
args = []
|
||||||
for n, k, v in and_filters:
|
for n, k, v in and_filters:
|
||||||
if n:
|
if n:
|
||||||
@@ -234,6 +254,11 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
else:
|
else:
|
||||||
q |= Q(**{k:v})
|
q |= Q(**{k:v})
|
||||||
args.append(q)
|
args.append(q)
|
||||||
|
if search_filters:
|
||||||
|
q = Q()
|
||||||
|
for k,v in search_filters:
|
||||||
|
q |= Q(**{k:v})
|
||||||
|
args.append(q)
|
||||||
for n,k,v in chain_filters:
|
for n,k,v in chain_filters:
|
||||||
if n:
|
if n:
|
||||||
q = ~Q(**{k:v})
|
q = ~Q(**{k:v})
|
||||||
|
|||||||
@@ -267,10 +267,25 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
|
|||||||
fields = []
|
fields = []
|
||||||
for field in self.model._meta.fields:
|
for field in self.model._meta.fields:
|
||||||
if field.name in ('username', 'first_name', 'last_name', 'email',
|
if field.name in ('username', 'first_name', 'last_name', 'email',
|
||||||
'name', 'description', 'email'):
|
'name', 'description'):
|
||||||
fields.append(field.name)
|
fields.append(field.name)
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
@property
|
||||||
|
def related_search_fields(self):
|
||||||
|
fields = []
|
||||||
|
for field in self.model._meta.fields:
|
||||||
|
if field.name.endswith('_role'):
|
||||||
|
continue
|
||||||
|
if getattr(field, 'related_model', None):
|
||||||
|
fields.append('{}__search'.format(field.name))
|
||||||
|
for rel in self.model._meta.related_objects:
|
||||||
|
name = rel.get_accessor_name()
|
||||||
|
if name.endswith('_set'):
|
||||||
|
continue
|
||||||
|
fields.append('{}__search'.format(name))
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView):
|
class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView):
|
||||||
# Base class for a list view that allows creating new objects.
|
# Base class for a list view that allows creating new objects.
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ class Metadata(metadata.SimpleMetadata):
|
|||||||
if getattr(view, 'search_fields', None):
|
if getattr(view, 'search_fields', None):
|
||||||
metadata['search_fields'] = view.search_fields
|
metadata['search_fields'] = view.search_fields
|
||||||
|
|
||||||
|
# Add related search fields if available from the view.
|
||||||
|
if getattr(view, 'related_search_fields', None):
|
||||||
|
metadata['related_search_fields'] = view.related_search_fields
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ within all designated text fields of a model.
|
|||||||
|
|
||||||
_Added in AWX 1.4_
|
_Added in AWX 1.4_
|
||||||
|
|
||||||
|
(_Added in Ansible Tower 3.1.0_) Search across related fields:
|
||||||
|
|
||||||
|
?related__search=findme
|
||||||
|
|
||||||
## Filtering
|
## Filtering
|
||||||
|
|
||||||
Any additional query string parameters may be used to filter the list of
|
Any additional query string parameters may be used to filter the list of
|
||||||
|
|||||||
Reference in New Issue
Block a user