def test_resolve_transformed_lookups(self): """ Check that chained field transforms are correctly resolved. eg, a 'date__year__gte' lookup on an article's 'published' timestamp. """ # Use a DateTimeField, so we can check multiple transforms. # eg, date__year__gte model_field = Article._meta.get_field('published') standard_lookups = [ 'exact', 'iexact', 'gte', 'gt', 'lte', 'lt', ] date_lookups = [ 'year', 'month', 'day', 'week_day', ] datetime_lookups = date_lookups + [ 'hour', 'minute', 'second', ] # ex: 'date__gt' for lookup in standard_lookups: field, resolved_lookup = resolve_field(model_field, LOOKUP_SEP.join(['date', lookup])) self.assertIsInstance(field, models.DateField) self.assertEqual(resolved_lookup, lookup) # ex: 'year__iexact' for part in datetime_lookups: for lookup in standard_lookups: field, resolved_lookup = resolve_field(model_field, LOOKUP_SEP.join([part, lookup])) self.assertIsInstance(field, models.IntegerField) self.assertEqual(resolved_lookup, lookup) # ex: 'date__year__lte' for part in date_lookups: for lookup in standard_lookups: field, resolved_lookup = resolve_field(model_field, LOOKUP_SEP.join(['date', part, lookup])) self.assertIsInstance(field, models.IntegerField) self.assertEqual(resolved_lookup, lookup)
def __new__(cls, name, bases, attrs): new_class = super(FilterSetMetaclass, cls).__new__(cls, name, bases, attrs) # Populate our FilterSet fields with all the possible # filters for the AllLookupsFilter field. for name, filter_ in six.iteritems(new_class.base_filters.copy()): if isinstance(filter_, filters.AllLookupsFilter): model = new_class._meta.model field = filterset.get_model_field(model, filter_.name) for lookup_type in django_filters.filters.LOOKUP_TYPES: if isinstance(field, ForeignObjectRel): f = new_class.filter_for_reverse_field(field, filter_.name) else: f = new_class.filter_for_field(field, filter_.name) f.lookup_type = lookup_type f = new_class.fix_filter_field(f) # compute filter name filter_name = name # Don't add "exact" to filter names if lookup_type != 'exact': filter_name = LOOKUP_SEP.join([name, lookup_type]) new_class.base_filters[filter_name] = f elif name not in new_class.declared_filters: new_class.base_filters[name] = new_class.fix_filter_field(filter_) return new_class
def construct_search(self, field_name): lookup = self.lookup_prefixes.get(field_name[0]) if lookup: field_name = field_name[1:] else: lookup = 'icontains' return LOOKUP_SEP.join([field_name, lookup])
def reverse_field_path(model, path): """ Create a reversed field path. E.g. Given (Order, "user__groups"), return (Group, "user__order"). Final field must be a related model, not a data field. """ reversed_path = [] parent = model pieces = path.split(LOOKUP_SEP) for piece in pieces: field, model, direct, m2m = parent._meta.get_field_by_name(piece) # skip trailing data field if extant: if len(reversed_path) == len(pieces) - 1: # final iteration try: get_model_from_relation(field) except NotRelationField: break if direct: related_name = field.related_query_name() parent = field.rel.to else: related_name = field.field.name parent = field.model reversed_path.insert(0, related_name) return (parent, LOOKUP_SEP.join(reversed_path))
def _get_clean_name(name): # Get rid of __lt, __gt etc for the currency lookup path = name.split(LOOKUP_SEP) if path[-1] in QUERY_TERMS: return LOOKUP_SEP.join(path[:-1]) else: return name
def reverse_field_path(model, path): """ Create a reversed field path. E.g. Given (Order, "user__groups"), return (Group, "user__order"). Final field must be a related model, not a data field. """ reversed_path = [] parent = model pieces = path.split(LOOKUP_SEP) for piece in pieces: field = parent._meta.get_field(piece) # skip trailing data field if extant: if len(reversed_path) == len(pieces) - 1: # final iteration try: get_model_from_relation(field) except NotRelationField: break # Field should point to another model if field.is_relation and not (field.auto_created and not field.concrete): related_name = field.related_query_name() parent = field.remote_field.model else: related_name = field.field.name parent = field.related_model reversed_path.insert(0, related_name) return (parent, LOOKUP_SEP.join(reversed_path))
def filter_related_filtersets(self, queryset): """Filter the provided ``queryset`` by the ``related_filtersets``. Override this method to change the filtering behavior across relationships. Args: queryset: The filterset's filtered queryset. Returns: The ``queryset`` filtered by its related filtersets' querysets. """ for related_name, related_filterset in self.related_filtersets.items(): # Related filtersets should only be applied if they had data. prefix = '%s%s' % (related(self, related_name), LOOKUP_SEP) if not any(value.startswith(prefix) for value in self.data): continue field = self.filters[related_name].field to_field_name = getattr(field, 'to_field_name', 'pk') or 'pk' field_name = self.filters[related_name].field_name lookup_expr = LOOKUP_SEP.join([field_name, 'in']) subquery = related_filterset.qs.values(to_field_name) queryset = queryset.filter(**{lookup_expr: subquery}) # handle disinct if self.related_filters[related_name].distinct: queryset = queryset.distinct() return queryset
def _get_ancestors_path(self, model, levels=None): """ Serves as an opposite to _get_subclasses_recurse, instead walking from the Model class up the Model's ancestry and constructing the desired select_related string backwards. """ if not issubclass(model, self.model): raise ValueError( "%r is not a subclass of %r" % (model, self.model)) ancestry = [] # should be a OneToOneField or None parent_link = model._meta.get_ancestor_link(self.model) if levels: levels -= 1 while parent_link is not None: related = parent_link.remote_field ancestry.insert(0, related.get_accessor_name()) if levels or levels is None: parent_model = related.model parent_link = parent_model._meta.get_ancestor_link( self.model) else: parent_link = None return LOOKUP_SEP.join(ancestry)
def lookups_for_transform(transform): """ Generates a list of subsequent lookup expressions for a transform. Note: Infinite transform recursion is only prevented when the subsequent and passed in transforms are the same class. For example, the ``Unaccent`` transform from ``django.contrib.postgres``. There is no cycle detection across multiple transforms. For example, ``a__b__a__b`` would continue to recurse. However, this is not currently a problem (no builtin transforms exhibit this behavior). """ lookups = [] for expr, lookup in six.iteritems(class_lookups(transform.output_field)): if issubclass(lookup, Transform): # type match indicates recursion. if type(transform) == lookup: continue sub_transform = lookup(transform) lookups += [ LOOKUP_SEP.join([expr, sub_expr]) for sub_expr in lookups_for_transform(sub_transform) ] else: lookups.append(expr) return lookups
def _get_related_path(model, base_model): """Return path suitable for select related for sublcass.""" ancestry = [] if model._meta.proxy: for parent in model._meta.get_parent_list(): if not parent._meta.proxy: model = parent break parent = model._meta.get_ancestor_link(base_model) while parent is not None: related = parent.remote_field if hasattr( parent, 'remote_field') else parent.related ancestry.insert(0, related.get_accessor_name()) if django.VERSION < (1, 8): parent_model = related.parent_model else: parent_model = related.model parent = parent_model._meta.get_ancestor_link(base_model) return LOOKUP_SEP.join(ancestry)
def _get_field(self, lookup): """ Return the Django model field for a lookup plus the remainder of the lookup, which should be the lookup type. """ model = self.model lookup_type = None # pk is not an actual field, but an alias for the implicit id field. if lookup == "pk": key = None for field in model._meta.get_fields(): if getattr(field, "primary_key", False): key = field return key, None field = None bits = lookup.split(LOOKUP_SEP) for i, bit in enumerate(bits): try: field = model._meta.get_field(bit) except FieldDoesNotExist: lookup_type = LOOKUP_SEP.join(bits[i:]) break if hasattr(field, "remote_field"): rel = getattr(field, "remote_field", None) model = getattr(rel, "model", model) return field, lookup_type
def __missing__(self, model_key): """ Generate the accessors for this model by recursively generating its children accessors and prefixing them. """ owner = self.apps.get_model(*model_key) if not issubclass(owner, self.model): raise KeyError accessors = {owner: EMPTY_ACCESSOR} with self.lock: for model in self.apps.get_models(): opts = model._meta if opts.proxy and issubclass( model, owner) and (owner._meta.proxy or opts.concrete_model is owner): accessors[model] = SubclassAccessor((), model, '') # Use .get() instead of `in` as proxy inheritance is also # stored in _meta.parents as None. elif opts.parents.get(owner): part = opts.model_name for child, ( parts, proxy, _lookup) in self[self.get_model_key(opts)].items(): accessors[child] = SubclassAccessor( (part, ) + parts, proxy, LOOKUP_SEP.join((part, ) + parts)) return accessors
def _get_ancestors_path(self, model, levels=None): """ Serves as an opposite to _get_subclasses_recurse, instead walking from the Model class up the Model's ancestry and constructing the desired select_related string backwards. """ if not issubclass(model, self.model): raise ValueError("%r is not a subclass of %r" % (model, self.model)) ancestry = [] # should be a OneToOneField or None parent_link = model._meta.get_ancestor_link(self.model) if levels: levels -= 1 while parent_link is not None: ancestry.insert(0, parent_link.related.get_accessor_name()) if levels or levels is None: if django.VERSION < (1, 8): parent_model = parent_link.related.parent_model else: parent_model = parent_link.related.model parent_link = parent_model._meta.get_ancestor_link(self.model) else: parent_link = None return LOOKUP_SEP.join(ancestry)
def build_filters(self, filters=None): if filters is None: filters = {} qs_filters = {} if getattr(self._meta, 'queryset', None) is not None: # Get the possible query terms from the current QuerySet. query_terms = self._meta.queryset.query.query_terms else: query_terms = QUERY_TERMS for filter_expr, value in filters.items(): filter_bits = filter_expr.split(LOOKUP_SEP) field_name = filter_bits.pop(0) filter_type = 'exact' if not field_name in self.fields: # It's not a field we know about. Move along citizen. continue if len(filter_bits) and filter_bits[-1] in query_terms: filter_type = filter_bits.pop() lookup_bits = self.check_filtering(field_name, filter_type, filter_bits) value = self.filter_value_to_python(value, field_name, filters, filter_expr, filter_type) db_field_name = LOOKUP_SEP.join(lookup_bits) qs_filter = "%s%s%s" % (db_field_name, LOOKUP_SEP, filter_type) qs_filters[qs_filter] = value return dict_strip_unicode_keys(qs_filters)
def related(filterset, filter_name): """ Return a related filter_name, using the filterset relationship if present. """ if not filterset.relationship: return filter_name return LOOKUP_SEP.join([filterset.relationship, filter_name])
def _expandable_fields(self): """ Get all defined expandable fields with their path within serializers """ components = [] root = self.root f = self while f is not root: if f.parent is root and isinstance(f.parent, serializers.ListSerializer): break if isinstance(f, serializers.ListSerializer): f = f.parent else: components.insert(0, f.field_name) f = f.parent nt = namedtuple("ExpandableField", ["name", "parts", "path", "replacement"]) for name, replacement in getattr(self.Meta, "expandable_fields", {}).items(): parts = components + [name] path = LOOKUP_SEP.join(parts) yield nt(name, parts, path, copy.deepcopy(replacement))
def _get_all_expandable_fields(self, parents, this, exclude): """ Recursively search for all expandable fields on class """ nt = namedtuple("ExpandableField", ["query_key", "parts", "path"]) query_key = getattr(getattr(this, "Meta", None), "expandable_query_key", "expand") for name in getattr(getattr(this, "Meta", None), "expandable_fields", {}): parts = parents + [name] path = LOOKUP_SEP.join(parts) if path in exclude: continue yield nt(query_key, parts, path) for field_name, field in this.fields.items(): if not isinstance(field, serializers.BaseSerializer): continue if isinstance(field, serializers.ListSerializer): field = field.child for i in self._get_all_expandable_fields(parents=parents + [field_name], this=field, exclude=exclude): yield i
def _get_related_path(model, base_model): """Return path suitable for select related for sublcass.""" ancestry = [] if model._meta.proxy: for parent in model._meta.get_parent_list(): if not parent._meta.proxy: model = parent break parent = model._meta.get_ancestor_link(base_model) while parent is not None: related = parent.remote_field if hasattr(parent, 'remote_field') else parent.rel ancestry.insert(0, related.get_accessor_name()) if django.VERSION < (1, 8): parent_model = related.parent_model else: parent_model = related.model parent = parent_model._meta.get_ancestor_link(base_model) return LOOKUP_SEP.join(ancestry)
def lookup_allowed(self, lookup, value): # overriden to allow filter on cell_filter fields # import django.contrib.admin.options # django.contrib.admin.options.QUERY_TERMS.update({'not':'not'}) original = DjangoModelAdmin.lookup_allowed(self, lookup, value) if original: return True model = self.model parts = lookup.split(LOOKUP_SEP) if len(parts) > 1 and parts[-1] in QUERY_TERMS: parts.pop() pk_attr_name = None for part in parts[:-1]: field, _, _, _ = model._meta.get_field_by_name(part) if hasattr(field, 'rel'): model = field.rel.to pk_attr_name = model._meta.pk.name elif isinstance(field, RelatedObject): model = field.model pk_attr_name = model._meta.pk.name else: pk_attr_name = None if pk_attr_name and len(parts) > 1 and parts[-1] == pk_attr_name: parts.pop() clean_lookup = LOOKUP_SEP.join(parts) flat_filter = [isinstance(v, tuple) and v[0] or v for v in self.list_filter] flat_filter.extend([isinstance(v, tuple) and v[0] or v for v in self.cell_filter]) return clean_lookup in self.extra_allowed_filter or clean_lookup in flat_filter
def get_filters(self): """ Build a set of filters based on the requested data. The resulting set will walk `RelatedFilter`s to recursively build the set of filters. """ # build param data for related filters: {rel: {param: value}} related_data = OrderedDict( [(name, OrderedDict()) for name in self.__class__.related_filters] ) for param, value in six.iteritems(self.data): filter_name, related_param = self.get_related_filter_param(param) # skip non lookup/related keys if filter_name is None: continue if filter_name in related_data: related_data[filter_name][related_param] = value # build the compiled set of all filters requested_filters = OrderedDict() for filter_name, f in six.iteritems(self.filters): exclude_name = '%s!' % filter_name # Add plain lookup filters if match. ie, `username__icontains` if filter_name in self.data: requested_filters[filter_name] = f # include exclusion keys if exclude_name in self.data: f = copy.deepcopy(f) f.exclude = not f.exclude requested_filters[exclude_name] = f # include filters from related subsets if isinstance(f, filters.RelatedFilter) and filter_name in related_data: subset_data = related_data[filter_name] subset_class = f.filterset.get_subset(subset_data) filterset = subset_class(data=subset_data) # modify filter names to account for relationship for related_name, related_f in six.iteritems(filterset.get_filters()): related_name = LOOKUP_SEP.join([filter_name, related_name]) related_f.name = LOOKUP_SEP.join([f.name, related_f.name]) requested_filters[related_name] = related_f return requested_filters
def prepare_polymorphic_model(sender, **kwargs): if issubclass(sender, BasePolymorphicModel): opts = sender._meta try: content_type_field_name = getattr(sender, 'CONTENT_TYPE_FIELD') except AttributeError: raise ImproperlyConfigured( '`BasePolymorphicModel` subclasses must ' 'define a `CONTENT_TYPE_FIELD`.' ) else: try: content_type_field = opts.get_field(content_type_field_name) except FieldDoesNotExist: raise ImproperlyConfigured( '`%s.%s.CONTENT_TYPE_FIELD` points to an inexistent field "%s".' % (sender.__module__, sender.__name__, content_type_field_name) ) else: if (not isinstance(content_type_field, models.ForeignKey) or content_type_field.rel.to is not ContentType): raise ImproperlyConfigured( '`%s.%s.%s` must be a `ForeignKey` to `ContentType`.' % (sender.__module__, sender.__name__, content_type_field_name) ) setattr(opts, '_subclass_accessors', {}) parents = [sender] proxy = sender if opts.proxy else None attrs = [] while parents: parent = parents.pop(0) if issubclass(parent, BasePolymorphicModel): parent_opts = parent._meta # We can't do `select_related` on multiple one-to-one # relationships on django < 1.6 # see https://code.djangoproject.com/ticket/16572 and # https://code.djangoproject.com/ticket/13781 if django.VERSION < (1, 6): lookup = LOOKUP_SEP.join(attrs[0:1]) else: lookup = LOOKUP_SEP.join(attrs) parent_opts._subclass_accessors[sender] = (tuple(attrs), proxy, lookup) if parent_opts.proxy: parents.insert(0, parent_opts.proxy_for_model) else: attrs.insert(0, model_name(parent_opts)) parents = list(parent._meta.parents.keys()) + parents
def prepare_polymorphic_model(sender, **kwargs): if issubclass(sender, BasePolymorphicModel): opts = sender._meta try: content_type_field_name = getattr(sender, 'CONTENT_TYPE_FIELD') except AttributeError: raise ImproperlyConfigured( '`BasePolymorphicModel` subclasses must ' 'define a `CONTENT_TYPE_FIELD`.') else: try: content_type_field = opts.get_field(content_type_field_name) except FieldDoesNotExist: raise ImproperlyConfigured( '`%s.%s.CONTENT_TYPE_FIELD` points to an inexistent field "%s".' % (sender.__module__, sender.__name__, content_type_field_name)) else: if (not isinstance(content_type_field, models.ForeignKey) or content_type_field.rel.to is not ContentType): raise ImproperlyConfigured( '`%s.%s.%s` must be a `ForeignKey` to `ContentType`.' % (sender.__module__, sender.__name__, content_type_field_name)) setattr(opts, '_subclass_accessors', {}) parents = [sender] proxy = sender if opts.proxy else None attrs = [] while parents: parent = parents.pop(0) if issubclass(parent, BasePolymorphicModel): parent_opts = parent._meta # We can't do `select_related` on multiple one-to-one # relationships on django < 1.6 # see https://code.djangoproject.com/ticket/16572 and # https://code.djangoproject.com/ticket/13781 if django.VERSION < (1, 6): lookup = LOOKUP_SEP.join(attrs[0:1]) else: lookup = LOOKUP_SEP.join(attrs) parent_opts._subclass_accessors[sender] = (tuple(attrs), proxy, lookup) if parent_opts.proxy: parents.insert(0, parent_opts.proxy_for_model) else: attrs.insert(0, model_name(parent_opts)) parents = list(parent._meta.parents.keys()) + parents
def join_lookup(*args): u""" Joins Django field lookup path skipping empty parts. For instance: join_lookup('foo', None, 'bar', 'bar') -> 'foo__bar__bar' join_lookup('foo') -> 'foo' join_lookup(None) -> '' """ return LOOKUP_SEP.join(a for a in args if a)
def get_filter_predicate(self, v): name = self.field_name if name and self.lookup_expr != 'exact': name = LOOKUP_SEP.join([name, self.lookup_expr]) try: return {name: getattr(v, self.field.to_field_name)} except (AttributeError, TypeError): return {name: v}
def form_apply_select_related(self, form): for related_field in self.list_select_related: splitted = related_field.split(LOOKUP_SEP) if len(splitted) > 1: field = splitted[0] related = LOOKUP_SEP.join(splitted[1:]) form.base_fields[field].queryset = form.base_fields[field].queryset.select_related(related)
def get_filter_predicate(self, v): name = self.field_name if name and self.lookup_expr != settings.DEFAULT_LOOKUP_EXPR: name = LOOKUP_SEP.join([name, self.lookup_expr]) try: return {name: getattr(v, self.field.to_field_name)} except (AttributeError, TypeError): return {name: v}
def __init__(self, **extra): self.normalization = extra.pop("normalization", []) self.weights = extra.pop("weights", []) params = tuple(extra.items())[0] self.extra = extra lookups, self.rhs = params[0].split(LOOKUP_SEP), params[1] self.srt_lookup = lookups[-1] self.lookup = LOOKUP_SEP.join(lookups[:-1]) self._do_checks()
def to_internal_value(self, data): data = super().to_internal_value(data) for i in data[:]: parts = i.split(LOOKUP_SEP) data = list( ({LOOKUP_SEP.join(parts[:i]) for i in six.moves.range(1, len(parts))} & set(self.child.choices)) | set(data) ) return data
def __init__(self, **extra): self.normalization = extra.pop('normalization', []) self.weights = extra.pop('weights', []) params = tuple(extra.items())[0] self.extra, self.rhs = extra, params[1] lookups = params[0].split(LOOKUP_SEP) self.dictionary, self.srt_lookup = lookups[-2:] self.lookup = LOOKUP_SEP.join(lookups[:-2]) self._do_checks()
def expanded_filters(self): if '_expanded_filters' not in self.__dict__: self._expanded_filters = self.base_filters.copy() for filter_name, f in self.related_filters.items(): del self._expanded_filters[filter_name] for related_name, related_f in six.iteritems(f.filterset.expanded_filters): related_name = LOOKUP_SEP.join([filter_name, related_name]) self._expanded_filters[related_name] = related_f return self._expanded_filters
def filter_method(queryset, name, value): q_obj = Q() for field in fields: lookup = LOOKUP_SEP.join([field, lookup_expr]) q_obj = q_obj & Q(**{lookup: value}) if exclude: return queryset.exclude(q_obj) else: return queryset.filter(q_obj)
def lookups_to_text(lookups): """ Givne a set of elements like [ ('a', 'b', 'c'), ('d', 'e'), ] return something like: ('a__b__c', 'd__e') """ return tuple(LOOKUP_SEP.join(lookup) for lookup in lookups)
def _construct_search(self, field_name): lookup = self.lookup_prefixes.get(field_name[0]) if not lookup: lookup = 'icontains' elif lookup == 'external_func': return None else: field_name = field_name[1:] return LOOKUP_SEP.join([field_name, lookup])
def filter_at_least(self, queryset, name, value): ''' Evaluates if a proficiency is of at least a certain level F > X > P > 0 > 0+ . . . 5 ''' value = value.replace("plus", "+") index = Proficiency.RANKING.index(value) valid_ranks = Proficiency.RANKING[index:] lookup = LOOKUP_SEP.join([name, "in"]) return queryset.filter(Q(**{lookup: valid_ranks}))
def iteritems(self, lookup_stack=None): lookup_stack = [] if lookup_stack is None else lookup_stack for component, node in self.children.viewitems(): if component == LookupComponent.EMPTY: yield (LookupComponent(LOOKUP_SEP.join(lookup_stack)), self.value) else: lookup_stack.append(component) for item in node.iteritems(lookup_stack=lookup_stack): yield item lookup_stack.pop()
def refs_aggregate(lookup_parts, aggregates): """ A little helper method to check if the lookup_parts contains references to the given aggregates set. Because the LOOKUP_SEP is contained in the default annotation names we must check each prefix of the lookup_parts for match. """ for i in range(len(lookup_parts) + 1): if LOOKUP_SEP.join(lookup_parts[0:i]) in aggregates: return True return False
def iteritems(self, lookup_stack=None): lookup_stack = [] if lookup_stack is None else lookup_stack for component, node in six.viewitems(self.children): if component == LookupComponent.EMPTY: yield (LookupComponent(LOOKUP_SEP.join(lookup_stack)), self.value) else: lookup_stack.append(component) for item in node.iteritems(lookup_stack=lookup_stack): yield item lookup_stack.pop()
def refs_expression(lookup_parts, annotations): """ Check if the lookup_parts contains references to the given annotations set. Because the LOOKUP_SEP is contained in the default annotation names, check each prefix of the lookup_parts for a match. """ for n in range(1, len(lookup_parts) + 1): level_n_lookup = LOOKUP_SEP.join(lookup_parts[0:n]) if level_n_lookup in annotations and annotations[level_n_lookup]: return annotations[level_n_lookup], lookup_parts[n:] return False, ()
def construct_search(self, field_name): lookup = self.lookup_prefixes.get(field_name[0]) if lookup: field_name = field_name[1:] else: lookup = "icontains" return LOOKUP_SEP.join([ field_name, "unaccent", lookup, ])
def _relate_Q(self, q, relate_to): if not q: return q result = q.clone() for i, child in enumerate(result.children): if isinstance(child, Q): result.children[i] = self._relate_Q(child, relate_to) else: result.children[i] = (LOOKUP_SEP.join(relate_to + [child[0]]), child[1]) return result
def lookup_allowed(self, lookup, value): ret = super(AdminExceptionFieldsFilterMixin, self).lookup_allowed(lookup, value) if not ret and self.exception_fields_filter: parts = lookup.split(LOOKUP_SEP) if len(parts) > 1 and parts[-1] in QUERY_TERMS: parts.pop() clean_lookup = LOOKUP_SEP.join(parts) if clean_lookup in self.exception_fields_filter: return True return ret
def form_apply_prefetch_related(self, form): for related_field in self.list_prefetch_related: splitted = related_field.split(LOOKUP_SEP) if len(splitted) > 1: field = splitted[0] related = LOOKUP_SEP.join(splitted[1:]) try: form.base_fields[field].queryset = form.base_fields[ field].queryset.prefetch_related(related) except KeyError: pass
def refs_aggregate(lookup_parts, aggregates): """ A helper method to check if the lookup_parts contains references to the given aggregates set. Because the LOOKUP_SEP is contained in the default annotation names we must check each prefix of the lookup_parts for a match. """ for n in range(len(lookup_parts) + 1): level_n_lookup = LOOKUP_SEP.join(lookup_parts[0:n]) if level_n_lookup in aggregates and aggregates[level_n_lookup].contains_aggregate: return aggregates[level_n_lookup], lookup_parts[n:] return False, ()
def get_queryset(self): filters = {} ancestor_lookups = [] resource_names_and_lookups = \ _get_resource_ancestors_and_lookups(flattened_resource) for resource_name, lookup in resource_names_and_lookups: urlvar_value = self.kwargs[resource_name] ancestor_lookups.append(lookup) lookup = LOOKUP_SEP.join(ancestor_lookups) filters[lookup] = urlvar_value queryset = super(NestedViewSet, self).get_queryset() queryset = queryset.filter(**filters) return queryset
def _all_filters(filterset, prefix=()): for name, field in filterset.filters.items(): if isinstance(field, FilterSet): for i in _all_filters(field, prefix=prefix + (name,)): yield i else: yield coreapi.Field( name=LOOKUP_SEP.join(prefix + (name,)), required=False, location="query", schema=_field_to_schema(field, field.default_lookup), ) if field.no_lookup: continue for lookup in field.lookups: yield coreapi.Field( name=LOOKUP_SEP.join(prefix + (name, lookup)), required=False, location="query", schema=_field_to_schema(field, lookup), )
def expand_lookup(opts, field_name): """ Utility that expands simple multilingual lookup to lookup which can be handled by DJango ORM. """ # Check if field is a translation field = _get_proxy_or_none(opts, field_name) if field is None: # Not a multilingual lookup, return return field_name # Multilingual field, add 'TranslationRelation' to lookup translation_name = '%s_%s' % (TRANSLATION_FIELD_NAME, sanitize_language_code(field.language_code)) return LOOKUP_SEP.join((translation_name, field.field_name))
def lookup_allowed(self, lookup, value): model = self.model # Check FKey lookups that are allowed, so that popups produced by # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to, # are allowed to work. for l in model._meta.related_fkey_lookups: for k, v in widgets.url_params_from_lookup_dict(l).items(): if k == lookup and v == value: return True parts = lookup.split(LOOKUP_SEP) # Last term in lookup is a query term (__exact, __startswith etc) # This term can be ignored. if len(parts) > 1 and parts[-1] in QUERY_TERMS: parts.pop() # Special case -- foo__id__exact and foo__id queries are implied # if foo has been specifically included in the lookup list; so # drop __id if it is the last part. However, first we need to find # the pk attribute name. rel_name = None for part in parts[:-1]: try: field, _, _, _ = model._meta.get_field_by_name(part) except FieldDoesNotExist: # Lookups on non-existent fields are ok, since they're ignored # later. return True if hasattr(field, 'rel'): if field.rel is None: # This property or relation doesn't exist, but it's allowed # since it's ignored in ChangeList.get_filters(). return True model = field.rel.to rel_name = field.rel.get_related_field().name elif isinstance(field, ForeignObjectRel): model = field.model rel_name = model._meta.pk.name else: rel_name = None if rel_name and len(parts) > 1 and parts[-1] == rel_name: parts.pop() if len(parts) == 1: return True clean_lookup = LOOKUP_SEP.join(parts) return ( clean_lookup in self.list_filter or clean_lookup == self.date_hierarchy)
def build_filter(self, filter_expr, branch_negated=False, current_negated=False, can_reuse=None): """ Build filters with respect to the multilingual fields. """ arg, value = filter_expr parts = arg.split(LOOKUP_SEP) field_name = parts[0] new_name = expand_lookup(self.get_meta(), field_name) filter_expr = LOOKUP_SEP.join([new_name] + parts[1:]), value return super(MultilingualQuery, self).build_filter(filter_expr, branch_negated=branch_negated, current_negated=current_negated, can_reuse=can_reuse)
def _filter_or_exclude(self, negate, *args, **kwargs): localized_kwargs = {} if self.language: for lookup in kwargs: parts = lookup.split(LOOKUP_SEP) fieldname = parts[0] localized_fieldname = fieldname if fieldname in getattr(self.model, '_i18n_attributes', ()): localized_fieldname = '%s_%s' % (fieldname, self.language) localized_lookup = LOOKUP_SEP.join([localized_fieldname] + parts[1:]) localized_kwargs[localized_lookup] = kwargs[lookup] else: localized_kwargs = kwargs return super(LocalizedQuerySet, self)._filter_or_exclude(negate, *args, **localized_kwargs)
def get_filter_name(cls, field_name, lookup_expr): """ Combine a field name and lookup expression into a usable filter name. Exact lookups are the implicit default, so "exact" is stripped from the end of the filter name. """ filter_name = LOOKUP_SEP.join([field_name, lookup_expr]) # This also works with transformed exact lookups, such as 'date__exact' _exact = LOOKUP_SEP + 'exact' if filter_name.endswith(_exact): filter_name = filter_name[:-len(_exact)] return filter_name