예제 #1
0
    def _get_join_paths(self, table_alias, accessor_path):
        try:
            model_class = self.queryset._get_model_class_from_table(table_alias)
        except CacheBotException:
            # this is a many to many field
            try:
                model_class = [f.rel.to for m in get_models() for f in m._meta.local_many_to_many if f.m2m_db_table() == table_alias][0]
            except:
                import ipdb; ipdb.set_trace()
            accessor_path = model_class._meta.pk.attname
        yield model_class, accessor_path

        join_from_tables = ifilter(lambda x: x[1] == table_alias, self.queryset.query.join_map.keys())
        for join_tuple in join_from_tables:
            if join_tuple[0]:
                for model_class, join_accessor_path in self._get_join_paths(join_tuple[0], join_tuple[2]):
                    if join_accessor_path == model_class._meta.pk.attname:
                        for attname, related in self.queryset._get_reverse_relations(model_class):
                            join_accessor_path = attname
                            yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
                    elif join_accessor_path.split(LOOKUP_SEP)[-1] == 'id':
                        accessor_path_split = join_accessor_path.split(LOOKUP_SEP) 
                        join_accessor_path = LOOKUP_SEP.join(accessor_path_split[:-1])
                        yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
                    elif join_accessor_path.endswith('_id'):
                        join_accessor_path = join_accessor_path[:-3]
                        yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
                    else:
                        yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
예제 #2
0
파일: queryset.py 프로젝트: bopopescu/pd
    def _get_join_paths(self, table_alias, accessor_path):
        model_class, m2m = self.queryset._get_model_class_from_table(
            table_alias)
        if m2m:
            accessor_path = model_class._meta.pk.attname

        yield model_class, accessor_path

        for join_tuple in self.queryset.query.join_map.keys():
            if join_tuple[0] and join_tuple[1] == table_alias:
                for model_class, join_accessor_path in self._get_join_paths(
                        join_tuple[0], join_tuple[2]):
                    if join_accessor_path == model_class._meta.pk.attname:
                        for attname, related in self.queryset._get_reverse_relations(
                                model_class):
                            join_accessor_path = attname
                            yield model_class, LOOKUP_SEP.join(
                                (join_accessor_path, accessor_path))
                    elif join_accessor_path.split(LOOKUP_SEP)[-1] == 'id':
                        accessor_path_split = join_accessor_path.split(
                            LOOKUP_SEP)
                        join_accessor_path = LOOKUP_SEP.join(
                            accessor_path_split[:-1])
                        yield model_class, LOOKUP_SEP.join(
                            (join_accessor_path, accessor_path))
                    elif join_accessor_path.endswith('_id'):
                        join_accessor_path = join_accessor_path[:-3]
                        yield model_class, LOOKUP_SEP.join(
                            (join_accessor_path, accessor_path))
                    else:
                        yield model_class, LOOKUP_SEP.join(
                            (join_accessor_path, accessor_path))
예제 #3
0
    def _handle_filter(self, query, item, used_aliases):
        # The generic strategy here is:
        # 1. Let Django's SQL system handle all the joins etc
        #    by adding the rangestart and rangeend filters with
        #    dummy data.
        # 2. Iterate over the where tree to pickup the spurious
        #    where clauses generated in (1), and replace them with
        #    appropriate SubWhereNode objects that have their own SQL.

        filter, value = item
        qtype = filter.rsplit(LOOKUP_SEP, 1)
        if len(qtype) == 1:
            qtype = qtype[0]
            query_first = ''
        else:
            query_first = qtype[0] + '__'
            qtype = qtype[1]

        if qtype[-3:] == '_id':
            qtype = qtype[:-3]

        if self.negated:
            qtype = REVERSE[qtype]

        new_q = Q(
            **{
                LOOKUP_SEP.join((query_first + 'rangestart', direction_map[qtype][0])):
                TOKENS[0],
                LOOKUP_SEP.join((query_first + 'rangeend', direction_map[qtype][1])):
                TOKENS[1],
            })
        query.add_q(new_q)
        self._update_where(query, query.where, value)
예제 #4
0
    def _chemicalite_filter(self, args, original_kwargs):
        chemicalite = connections[self.db].ops
        opts = self.query.get_meta()

        args = list(args)
        kwargs = {}

        for lookup, value in original_kwargs.items():
            parts = lookup.split(LOOKUP_SEP)
            if len(parts) == 1 or parts[-1] not in self.query.query_terms:
                lookup_type = 'exact'
            else:
                lookup_type = parts.pop()

            chemfield = _ChemWhereNode._check_chem_field(opts,
                                                         LOOKUP_SEP.join(parts))

            if (lookup_type in chemicalite.structure_operators and
                chemfield and isinstance(chemfield, _MoleculeField)):
                stridx_attname = chemfield.stridx_attname
                stridx_lookup_type = chemicalite.stridx_lookup[lookup_type]
                stridx_parts = (parts[:-1] +
                                [stridx_attname, stridx_lookup_type])
                stridx_lookup = LOOKUP_SEP.join(stridx_parts)
                args.extend((Q(**{lookup: value}),
                             Q(**{stridx_lookup: value})))
            else:
                kwargs[lookup] = value

        return tuple(args), kwargs
예제 #5
0
 def patch_child(self, parts, lookup, value):
     inherited_flag = ["is_%s_inherited" % parts[-1]]
     inherited_value = ["%s_value" % parts[-1]]
     lookup = [lookup] if lookup else []
     is_inherited = LOOKUP_SEP.join(parts[:-1] + inherited_flag)
     field = LOOKUP_SEP.join(parts[:-1] + inherited_value + lookup)
     print is_inherited
     print field
     return Q(**{is_inherited: False, field: value})
    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
예제 #7
0
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))
예제 #8
0
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))
예제 #9
0
파일: handler.py 프로젝트: denya/adrest
    def get_queryset(self, request, **resources):

        if self.queryset is None:
            return None

        # Make filters from URL variables or resources
        filters = dict((k, v) for k, v in resources.iteritems() if k in self.meta.model_fields)

        qs = self.queryset.filter(**filters)

        # Make filters from GET variables
        for field in request.GET.iterkeys():
            tokens = field.split(LOOKUP_SEP)
            field_name = tokens[0]

            if not field_name in self.meta.model_fields or filters.has_key(field_name):
                continue

            converter = self.model._meta.get_field(field).to_python if len(tokens) == 1 else lambda v: v
            value = map(converter, request.GET.getlist(field))

            if len(value) > 1:
                tokens.append('in')
            else:
                value = value.pop()

            try:
                qs = qs.filter(**{LOOKUP_SEP.join(tokens): value})
            except FieldError, e:
                logger.warning(e)
예제 #10
0
 def _get_reverse_relations(self, model_class):
     for related in chain(model_class._meta.get_all_related_objects(), model_class._meta.get_all_related_many_to_many_objects()):
         if related.opts.db_table in self.query.tables:
             related_name = related.get_accessor_name()
             yield related_name, related
             for attname, join_related in self._get_reverse_relations(related.model):
                 yield LOOKUP_SEP.join((related_name + '_cache', attname)), join_related
예제 #11
0
    def get_filters(self, request, **resources):
        " Make filters from GET variables. "

        filters = dict()
        for field in request.GET.iterkeys():
            tokens = field.split(LOOKUP_SEP)
            field_name = tokens[0]

            exclude = False
            if tokens[-1] == 'not':
                exclude = True
                tokens.pop()

            if not field_name in self.meta.model_fields:
                continue

            converter = self.model._meta.get_field(
                field_name).to_python if len(tokens) == 1 else lambda v: v
            value = map(converter, request.GET.getlist(field))

            if len(value) > 1:
                tokens.append('in')
            else:
                value = value.pop()

            filters[LOOKUP_SEP.join(tokens)] = (value, exclude)

        return filters
예제 #12
0
파일: queryset.py 프로젝트: bopopescu/pd
    def _get_related_models(self, parent_model):
        """
        A recursive function that looks at what tables this query spans, and
        finds that table's primary key accessor name and model class.
        """
        related_models = set()
        rev_reversemapping = dict([
            (v, k) for k, v in self._reversemapping.iteritems()
        ])
        if rev_reversemapping:
            for attname, related in self._get_reverse_relations(parent_model):
                if attname in rev_reversemapping:
                    related_models.add(
                        (rev_reversemapping[attname], related.model))

        for field in parent_model._meta.fields:
            if field.rel and field.rel.to._meta.db_table in self.query.tables and field.rel.to != parent_model:
                related_models.add((field.attname, field.rel.to))

        for attname, model_class in related_models:
            yield attname, model_class
            if attname.endswith("_id"):
                attname = attname[:-3]
                for join_attname, model_klass in self._get_related_models(
                        model_class):
                    yield LOOKUP_SEP.join((attname, join_attname)), model_klass
예제 #13
0
    def apply_sorting(self, obj_list, options=None):
        """
        Given a dictionary of options, apply some ORM-level sorting to the
        provided ``QuerySet``.

        Looks for the ``order_by`` key and handles either ascending (just the
        field name) or descending (the field name with a ``-`` in front).

        The field name should be the resource field, **NOT** model field.
        """
        if options is None:
            options = {}

        parameter_name = 'order_by'

        if not 'order_by' in options:
            if not 'sort_by' in options:
                # Nothing to alter the order. Return what we've got.
                return obj_list
            else:
                warnings.warn("'sort_by' is a deprecated parameter. Please use 'order_by' instead.")
                parameter_name = 'sort_by'

        order_by_args = []

        if hasattr(options, 'getlist'):
            order_bits = options.getlist(parameter_name)
        else:
            order_bits = options.get(parameter_name)

            if not isinstance(order_bits, (list, tuple)):
                order_bits = [order_bits]

        for order_by in order_bits:
            order_by_bits = order_by.split(LOOKUP_SEP)

            field_name = order_by_bits[0]
            order = ''

            if order_by_bits[0].startswith('-'):
                field_name = order_by_bits[0][1:]
                order = '-'

            if not field_name in self.fields:
                # It's not a field we know about. Move along citizen.
                raise InvalidSortError("No matching '%s' field for ordering on." % field_name)

            if not field_name in self._meta.ordering:
                raise InvalidSortError("The '%s' field does not allow ordering." % field_name)

            if self.fields[field_name].attribute is None:
                raise InvalidSortError("The '%s' field has no 'attribute' for ordering with." % field_name)

            order_by_args.append("%s%s" % (order, LOOKUP_SEP.join([self.fields[field_name].attribute] + order_by_bits[1:])))

        reverse = False
        if order == '-': reverse = True
        ordered_list = sorted(obj_list, key= attrgetter(field_name), reverse=reverse)
        return ordered_list
예제 #14
0
 def _get_reverse_relations(self, model_class):
     for related in chain(model_class._meta.get_all_related_objects(), model_class._meta.get_all_related_many_to_many_objects()):
         if related.opts.db_table in self.query.tables and related.model != model_class:
             related_name = related.get_accessor_name()
             yield related_name, related
             if related.model != related.parent_model:
                 for attname, join_related in self._get_reverse_relations(related.model):
                     yield LOOKUP_SEP.join((related_name + '_cache', attname)), join_related
예제 #15
0
 def patch_parent(self, parts, lookup, value):
     _parts = parts[:]
     last_field_name, model = self.traverse_models(_parts, self.model)
     lookup = [lookup] if lookup else []
     parents_name, parents_field_name = model.FIELD_INHERITANCE_MAP[last_field_name]
     parent_lookup = LOOKUP_SEP.join(parts[:-1] + [parents_name] + [parents_field_name] + lookup)
     print parent_lookup
     return Q(**{parent_lookup: value})
예제 #16
0
    def build_filters(self, filters=None):
        """
            Given a dictionary of filters, create the necessary ORM-level filters.

            Keys should be resource fields, **NOT** model fields.

            Valid values are either a list of Django filter types (i.e.
            ``['startswith', 'exact', 'lte']``), the ``ALL`` constant or the
            ``ALL_WITH_RELATIONS`` constant.
            """
        # At the declarative level:
        #     filtering = {
        #         'resource_field_name': ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
        #         'resource_field_name_3': ALL,
        #         'resource_field_name_4': ALL_WITH_RELATIONS,
        #         ...
        #     }
        # Accepts the filters as a dict. None by default, meaning no filters.
        if filters is None:
            filters = {}

        qs_filters = {}

        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.keys():
                filter_type = filter_bits.pop()

            lookup_bits = self.check_filtering(field_name, filter_type,
                                               filter_bits)

            if value in ['true', 'True', True]:
                value = True
            elif value in ['false', 'False', False]:
                value = False
            elif value in ('nil', 'none', 'None', None):
                value = None

            # Split on ',' if not empty string and either an in or range filter.
            if filter_type in ('in', 'range') and len(value):
                if hasattr(filters, 'getlist'):
                    value = filters.getlist(filter_expr)
                else:
                    value = value.split(',')

            redis_model_field_name = LOOKUP_SEP.join(lookup_bits)
            qs_filter = "%s%s%s" % (redis_model_field_name, LOOKUP_SEP,
                                    filter_type)
            qs_filters[qs_filter] = value

        return dict_strip_unicode_keys(qs_filters)
예제 #17
0
    def build_filters(self, filters=None):
        """
        Given a dictionary of filters, create the necessary ORM-level filters.

        Keys should be resource fields, **NOT** model fields.

        Valid values are either a list of Django filter types (i.e.
        ``['startswith', 'exact', 'lte']``), the ``ALL`` constant or the
        ``ALL_WITH_RELATIONS`` constant.
        """
        # At the declarative level:
        #     filtering = {
        #         'resource_field_name': ['exact', 'startswith', 'endswith', 'contains'],
        #         'resource_field_name_2': ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
        #         'resource_field_name_3': ALL,
        #         'resource_field_name_4': ALL_WITH_RELATIONS,
        #         ...
        #     }
        # Accepts the filters as a dict. None by default, meaning no filters.
        if filters is None:
            filters = {}

        qs_filters = {}

        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.keys():
                filter_type = filter_bits.pop()

            lookup_bits = self.check_filtering(field_name, filter_type, filter_bits)

            if value in ['true', 'True', True]:
                value = True
            elif value in ['false', 'False', False]:
                value = False
            elif value in ('nil', 'none', 'None', None):
                value = None

            # Split on ',' if not empty string and either an in or range filter.
            if filter_type in ('in', 'range') and len(value):
                if hasattr(filters, 'getlist'):
                    value = filters.getlist(filter_expr)
                else:
                    value = value.split(',')

            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)
예제 #18
0
    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 = super(IModelAdmin, self).lookup_allowed(lookup, value)
        if original:
            return True
        parts = lookup.split(LOOKUP_SEP)
        if len(parts) > 1 and parts[-1] in QUERY_TERMS:
            parts.pop()
        clean_lookup = LOOKUP_SEP.join(parts)
        return clean_lookup in self.extra_allowed_filter
예제 #19
0
    def _get_join_paths(self, table_alias, accessor_path):
        model_class, m2m = self.queryset._get_model_class_from_table(table_alias) 
        if m2m: 
            accessor_path = model_class._meta.pk.attname
            
        yield model_class, accessor_path

        for join_tuple in self.queryset.query.join_map.keys():
            if join_tuple[0] and join_tuple[1] == table_alias:
                for model_class, join_accessor_path in self._get_join_paths(join_tuple[0], join_tuple[2]):
                    if join_accessor_path == model_class._meta.pk.attname:
                        for attname, related in self.queryset._get_reverse_relations(model_class):
                            join_accessor_path = attname
                            yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
                    elif join_accessor_path.split(LOOKUP_SEP)[-1] == 'id':
                        accessor_path_split = join_accessor_path.split(LOOKUP_SEP) 
                        join_accessor_path = LOOKUP_SEP.join(accessor_path_split[:-1])
                        yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
                    elif join_accessor_path.endswith('_id'):
                        join_accessor_path = join_accessor_path[:-3]
                        yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
                    else:
                        yield model_class, LOOKUP_SEP.join((join_accessor_path, accessor_path))
예제 #20
0
def passes_through(base_model, path, target, addition=''):
    if not path:
        return False
    if base_model == target and addition:
        return addition
    elif base_model == target and not addition:
        return True
    steps = path.split(LOOKUP_SEP)
    model = base_model
    new_path = ()
    for step in steps:
        model = next_model(step, model)
        new_path += (step, )
        if model == target:
            return LOOKUP_SEP.join(new_path + (addition, ))
    return ''
예제 #21
0
def _expand_money_params(kwargs):
    from moneyed import Money
    from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS
    to_append = {}
    for name, value in kwargs.items():
        if isinstance(value, Money):
            # Get rid of __lt, __gt etc for the currency lookup
            path = name.split(LOOKUP_SEP)
            if QUERY_TERMS.has_key(path[-1]):
                clean_name = LOOKUP_SEP.join(path[:-1])
            else:
                clean_name = name

            to_append[name] = value.amount
            to_append[currency_field_name(clean_name)] = smart_unicode(value.currency)
    kwargs.update(to_append)
    return kwargs
예제 #22
0
파일: options.py 프로젝트: lapbay/milan
	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 specificially included in the lookup list; so
		# drop __id if it is the last part. However, first we need to find
		# the pk attribute name.
		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()

		try:
			self.model._meta.get_field_by_name(parts[0])
		except FieldDoesNotExist:
			# Lookups on non-existants fields are ok, since they're ignored
			# later.
			return True
		else:
			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
예제 #23
0
    def build_filters(self, filters=None):
        # Override the filters so we can stop Tastypie silently ignoring
        # invalid filters. That will cause an invalid filtering just to return
        # lots of results.
        if filters is None:
            filters = {}
        qs_filters = {}

        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:
                # Don't just ignore this. Tell the world. Shame I have to
                # override all this, just to do this.
                raise InvalidFilterError('Not a valid filtering field: %s'
                                         % field_name)

            if len(filter_bits) and filter_bits[-1] in QUERY_TERMS.keys():
                filter_type = filter_bits.pop()

            lookup_bits = self.check_filtering(field_name, filter_type,
                                               filter_bits)

            if value in ['true', 'True', True]:
                value = True
            elif value in ['false', 'False', False]:
                value = False
            elif value in ('nil', 'none', 'None', None):
                value = None

            # Split on ',' if not empty string and either an in or range
            # filter.
            if filter_type in ('in', 'range') and len(value):
                if hasattr(filters, 'getlist'):
                    value = filters.getlist(filter_expr)
                else:
                    value = value.split(',')

            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)
예제 #24
0
    def build_filters(self, filters=None):
        # Override the filters so we can stop Tastypie silently ignoring
        # invalid filters. That will cause an invalid filtering just to return
        # lots of results.
        if filters is None:
            filters = {}
        qs_filters = {}

        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:
                # Don't just ignore this. Tell the world. Shame I have to
                # override all this, just to do this.
                raise InvalidFilterError('Not a valid filtering field: %s' %
                                         field_name)

            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)

            if value in ['true', 'True', True]:
                value = True
            elif value in ['false', 'False', False]:
                value = False
            elif value in ('nil', 'none', 'None', None):
                value = None

            # Split on ',' if not empty string and either an in or range
            # filter.
            if filter_type in ('in', 'range') and len(value):
                if hasattr(filters, 'getlist'):
                    value = filters.getlist(filter_expr)
                else:
                    value = value.split(',')

            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)
예제 #25
0
def _expand_money_params(kwargs):
    from moneyed import Money
    from django.db.models.sql.constants import LOOKUP_SEP, QUERY_TERMS
    to_append = {}
    for name, value in kwargs.items():
        if isinstance(value, Money):
            # Get rid of __lt, __gt etc for the currency lookup
            path = name.split(LOOKUP_SEP)
            if QUERY_TERMS.has_key(path[-1]):
                clean_name = LOOKUP_SEP.join(path[:-1])
            else:
                clean_name = name

            to_append[name] = value.amount
            to_append[currency_field_name(clean_name)] = smart_unicode(
                value.currency)
    kwargs.update(to_append)
    return kwargs
예제 #26
0
def path_v2(model, *conditions):
    '''
Returns a filter of all objects of type model, subject to the list of conditions. 

The first argument, model, must be a model. It cannot be an instance of a model, or any other object.

The list of conditions can be arbitrarily long, but must consist entirely of instances of models.

The function uses path_v1() to find the forward path from model to conditions[i] (for all i), and then applies the appropriate filter.

If there is more than one path from model to conditions[i], the function asks the user (via the raw_input() function) which path(s) to filter on.
    '''
    if not (isclass(model) and issubclass(model, Model)):
        raise TypeError("path_v2 argument 1 must be a model")
    if not len(conditions):
        return model.objects.all()
    models = [None for i in range(len(conditions))]
    for i in range(len(conditions)):
        if not isinstance(conditions[i], Model):
            raise TypeError(
                "path_v2 argument 2 must be a list of instances of models")
        elif isinstance(conditions[i], model):
            raise TypeError(
                "path_v2 argument 2 must not contain any instances of argument 1"
            )
        models[i] = type(conditions[i])
    kwargs = {}
    paths = None
    repeat = None
    use = None
    for i in range(len(conditions)):
        paths = path_v1(model, models[i])
        for (path, _, _) in paths:
            repeat = True
            path = LOOKUP_SEP.join(path)
            while repeat:
                use = raw_input("Include the path " + path +
                                " as a condition ([y]/n)? ")
                repeat = False
                if use.lower() == 'y' or use.lower() == 'yes' or not use:
                    kwargs[path] = conditions[i]
                elif not (use.lower() == 'n' or use.lower() == 'no'):
                    repeat = True
    return model.objects.filter(**kwargs)
예제 #27
0
def filters_for_model(model, fields=None, exclude=None, filter_for_field=None):
    field_dict = SortedDict()
    opts = model._meta
    if fields is None:
        fields = [f.name for f in sorted(opts.fields + opts.many_to_many)]
    for f in fields:
        if exclude is not None and f in exclude:
            continue
        if f.split(LOOKUP_SEP)[-1] in QUERY_TERMS.keys():
            lookup_type = f.split(LOOKUP_SEP)[-1]
            f = LOOKUP_SEP.join(f.split(LOOKUP_SEP)[:-1])
        else:
            lookup_type = None
        field = get_model_field(model, f)
        if field is None:
            field_dict[f] = None
            continue
        filter_ = filter_for_field(field, f, lookup_type)
        if filter_:
            field_dict[f] = filter_
    return field_dict
예제 #28
0
    def _get_related_models(self, parent_model):
        """
        A recursive function that looks at what tables this query spans, and
        finds that table's primary key accessor name and model class.
        """
        related_models = set()
        rev_reversemapping = dict([(v,k) for k,v in self._reversemapping.iteritems()])
        if rev_reversemapping:
            for attname, related in self._get_reverse_relations(parent_model):
                related_models.add((rev_reversemapping[attname], related.model))

        for field in parent_model._meta.fields:
            if field.rel and field.rel.to._meta.db_table in self.query.tables:
                related_models.add((field.attname, field.rel.to))
        
        for attname, model_class in related_models:
            yield attname, model_class
            if attname.endswith("_id"):
                attname = attname[:-3]
                for join_attname, model_class in self._get_related_models(model_class):
                    yield LOOKUP_SEP.join((attname,join_attname)), model_class
예제 #29
0
def filters_for_model(model, fields=None, exclude=None, filter_for_field=None):
    field_dict = SortedDict()
    opts = model._meta
    if fields is None:
        fields = [f.name for f in sorted(opts.fields + opts.many_to_many)]
    for f in fields:
        if exclude is not None and f in exclude:
            continue
        if f.split(LOOKUP_SEP)[-1] in QUERY_TERMS.keys():
            lookup_type = f.split(LOOKUP_SEP)[-1]
            f = LOOKUP_SEP.join(f.split(LOOKUP_SEP)[:-1])
        else:
            lookup_type = None
        field = get_model_field(model, f)
        if field is None:
            field_dict[f] = None
            continue
        filter_ = filter_for_field(field, f, lookup_type)
        if filter_:
            field_dict[f] = filter_
    return field_dict
예제 #30
0
 def apply_sorting(self, obj_list, options=None):
     """
     Given a dictionary of options, apply some ORM-level sorting to the
     provided ``QuerySet``.
     
     Looks for the ``sort_by`` key and handles either ascending (just the
     field name) or descending (the field name with a ``-`` in front).
     
     The field name should be the resource field, **NOT** model field.
     """
     if options is None:
         options = {}
     
     if not 'sort_by' in options:
         # Nothing to alter the sort order. Return what we've got.
         return obj_list
     
     sort_by_bits = options['sort_by'].split(LOOKUP_SEP)
     field_name = sort_by_bits[0]
     order = ''
     
     if sort_by_bits[0].startswith('-'):
         field_name = sort_by_bits[0][1:]
         order = '-'
     
     if not field_name in self.fields:
         # It's not a field we know about. Move along citizen.
         raise InvalidSortError("No matching '%s' field for ordering on." % field_name)
     
     if not field_name in self._meta.ordering:
         raise InvalidSortError("The '%s' field does not allow ordering." % field_name)
     
     if self.fields[field_name].attribute is None:
         raise InvalidSortError("The '%s' field has no 'attribute' for ordering with." % field_name)
     
     sort_expr = "%s%s" % (order, LOOKUP_SEP.join([self.fields[field_name].attribute] + sort_by_bits[1:]))
     return obj_list.order_by(sort_expr)
예제 #31
0
def process_queryset(queryset,display_fields=None):
	"""
	This is used in the custom_view below, but its de-coupled so it can be used
	programatically as well. Simply pass in a queryset and a list of relations to display
	and viola.

	Relations look like: ['address__zip','contact__date']
	"""

	display_fields = display_fields or []
	extra_select_kwargs = {}
	select_related = []
	used_routes = []
	distinct = True
	for i in display_fields:
		if i in queryset.query.aggregates or i in queryset.query.extra:
			continue # Want below check to work only for relations, excluding aggregates.

		select_related_token = i
		if LOOKUP_SEP in i:
			"""
			Since select_related() is rather flexible about what it receives
			(ignoring things it doesn't like), we'll just haphazardly pass
			all filter and display fields in for now.
			"""
			select_related_token = i.split(LOOKUP_SEP)
			select_related_token.pop() # get rid of the field name
			select_related_token = LOOKUP_SEP.join(select_related_token)

			"""
			Here we remove distinct status for queries which have reverse relations
			and possibly numerous results per original record
			"""
			if distinct and is_reverse_related(i,queryset.model):
				distinct = False

			primary_model = queryset.model
			join_route = i
			if len(i.split(LOOKUP_SEP)) > 2:
				second_to_last = LOOKUP_SEP.join(i.split(LOOKUP_SEP)[0:-1])
				join_route = LOOKUP_SEP.join(i.split(LOOKUP_SEP)[-2:])
				primary_model = get_closest_relation(queryset.model,second_to_last)[0]

			primary_table = primary_model._meta.db_table
			if primary_table in queryset.query.table_map:
				primary_table = queryset.query.table_map[primary_table][-1] # Will the last one always work?

			join_model, join_field, join_name = get_closest_relation(primary_model,join_route)
			join_table = join_model._meta.db_table
			try:
				join_table = queryset.query.table_map[join_table][-1]
				queryset = queryset.extra(select={i: '%s.%s' % (join_table,join_field.column)})
			except KeyError:
				"""
				Design decision. This will work fine if the ModelAdmin does the displaying of
				related objects for us. At this time, Django doesn't. We have a patch in place
				but aren't using it.

				for now we just need the join column between the primary table and the join table.
				"""
				join_table = join_model._meta.db_table
				join_column = "id" # Always this for now.

				for field_name in primary_model._meta.get_all_field_names():
					from django.db import models
					try:
						field = primary_model._meta.get_field(field_name)
						if (isinstance(field,models.OneToOneField) or isinstance(field,models.ForeignKey)) and \
								field.rel.to == join_model:

							# 2 foreignkeys on the same model issue
							# https://code.djangoproject.com/ticket/12890
							if field_name not in select_related_token:
								continue

							whereclause = '%s.%s=%s.%s' % (join_table,join_column,primary_table,field.column)
							if not (select_related_token,join_table) in used_routes:
								queryset = queryset.extra(select={i: '%s.%s' % (join_table,join_field.column)},\
										tables=[join_table],where=[whereclause])
							else:
								queryset = queryset.extra(select={i: '%s.%s' % (join_table,join_field.column)},where=[whereclause])

					except models.FieldDoesNotExist:
						pass

				if not (select_related_token,join_table) in used_routes:
					used_routes.append((select_related_token, join_table))

		if not select_related_token in select_related:
			select_related.append(select_related_token)

	if select_related:
		queryset = queryset.select_related(*select_related)

	if distinct:
		queryset = queryset.distinct()

	return queryset
예제 #32
0
def display_list_redux(query_class,_model_class=None,inclusions=None,
		model_exclusions=None,_relation_list=None):
	"""
	User Args:
		inclusions: A list of string-module relationships we want to be allowed to
				 choose from, if not passed in, it means we want all the relations

		query_class: The class at the top of the tree hierarchy, essentially what
					we are reporting on.

	Function Args:
		_model_class: As we progress through the tree, we need to keep track of what model we are on.

		_relation_list: What our function returns, but we need to pass it through so it can find
			 it's way to the top... may be able to change this though

	function return: A list of tuples, where each tuple represents
		(string-module relationship, human-readable-value)
		ex. [
				('first_name', 'Consumer :: First Name'),
				('address__zip',	'Consmer :: Address :: Zip'),
				('pwi__refer_date', 'Consumer :: PWI :: Referral Date')
			]
	"""
	_relation_list = _relation_list or []
	model_exclusions = model_exclusions or []
	inclusions = inclusions or []

	# if no model class is passed, then we are at the beginning or "base_class"
	query_aggregates = None
	if query_class.__class__ == query.QuerySet:
		query_aggregates = query_class.query.aggregates
		query_class = query_class.model

	_model_class = _model_class or query_class

	model_exclusions.append(_model_class._meta.module_name)

	current_inclusions = [r.split(LOOKUP_SEP,1)[0] for r in inclusions] # these are the ONLY fields and relations to be returned

	# Non-relational fields are easy and just get appended to the list as is pretty much
	non_relation_fields = [f for f in _model_class._meta.fields]

	# Now handle the relations
	# Get the forward ones
	relations = [(f.rel.to, f.name, f.verbose_name.lower()) for f in _model_class._meta.fields if \
			hasattr(f.rel, "to") and \
			f.rel.to._meta.module_name not in model_exclusions]

	# and grab our backward ones
	relations.extend([(r.model, r.field.related_query_name(), r.model._meta.verbose_name) for r in _model_class._meta.get_all_related_objects() if \
			r.model._meta.module_name not in model_exclusions])

	# We have to handle the inclusion list separately because if there isn't one, we don't want to filter over nothing
	if current_inclusions:
		non_relation_fields = [f for f in non_relation_fields if f.name in current_inclusions]
		relations = [r for r in relations if r[0]._meta.module_name in current_inclusions] # r == (model, model.verbose_name)

	# At this point we are finally adding our fields to the tuple list
	if query_aggregates:
		[_relation_list.append(( q, q )) for q in query_aggregates.keys()]

	for field in non_relation_fields:
		if _model_class != query_class:
			_relation_list.append((
				field.name,
				field.verbose_name.lower()
				#' :: '.join([_model_class._meta.verbose_name.lower(), field.verbose_name.lower()])
			))
		else: _relation_list.append(( field.name, ' :: '.join([_model_class._meta.module_name, field.verbose_name.lower()]) ))

	## Recursion happens at this point, we are basically going down one tree / relations at a time before we do another one
	# so taking consumer... it will do consumer->address->zip and then it will do consumer->emergency_contact and
	# then whatever backward relations
	for relation in relations:
		# prepare the inclusion for the next recursive call by chopping off all relations that match the one in our loop
		relation_inclusions = [name.split(LOOKUP_SEP, 1)[1] for name in inclusions if LOOKUP_SEP in name and name.split(LOOKUP_SEP,1)[0] == relation[1]]

		if not current_inclusions:
			return _relation_list

		# recurse
		###
		# we use copy.deepcopy on model_exclusions because we don't want a global list of exclusions everytime it adds a new one,
		# just the ones down this tree
		relation_pair_list = display_list_redux(query_class,_model_class=relation[0],\
			inclusions=relation_inclusions,model_exclusions=copy.deepcopy(model_exclusions),\
			)

		# build the module-relation and human-readable string
		###
		# We check if _model_class != query_class because we have a case here where once we hit the top of the tree,
		# then we don't want to append the query_class to the module-relation
		if _model_class != query_class:
			_relation_list.extend([
				(LOOKUP_SEP.join([relation[1], relation_pair[0]]),
				' :: '.join([relation[2], relation_pair[1]]))
				for relation_pair in relation_pair_list
			])
		else:
			_relation_list.extend([
				(LOOKUP_SEP.join([relation[1], relation_pair[0]]),
				' :: '.join([_model_class._meta.module_name, relation[2], relation_pair[1]])) \
				for relation_pair in relation_pair_list
			])

	# go back up the recursion tree now
	return _relation_list
예제 #33
0
def display_list(query_class,_model_class=None,inclusions=None,exclusions=None,depth=None,\
		model_exclusions=None,_max_depth=None,_relation_list=None):
	"""
	User Args:
		depth: how far our relation follows, we want to make sure to include forward then backward relations, as well.
			ex. depth=1, consumer->field, consumer->disability_primary->field, consumer->address->field, consumer<-goal<-field, consumer<-pwi<-field

		inclusions: A list of string-module relationships we want to be allowed to choose from, if not passed in, it means we want all the relations

		exclusions: A list of string-module relationships to not follow, it could be a field or whole relation.

		query_class: The class at the top of the tree hierarchy, essentially what we are reporting on.

	Function Args:
		_model_class: As we progress through the tree, we need to keep track of what model we are on.

		_max_depth: Takes the depth, and depth starts as a counter from 0, just easier to read this way

		_relation_list: What our function returns, but we need to pass it through so it can find it's way to the top... may be able to change this though

	function return: A list of tuples, where each tuple represents (string-module relationship, human-readable-value)
		ex. [
				('first_name', 'Consumer :: First Name'),
				('address__zip',	'Consmer :: Address :: Zip'),
				('pwi__refer_date', 'Consumer :: PWI :: Referral Date')
			]
	"""
	_relation_list = _relation_list or []
	exclusions = exclusions or []
	model_exclusions = model_exclusions or []
	inclusions = inclusions or []

	# if no model class is passed, then we are at the beginning or "base_class"
	query_aggregates = None
	if query_class.__class__ == query.QuerySet:
		query_aggregates = query_class.query.aggregates
		query_class = query_class.model

	_model_class = _model_class or query_class

	## less typing when calling the function, we use depth to set _max_depth from the first call, and use _max_depth henceforth.
	# thus depth just keeps track of what level we are on
	if not _max_depth:
		_max_depth = depth
		depth = 0

	exclusions.extend(['logentry', 'message', 'id']) # these are always excluded fields

	## We dont want our backward relations in the next recursive step, so we add it to our exclusions.
	# as well as the base class.
	exclusions.append(_model_class._meta.module_name)
	model_exclusions.append(_model_class._meta.module_name)

	current_inclusions = [r.split(LOOKUP_SEP,1)[0] for r in inclusions] # these are the ONLY fields and relations to be returned
	current_exclusions = [r for r in exclusions if LOOKUP_SEP not in r] # these are the fields / relations we don't want to show up


	# Non-relational fields are easy and just get appended to the list as is pretty much
	non_relation_fields = [f for f in _model_class._meta.fields if \
			f.name not in current_exclusions and
			f.name not in model_exclusions]

	# Now handle the relations
	# Get the forward ones
	relations = [(f.rel.to, f.name, f.verbose_name.lower()) for f in _model_class._meta.fields if \
			hasattr(f.rel, "to") and \
			f.name not in current_exclusions and
			f.rel.to._meta.module_name not in model_exclusions]

	# and grab our backward ones
	relations.extend([(r.model, r.field.related_query_name(), r.model._meta.verbose_name) for r in _model_class._meta.get_all_related_objects() if \
			r.model._meta.module_name not in current_exclusions and
			r.model._meta.module_name not in model_exclusions])

	# We have to handle the inclusion list separately because if there isn't one, we don't want to filter over nothing
	if current_inclusions:
		non_relation_fields = [f for f in non_relation_fields if f.name in current_inclusions]
		relations = [r for r in relations if r[0]._meta.module_name in current_inclusions] # r == (model, model.verbose_name)

	# At this point we are finally adding our fields to the tuple list
	if query_aggregates:
		[_relation_list.append(( q, q )) for q in query_aggregates.keys()]

	for field in non_relation_fields:
		if _model_class != query_class:
			_relation_list.append((
				field.name,
				field.verbose_name.lower()
				#' :: '.join([_model_class._meta.verbose_name.lower(), field.verbose_name.lower()])
			))
		else: _relation_list.append(( field.name, ' :: '.join([_model_class._meta.module_name, field.verbose_name.lower()]) ))

	## Recursion happens at this point, we are basically going down one tree / relations at a time before we do another one
	# so taking consumer... it will do consumer->address->zip and then it will do consumer->emergency_contact and
	# then whatever backward relations
	for relation in relations:
		# prepare the inclusion/exclusion for the next recursive call by chopping off all relations that match the one in our loop
		relation_inclusions = [name.split(LOOKUP_SEP, 1)[1] for name in inclusions if LOOKUP_SEP in name and name.split(LOOKUP_SEP,1)[0] == relation[1]]
		relation_exclusions = [name.split(LOOKUP_SEP, 1)[1] for name in exclusions if LOOKUP_SEP in name and name.split(LOOKUP_SEP,1)[0] == relation[1]]

		if current_inclusions: pass # if we have inclusions we want to continue with, don't return this tree yet
		elif depth >= _max_depth: return _relation_list # return this tree

		# if we have reached a star in the exclusions, then skip the rest of this relation
		if [True for r in relation_exclusions if '*' in r.split(LOOKUP_SEP, 1)[0]]:
			continue

		# recurse
		###
		# we use copy.deepcopy on model_exclusions because we don't want a global list of exclusions everytime it adds a new one,
		# just the ones down this tree
		relation_pair_list = display_list(query_class,_model_class=relation[0],\
			inclusions=relation_inclusions,exclusions=relation_exclusions,model_exclusions=copy.deepcopy(model_exclusions),\
			depth=depth + 1,_max_depth=_max_depth)

		# build the module-relation and human-readable string
		###
		# We check if _model_class != query_class because we have a case here where once we hit the top of the tree,
		# then we don't want to append the query_class to the module-relation
		if _model_class != query_class:
			_relation_list.extend([
				(LOOKUP_SEP.join([relation[1], relation_pair[0]]),
				' :: '.join([relation[2], relation_pair[1]]))
				for relation_pair in relation_pair_list
			])
		else:
			_relation_list.extend([
				(LOOKUP_SEP.join([relation[1], relation_pair[0]]),
				' :: '.join([_model_class._meta.module_name, relation[2], relation_pair[1]])) \
				for relation_pair in relation_pair_list
			])

	# go back up the recursion tree now
	return _relation_list
예제 #34
0
def clip_lookup(lookup):
    return LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[1:]) or None
예제 #35
0
def deep_prefetch_related_objects(objects, lookups):
    """
    Helper function for prefetch_related functionality.

    Populates prefetched objects caches for a list of results
    from a QuerySet.

    Differs from :meth:`django.db.models.query.prefetch_related_objects`
    in that that it can prefetch "non-strict" lookups through GFK.

    :param objects: result cache of base queryset.
    :param lookups: list with lookups.
    """

    #How it works
    #------------
    #   - Since data is not of same type - "unit" of data is a model instance
    #     (object), not QuerySet.
    #   - Goals that I was tried to achieve:
    #       - Prefetching 'non-strict' lookups.
    #       - Absence of doubled queries to DB, other redundancy in processing
    #       - and infinite recursions.
    #       - Simplicity of design in comparison with original
    #         `django.db.models.query.prefetch_related_objects`
    #   - Sets are used to avoid duplicates.
    #   - Python's sets distinguish objects by value. But task require
    #     to distinguish objects by id, not by value, because two objects
    #     that are same by value, might be included in two different
    #     querysets, and both of them must be populated with cache.
    #   - BUT fetched cache data for object are distinguished by value.
    #     Rationale is to distinguish unique queries and prevent their
    #     repeated execution. We don't care about "id" of queries, we care
    #     about their value. Composite identifier for query is
    #     object and lookup for which query being constructed.
    #     Only one query exists for one object (by value) and lookup part.
    #     In function there slightly more complex structure than
    #     `obj -> lookup` used for storing data for traversed lookups
    #     (``seen``) - that's done to reduce redundancy in data, but
    #     "primary key" for stroed data is `obj -> lookup`.
    #   - Data flows through `buffer`, during processing.
    #     Objects discovered while traversing DB structure are being added
    #     and processed objects are removed.
    #todo beauty and refactoring
    if len(objects) == 0:
        return  # nothing to do

    #lookup -> model -> {(id(obj), obj), ...}
    #id(obj) - because objects must be distinguished by id.
    #DefaultOrederedDict because order of lookups matter,
    #see tests.LookupOrderingTest.test_order of Django test suite.
    buffer = DefaultOrderedDict(lambda: defaultdict(set))

    update_buffer(buffer, objects, reversed(lookups))
    seen = tree()  # model -> attr ->
    #              single     -> bool
    #              cache_name -> str
    #              cache      ->
    #                         obj -> [cache]
    while True:
        try:
            lookup = last(buffer.keys())
        except TypeError:
            break
        try:
            model, current = buffer[lookup].popitem()
        except KeyError:
            del buffer[lookup]
            continue

        attr = lookup.split(LOOKUP_SEP)[0]

        sample = current.pop()
        current.add(sample)
        _, object = sample
        prefetcher, _, attr_found, _ = get_prefetcher(object, attr)

        #Lookup is not valid for that object, it must be skipped.
        #No exception, because data it is that data is of diffrerent types,
        #so - such situation is normal.
        if not attr_found:
            continue

        if LOOKUP_SEP not in lookup and prefetcher is None:
            raise ValueError("'%s' does not resolve to a item that supports "
                             "prefetching - this is an invalid parameter to "
                             "prefetch_related()." % lookup)

        if prefetcher is None:
            clipped = clip_lookup(lookup)
            if clipped:
                update_buffer(
                    buffer,
                    filter(is_not_none,
                           [getattr(o, attr) for _, o in current]), [clipped])
            continue

        to_discard = set()
        for e in current:  # no need to query for already prefetched data
            obj = e[1]
            obj_model = obj.__class__
            p, d, _, is_fetched = get_prefetcher(obj, attr)
            cache = None
            if is_fetched:  # case of Django internal cache
                single, cache_name = get_info(p, d)
                cache = get_cache(obj, single, cache_name, attr)
                single, cache_name = get_info(p, d)
                update_seen(seen, model, attr, single, cache_name, obj, cache)
                to_discard.add(e)
            elif (model in seen and  # case of `seen`
                  attr in seen[obj_model] and obj
                  in seen[obj_model][attr]['cache']):
                single = seen[obj_model][attr]['single']
                cache_name = seen[obj_model][attr]['cache_name']
                cache = seen[obj_model][attr]['cache'][obj]
                to_discard.add(e)
                set_cache(obj, single, cache, cache_name, attr)
            if cache is not None and len(cache) != 0:  # if data was cached
                clipped = clip_lookup(lookup)  # it still must get
                if clipped:  # into `buffer`.
                    update_buffer(buffer, cache, [clipped])

        current -= to_discard

        if current:
            prefetch_qs, rel_attr_fn, cur_attr_fn, single, cache_name =       \
            prefetcher.get_prefetch_query_set(
                map(itemgetter(1), current)
            )

            #prefetch lookups from prefetch queries are merged into processing.
            additional_lookups = getattr(prefetch_qs,
                                         '_prefetch_related_lookups', [])

            if additional_lookups:
                setattr(prefetch_qs, '_prefetch_related_lookups', [])
            discovered = list(prefetch_qs)
            lookups_for_discovered = additional_lookups
            clipped_lookup = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[1:])
            if len(clipped_lookup) > 0:
                lookups_for_discovered = chain([clipped_lookup],
                                               additional_lookups)
            if lookups_for_discovered:
                update_buffer(buffer, discovered,
                              reversed(list(lookups_for_discovered)))

            rel_to_cur = defaultdict(list)

            for obj in discovered:
                val = rel_attr_fn(obj)
                rel_to_cur[val].append(obj)
            for pair in current:  # queried data is set up to objects
                obj = pair[1]
                val = cur_attr_fn(obj)
                cache = rel_to_cur.get(val, [])
                update_seen(seen, model, attr, single, cache_name, obj, cache)
                set_cache(obj, single, cache, cache_name, attr)
예제 #36
0
def process_queryset(queryset, display_fields=None):
    """
	This is used in the custom_view below, but its de-coupled so it can be used
	programatically as well. Simply pass in a queryset and a list of relations to display
	and viola.

	Relations look like: ['address__zip','contact__date']
	"""

    display_fields = display_fields or []
    extra_select_kwargs = {}
    select_related = []
    used_routes = []
    distinct = True
    for i in display_fields:
        if i in queryset.query.aggregates or i in queryset.query.extra:
            continue  # Want below check to work only for relations, excluding aggregates.

        select_related_token = i
        if LOOKUP_SEP in i:
            """
			Since select_related() is rather flexible about what it receives
			(ignoring things it doesn't like), we'll just haphazardly pass
			all filter and display fields in for now.
			"""
            select_related_token = i.split(LOOKUP_SEP)
            select_related_token.pop()  # get rid of the field name
            select_related_token = LOOKUP_SEP.join(select_related_token)
            """
			Here we remove distinct status for queries which have reverse relations
			and possibly numerous results per original record
			"""
            if distinct and is_reverse_related(i, queryset.model):
                distinct = False

            primary_model = queryset.model
            join_route = i
            if len(i.split(LOOKUP_SEP)) > 2:
                second_to_last = LOOKUP_SEP.join(i.split(LOOKUP_SEP)[0:-1])
                join_route = LOOKUP_SEP.join(i.split(LOOKUP_SEP)[-2:])
                primary_model = get_closest_relation(queryset.model,
                                                     second_to_last)[0]

            primary_table = primary_model._meta.db_table
            if primary_table in queryset.query.table_map:
                primary_table = queryset.query.table_map[primary_table][
                    -1]  # Will the last one always work?

            join_model, join_field, join_name = get_closest_relation(
                primary_model, join_route)
            join_table = join_model._meta.db_table
            try:
                join_table = queryset.query.table_map[join_table][-1]
                queryset = queryset.extra(
                    select={i: '%s.%s' % (join_table, join_field.column)})
            except KeyError:
                """
				Design decision. This will work fine if the ModelAdmin does the displaying of
				related objects for us. At this time, Django doesn't. We have a patch in place
				but aren't using it.

				for now we just need the join column between the primary table and the join table.
				"""
                join_table = join_model._meta.db_table
                join_column = "id"  # Always this for now.

                for field_name in primary_model._meta.get_all_field_names():
                    from django.db import models
                    try:
                        field = primary_model._meta.get_field(field_name)
                        if (isinstance(field,models.OneToOneField) or isinstance(field,models.ForeignKey)) and \
                          field.rel.to == join_model:

                            # 2 foreignkeys on the same model issue
                            # https://code.djangoproject.com/ticket/12890
                            if field_name not in select_related_token:
                                continue

                            whereclause = '%s.%s=%s.%s' % (
                                join_table, join_column, primary_table,
                                field.column)
                            if not (select_related_token,
                                    join_table) in used_routes:
                                queryset = queryset.extra(select={i: '%s.%s' % (join_table,join_field.column)},\
                                  tables=[join_table],where=[whereclause])
                            else:
                                queryset = queryset.extra(select={
                                    i:
                                    '%s.%s' % (join_table, join_field.column)
                                },
                                                          where=[whereclause])

                    except models.FieldDoesNotExist:
                        pass

                if not (select_related_token, join_table) in used_routes:
                    used_routes.append((select_related_token, join_table))

        if not select_related_token in select_related:
            select_related.append(select_related_token)

    if select_related:
        queryset = queryset.select_related(*select_related)

    if distinct:
        queryset = queryset.distinct()

    return queryset
예제 #37
0
def display_list(query_class,_model_class=None,inclusions=None,exclusions=None,depth=None,\
  model_exclusions=None,_max_depth=None,_relation_list=None):
    """
	User Args:
		depth: how far our relation follows, we want to make sure to include forward then backward relations, as well.
			ex. depth=1, consumer->field, consumer->disability_primary->field, consumer->address->field, consumer<-goal<-field, consumer<-pwi<-field

		inclusions: A list of string-module relationships we want to be allowed to choose from, if not passed in, it means we want all the relations

		exclusions: A list of string-module relationships to not follow, it could be a field or whole relation.

		query_class: The class at the top of the tree hierarchy, essentially what we are reporting on.

	Function Args:
		_model_class: As we progress through the tree, we need to keep track of what model we are on.

		_max_depth: Takes the depth, and depth starts as a counter from 0, just easier to read this way

		_relation_list: What our function returns, but we need to pass it through so it can find it's way to the top... may be able to change this though

	function return: A list of tuples, where each tuple represents (string-module relationship, human-readable-value)
		ex. [
				('first_name', 'Consumer :: First Name'),
				('address__zip',	'Consmer :: Address :: Zip'),
				('pwi__refer_date', 'Consumer :: PWI :: Referral Date')
			]
	"""
    _relation_list = _relation_list or []
    exclusions = exclusions or []
    model_exclusions = model_exclusions or []
    inclusions = inclusions or []

    # if no model class is passed, then we are at the beginning or "base_class"
    query_aggregates = None
    if query_class.__class__ == query.QuerySet:
        query_aggregates = query_class.query.aggregates
        query_class = query_class.model

    _model_class = _model_class or query_class

    ## less typing when calling the function, we use depth to set _max_depth from the first call, and use _max_depth henceforth.
    # thus depth just keeps track of what level we are on
    if not _max_depth:
        _max_depth = depth
        depth = 0

    exclusions.extend(['logentry', 'message',
                       'id'])  # these are always excluded fields

    ## We dont want our backward relations in the next recursive step, so we add it to our exclusions.
    # as well as the base class.
    exclusions.append(_model_class._meta.module_name)
    model_exclusions.append(_model_class._meta.module_name)

    current_inclusions = [
        r.split(LOOKUP_SEP, 1)[0] for r in inclusions
    ]  # these are the ONLY fields and relations to be returned
    current_exclusions = [
        r for r in exclusions if LOOKUP_SEP not in r
    ]  # these are the fields / relations we don't want to show up

    # Non-relational fields are easy and just get appended to the list as is pretty much
    non_relation_fields = [f for f in _model_class._meta.fields if \
      f.name not in current_exclusions and
      f.name not in model_exclusions]

    # Now handle the relations
    # Get the forward ones
    relations = [(f.rel.to, f.name, f.verbose_name.lower()) for f in _model_class._meta.fields if \
      hasattr(f.rel, "to") and \
      f.name not in current_exclusions and
      f.rel.to._meta.module_name not in model_exclusions]

    # and grab our backward ones
    relations.extend([(r.model, r.field.related_query_name(), r.model._meta.verbose_name) for r in _model_class._meta.get_all_related_objects() if \
      r.model._meta.module_name not in current_exclusions and
      r.model._meta.module_name not in model_exclusions])

    # We have to handle the inclusion list separately because if there isn't one, we don't want to filter over nothing
    if current_inclusions:
        non_relation_fields = [
            f for f in non_relation_fields if f.name in current_inclusions
        ]
        relations = [
            r for r in relations
            if r[0]._meta.module_name in current_inclusions
        ]  # r == (model, model.verbose_name)

    # At this point we are finally adding our fields to the tuple list
    if query_aggregates:
        [_relation_list.append((q, q)) for q in query_aggregates.keys()]

    for field in non_relation_fields:
        if _model_class != query_class:
            _relation_list.append((
                field.name, field.verbose_name.lower()
                #' :: '.join([_model_class._meta.verbose_name.lower(), field.verbose_name.lower()])
            ))
        else:
            _relation_list.append((field.name, ' :: '.join(
                [_model_class._meta.module_name,
                 field.verbose_name.lower()])))

    ## Recursion happens at this point, we are basically going down one tree / relations at a time before we do another one
    # so taking consumer... it will do consumer->address->zip and then it will do consumer->emergency_contact and
    # then whatever backward relations
    for relation in relations:
        # prepare the inclusion/exclusion for the next recursive call by chopping off all relations that match the one in our loop
        relation_inclusions = [
            name.split(LOOKUP_SEP, 1)[1] for name in inclusions if
            LOOKUP_SEP in name and name.split(LOOKUP_SEP, 1)[0] == relation[1]
        ]
        relation_exclusions = [
            name.split(LOOKUP_SEP, 1)[1] for name in exclusions if
            LOOKUP_SEP in name and name.split(LOOKUP_SEP, 1)[0] == relation[1]
        ]

        if current_inclusions:
            pass  # if we have inclusions we want to continue with, don't return this tree yet
        elif depth >= _max_depth:
            return _relation_list  # return this tree

        # if we have reached a star in the exclusions, then skip the rest of this relation
        if [
                True for r in relation_exclusions
                if '*' in r.split(LOOKUP_SEP, 1)[0]
        ]:
            continue

        # recurse
        ###
        # we use copy.deepcopy on model_exclusions because we don't want a global list of exclusions everytime it adds a new one,
        # just the ones down this tree
        relation_pair_list = display_list(query_class,_model_class=relation[0],\
         inclusions=relation_inclusions,exclusions=relation_exclusions,model_exclusions=copy.deepcopy(model_exclusions),\
         depth=depth + 1,_max_depth=_max_depth)

        # build the module-relation and human-readable string
        ###
        # We check if _model_class != query_class because we have a case here where once we hit the top of the tree,
        # then we don't want to append the query_class to the module-relation
        if _model_class != query_class:
            _relation_list.extend([
                (LOOKUP_SEP.join([relation[1], relation_pair[0]]),
                 ' :: '.join([relation[2], relation_pair[1]]))
                for relation_pair in relation_pair_list
            ])
        else:
            _relation_list.extend([
             (LOOKUP_SEP.join([relation[1], relation_pair[0]]),
             ' :: '.join([_model_class._meta.module_name, relation[2], relation_pair[1]])) \
             for relation_pair in relation_pair_list
            ])

    # go back up the recursion tree now
    return _relation_list
예제 #38
0
def clip_lookup(lookup):
    return LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[1:]) or None
예제 #39
0
def display_list_redux(query_class,
                       _model_class=None,
                       inclusions=None,
                       model_exclusions=None,
                       _relation_list=None):
    """
	User Args:
		inclusions: A list of string-module relationships we want to be allowed to
				 choose from, if not passed in, it means we want all the relations

		query_class: The class at the top of the tree hierarchy, essentially what
					we are reporting on.

	Function Args:
		_model_class: As we progress through the tree, we need to keep track of what model we are on.

		_relation_list: What our function returns, but we need to pass it through so it can find
			 it's way to the top... may be able to change this though

	function return: A list of tuples, where each tuple represents
		(string-module relationship, human-readable-value)
		ex. [
				('first_name', 'Consumer :: First Name'),
				('address__zip',	'Consmer :: Address :: Zip'),
				('pwi__refer_date', 'Consumer :: PWI :: Referral Date')
			]
	"""
    _relation_list = _relation_list or []
    model_exclusions = model_exclusions or []
    inclusions = inclusions or []

    # if no model class is passed, then we are at the beginning or "base_class"
    query_aggregates = None
    if query_class.__class__ == query.QuerySet:
        query_aggregates = query_class.query.aggregates
        query_class = query_class.model

    _model_class = _model_class or query_class

    model_exclusions.append(_model_class._meta.module_name)

    current_inclusions = [
        r.split(LOOKUP_SEP, 1)[0] for r in inclusions
    ]  # these are the ONLY fields and relations to be returned

    # Non-relational fields are easy and just get appended to the list as is pretty much
    non_relation_fields = [f for f in _model_class._meta.fields]

    # Now handle the relations
    # Get the forward ones
    relations = [(f.rel.to, f.name, f.verbose_name.lower()) for f in _model_class._meta.fields if \
      hasattr(f.rel, "to") and \
      f.rel.to._meta.module_name not in model_exclusions]

    # and grab our backward ones
    relations.extend([(r.model, r.field.related_query_name(), r.model._meta.verbose_name) for r in _model_class._meta.get_all_related_objects() if \
      r.model._meta.module_name not in model_exclusions])

    # We have to handle the inclusion list separately because if there isn't one, we don't want to filter over nothing
    if current_inclusions:
        non_relation_fields = [
            f for f in non_relation_fields if f.name in current_inclusions
        ]
        relations = [
            r for r in relations
            if r[0]._meta.module_name in current_inclusions
        ]  # r == (model, model.verbose_name)

    # At this point we are finally adding our fields to the tuple list
    if query_aggregates:
        [_relation_list.append((q, q)) for q in query_aggregates.keys()]

    for field in non_relation_fields:
        if _model_class != query_class:
            _relation_list.append((
                field.name, field.verbose_name.lower()
                #' :: '.join([_model_class._meta.verbose_name.lower(), field.verbose_name.lower()])
            ))
        else:
            _relation_list.append((field.name, ' :: '.join(
                [_model_class._meta.module_name,
                 field.verbose_name.lower()])))

    ## Recursion happens at this point, we are basically going down one tree / relations at a time before we do another one
    # so taking consumer... it will do consumer->address->zip and then it will do consumer->emergency_contact and
    # then whatever backward relations
    for relation in relations:
        # prepare the inclusion for the next recursive call by chopping off all relations that match the one in our loop
        relation_inclusions = [
            name.split(LOOKUP_SEP, 1)[1] for name in inclusions if
            LOOKUP_SEP in name and name.split(LOOKUP_SEP, 1)[0] == relation[1]
        ]

        if not current_inclusions:
            return _relation_list

        # recurse
        ###
        # we use copy.deepcopy on model_exclusions because we don't want a global list of exclusions everytime it adds a new one,
        # just the ones down this tree
        relation_pair_list = display_list_redux(query_class,_model_class=relation[0],\
         inclusions=relation_inclusions,model_exclusions=copy.deepcopy(model_exclusions),\
         )

        # build the module-relation and human-readable string
        ###
        # We check if _model_class != query_class because we have a case here where once we hit the top of the tree,
        # then we don't want to append the query_class to the module-relation
        if _model_class != query_class:
            _relation_list.extend([
                (LOOKUP_SEP.join([relation[1], relation_pair[0]]),
                 ' :: '.join([relation[2], relation_pair[1]]))
                for relation_pair in relation_pair_list
            ])
        else:
            _relation_list.extend([
             (LOOKUP_SEP.join([relation[1], relation_pair[0]]),
             ' :: '.join([_model_class._meta.module_name, relation[2], relation_pair[1]])) \
             for relation_pair in relation_pair_list
            ])

    # go back up the recursion tree now
    return _relation_list
예제 #40
0
    def __validate_definition(self, *args, **kwargs):
        def error(message):
            raise FieldError("%s: %s: %s" % (self.related.model._meta, self.name, message))

        original_choices_callback = self._choices_callback

        # The choices we're defined by a string
        # therefore it should be a cls method
        if isinstance(self._choices_callback, basestring):
            callback = getattr(self.related.model, self._choices_callback, None)
            if not callable(callback):
                error('Cannot find method specified by choices.')
            args_length = 2 # Since the callback is a method we must emulate the 'self'
            self._choices_callback = callback
        else:
            args_length = 1 # It's a callable, it needs no reference to model instance
        
        spec = inspect.getargspec(self._choices_callback)
        
        # Make sure the callback has the correct number or arg
        if spec.defaults is not None:
            spec_defaults_len = len(spec.defaults)
            args_length += spec_defaults_len
            self._choices_relationships = spec.args[-spec_defaults_len:]
        else:
            self._choices_relationships = []
        
        if len(spec.args) != args_length:
            error('Specified choices callback must accept only a single arg')
        
        self._choices_callback_field_descriptors = {}
        
        # We make sure field descriptors are valid
        for descriptor in self._choices_relationships:
            lookups = descriptor.split(LOOKUP_SEP)
            meta = self.related.model._meta
            depth = len(lookups)
            step = 1
            fields = []
            for lookup in lookups:
                try:
                    field = meta.get_field(lookup)
                    # The field is a foreign key to another model
                    if isinstance(field, ForeignKey):
                        try:
                            meta = field.rel.to._meta
                        except AttributeError:
                            # The model hasn't been loaded yet
                            # so we must stop here and start over
                            # when it is loaded.
                            if isinstance(field.rel.to, basestring):
                                self._choices_callback = original_choices_callback
                                return add_lazy_relation(field.model, field,
                                                         field.rel.to,
                                                         self.__validate_definition)
                            else:
                                raise
                        step += 1
                    # We cannot go deeper if it's not a model
                    elif step != depth:
                        error('Invalid descriptor "%s", "%s" is not a ForeignKey to a model' % (
                               LOOKUP_SEP.join(lookups), LOOKUP_SEP.join(lookups[:step])))
                    fields.append(field)
                except FieldDoesNotExist:
                    # Lookup failed, suggest alternatives
                    depth_descriptor = LOOKUP_SEP.join(descriptor[:step - 1])
                    if depth_descriptor:
                        depth_descriptor += LOOKUP_SEP
                    choice_descriptors = [(depth_descriptor + name) for name in meta.get_all_field_names()]
                    error('Invalid descriptor "%s", choices are %s' % (
                          LOOKUP_SEP.join(descriptor), ', '.join(choice_descriptors)))
            
            self._choices_callback_field_descriptors[descriptor] = fields
예제 #41
0
def path_v4(model, *conditions):
    '''
Returns a filter of all objects of type model, subject to the list of conditions. 

The first argument, model, must be a model. It cannot be an instance of a model, or any other object.

The list of conditions can be arbitrarily long, but each condition must be one of the following: 
 * an instance of a model 
 * a 3-tuple (model, field, value), where field is a string which is the name of a field of model, and value is a valid value of that field
 * a 4-tuple (model, field, query_term, value), where field is a string which is the name of a field of model, query_term is a string which is the name of a Django query term (defined in django.db.models.sql.constants.QUERY_TERMS), and value is a valid value of that field

The function uses path_v1() to find the forward path from model to conditions[i] or conditions[i][0] (for all i), and then applies the appropriate filter.

The function asks the user (via the raw_input() function) which path(s) to filter on.
    '''
    conditions = list(conditions)
    if not (isclass(model) and issubclass(model, Model)):
        raise TypeError("path_v4 argument 1 must be a model")
    iter_conditions = range(len(conditions))
    models = [None for i in iter_conditions]
    fields = ['' for i in iter_conditions]
    lookup_type = ['' for i in iter_conditions]
    values = [None for i in iter_conditions]
    for i in iter_conditions:
        if isinstance(conditions[i], tuple) and isinstance(
                conditions[i][-1], Model):
            conditions[i] = conditions[i][-1]
        if isinstance(conditions[i], Model):
            if isinstance(conditions[i], model):
                raise TypeError(
                    "path_v4 argument 2 must not contain any instances of argument 1"
                )
            values[i] = conditions[i]
            models[i] = type(conditions[i])
        elif isinstance(conditions[i], tuple) and iscorrecttuple(
                conditions[i]):
            fields[i] = conditions[i][1]
            models[i] = conditions[i][0]
            if len(conditions[i]) == 3:
                values[i] = conditions[i][2]
            else:
                values[i] = conditions[i][3]
                lookup_type[i] = conditions[i][2]
        else:
            raise TypeError(
                "path_v4 argument 2 must be a list, with each item being either an instance of a model or valid (model, field, value) 3-tuple"
            )

    repeat = False
    use = ''
    kwargs = {}
    paths = []
    num_paths = 0
    for i in iter_conditions:
        for path in [
                LOOKUP_SEP.join(path + (fields[i], )).strip(LOOKUP_SEP)
                for (path, _, _) in path_v1(model, models[i])
        ]:
            repeat = True
            while repeat:
                use = raw_input("Include the path " + path +
                                " as a condition ([y]/n)? ")
                repeat = False
                if use.lower() == 'y' or use.lower() == 'yes' or not use:
                    kwargs[path] = values[i]
                elif not (use.lower() == 'n' or use.lower() == 'no'):
                    repeat = True
    return model.objects.filter(**kwargs)
    def build_filters(self, filters=None):
        """
        An enhanced tastypie method that will first check to see if a value
        passed inside of a filter can be decoded as JSON.  If so, it passes those
        values to ``::meth::<get_query_bits_from_dict> get_query_bits_from_dict``.
        """
        if filters is None:
            filters = {}

        qs_filters = {}
        if hasattr(self._meta, 'queryset'):
            # Get the possible query terms from the current QuerySet.
            if hasattr(self._meta.queryset.query.query_terms, 'keys'):
                # Django 1.4 & below compatibility.
                query_terms = self._meta.queryset.query.query_terms.keys()
            else:
                # Django 1.5+.
                query_terms = self._meta.queryset.query.query_terms
        else:
            query_terms = QUERY_TERMS.keys()

        for filter_expr, value in filters.items():
            filter_type = 'exact'
            # Check first to see if a value can be decoded as json
            try:
                value_dict = json.loads(value)
                # Because the python json module is not as strict as JSON standards
                # We must check to make sure that the value loaded is a dict
                if not isinstance(value_dict, dict):
                    value_dict = None
            except ValueError:
                value_dict = None

            # If a value is decoded, it passes that dict into a special method
            if value_dict:
                # Get the value and filter_bits from the method.
                filter_bits, value = self.get_query_bits_from_dict(value_dict,
                    keys_list=[], value=None)
                # Because the field_name and the filter_expr are backward,
                # we need to set the field name = to filter expr
                field_name = filter_expr

            # If not, stick with standard tastypie stuff
            else:
                filter_bits = filter_expr.split(LOOKUP_SEP)
                field_name = filter_bits.pop(0)

            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)
예제 #43
0
 def build_filters(self, filters=None):
     """
     Given a dictionary of filters, create the necessary ORM-level filters.
     
     Keys should be resource fields, **NOT** model fields.
     
     Valid values are either a list of Django filter types (i.e.
     ``['startswith', 'exact', 'lte']``), the ``ALL`` constant or the
     ``ALL_WITH_RELATIONS`` constant.
     """
     # At the declarative level:
     #     filtering = {
     #         'resource_field_name': ['exact', 'startswith', 'endswith', 'contains'],
     #         'resource_field_name_2': ['exact', 'gt', 'gte', 'lt', 'lte', 'range'],
     #         'resource_field_name_3': ALL,
     #         'resource_field_name_4': ALL_WITH_RELATIONS,
     #         ...
     #     }
     # Accepts the filters as a dict. None by default, meaning no filters.
     if filters is None:
         filters = {}
     
     qs_filters = {}
     
     for filter_expr, value in filters.items():
         filter_bits = filter_expr.split(LOOKUP_SEP)
         
         if not filter_bits[0] in self.fields:
             # It's not a field we know about. Move along citizen.
             continue
         
         if not filter_bits[0] in self._meta.filtering:
             raise InvalidFilterError("The '%s' field does not allow filtering." % filter_bits[0])
         
         if filter_bits[-1] in QUERY_TERMS.keys():
             filter_type = filter_bits.pop()
         else:
             filter_type = 'exact'
         
         # Check to see if it's allowed lookup type.
         if not self._meta.filtering[filter_bits[0]] in (ALL, ALL_WITH_RELATIONS):
             # Must be an explicit whitelist.
             if not filter_type in self._meta.filtering[filter_bits[0]]:
                 raise InvalidFilterError("'%s' is not an allowed filter on the '%s' field." % (filter_expr, filter_bits[0]))
         
         # Check to see if it's a relational lookup and if that's allowed.
         if len(filter_bits) > 1:
             if not self._meta.filtering[filter_bits[0]] == ALL_WITH_RELATIONS:
                 raise InvalidFilterError("Lookups are not allowed more than one level deep on the '%s' field." % filter_bits[0])
         
         if self.fields[filter_bits[0]].attribute is None:
             raise InvalidFilterError("The '%s' field has no 'attribute' for searching with." % filter_bits[0])
         
         if value == 'true':
             value = True
         elif value == 'false':
             value = False
         elif value in ('nil', 'none', 'None'):
             value = None
         
         db_field_name = LOOKUP_SEP.join([self.fields[filter_bits[0]].attribute] + filter_bits[1:])
         qs_filter = "%s%s%s" % (db_field_name, LOOKUP_SEP, filter_type)
         qs_filters[qs_filter] = value
     
     return dict_strip_unicode_keys(qs_filters)
예제 #44
0
def deep_prefetch_related_objects(objects, lookups):
    """
    Helper function for prefetch_related functionality.

    Populates prefetched objects caches for a list of results
    from a QuerySet.

    Differs from :meth:`django.db.models.query.prefetch_related_objects`
    in that that it can prefetch "non-strict" lookups through GFK.

    :param objects: result cache of base queryset.
    :param lookups: list with lookups.
    """

    #How it works
    #------------
    #   - Since data is not of same type - "unit" of data is a model instance
    #     (object), not QuerySet.
    #   - Goals that I was tried to achieve:
    #       - Prefetching 'non-strict' lookups.
    #       - Absence of doubled queries to DB, other redundancy in processing
    #       - and infinite recursions.
    #       - Simplicity of design in comparison with original
    #         `django.db.models.query.prefetch_related_objects`
    #   - Sets are used to avoid duplicates.
    #   - Python's sets distinguish objects by value. But task require
    #     to distinguish objects by id, not by value, because two objects
    #     that are same by value, might be included in two different
    #     querysets, and both of them must be populated with cache.
    #   - BUT fetched cache data for object are distinguished by value.
    #     Rationale is to distinguish unique queries and prevent their
    #     repeated execution. We don't care about "id" of queries, we care
    #     about their value. Composite identifier for query is
    #     object and lookup for which query being constructed.
    #     Only one query exists for one object (by value) and lookup part.
    #     In function there slightly more complex structure than
    #     `obj -> lookup` used for storing data for traversed lookups
    #     (``seen``) - that's done to reduce redundancy in data, but
    #     "primary key" for stroed data is `obj -> lookup`.
    #   - Data flows through `buffer`, during processing.
    #     Objects discovered while traversing DB structure are being added
    #     and processed objects are removed.
    #todo beauty and refactoring
    if len(objects) == 0:
        return # nothing to do

    #lookup -> model -> {(id(obj), obj), ...}
    #id(obj) - because objects must be distinguished by id.
    #DefaultOrederedDict because order of lookups matter,
    #see tests.LookupOrderingTest.test_order of Django test suite.
    buffer = DefaultOrderedDict(lambda: defaultdict(set))

    update_buffer(buffer, objects, reversed(lookups))
    seen = tree()     # model -> attr ->
                      #              single     -> bool
                      #              cache_name -> str
                      #              cache      ->
                      #                         obj -> [cache]
    while True:
        try:
            lookup = last(buffer.keys())
        except TypeError:
            break
        try:
            model, current = buffer[lookup].popitem()
        except KeyError:
            del buffer[lookup]
            continue

        attr = lookup.split(LOOKUP_SEP)[0]

        sample = current.pop()
        current.add(sample)
        _, object = sample
        prefetcher, _, attr_found, _ = get_prefetcher(object, attr)

        #Lookup is not valid for that object, it must be skipped.
        #No exception, because data it is that data is of diffrerent types,
        #so - such situation is normal.
        if not attr_found:
            continue

        if LOOKUP_SEP not in lookup and prefetcher is None:
            raise ValueError("'%s' does not resolve to a item that supports "
                             "prefetching - this is an invalid parameter to "
                             "prefetch_related()." % lookup)

        if prefetcher is None:
            clipped = clip_lookup(lookup)
            if clipped:
                update_buffer(
                    buffer,
                    filter(is_not_none, [getattr(o, attr)
                                         for _, o in current]),
                    [clipped])
            continue


        to_discard = set()
        for e in current: # no need to query for already prefetched data
            obj = e[1]
            obj_model = obj.__class__
            p, d, _, is_fetched = get_prefetcher(obj, attr)
            cache = None
            if is_fetched: # case of Django internal cache
                single, cache_name = get_info(p, d)
                cache = get_cache(obj, single, cache_name, attr)
                single, cache_name = get_info(p, d)
                update_seen(seen, model, attr, single, cache_name, obj, cache)
                to_discard.add(e)
            elif (model in seen and  # case of `seen`
                attr in seen[obj_model] and
                obj in seen[obj_model][attr]['cache']):
                single = seen[obj_model][attr]['single']
                cache_name = seen[obj_model][attr]['cache_name']
                cache = seen[obj_model][attr]['cache'][obj]
                to_discard.add(e)
                set_cache(obj, single, cache, cache_name, attr)
            if cache is not None and len(cache) != 0:  # if data was cached
                clipped = clip_lookup(lookup)          # it still must get
                if clipped:                            # into `buffer`.
                    update_buffer(buffer, cache, [clipped])

        current -= to_discard

        if current:
            prefetch_qs, rel_attr_fn, cur_attr_fn, single, cache_name =       \
            prefetcher.get_prefetch_query_set(
                map(itemgetter(1), current)
            )

            #prefetch lookups from prefetch queries are merged into processing.
            additional_lookups = getattr(prefetch_qs,
                                         '_prefetch_related_lookups', [])

            if additional_lookups:
                setattr(prefetch_qs, '_prefetch_related_lookups', [])
            discovered = list(prefetch_qs)
            lookups_for_discovered = additional_lookups
            clipped_lookup = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[1:])
            if len(clipped_lookup) > 0:
                lookups_for_discovered = chain([clipped_lookup],
                                               additional_lookups)
            if lookups_for_discovered:
                update_buffer(buffer, discovered, reversed(list(lookups_for_discovered)))


            rel_to_cur = defaultdict(list)

            for obj in discovered:
                val = rel_attr_fn(obj)
                rel_to_cur[val].append(obj)
            for pair in current: # queried data is set up to objects
                obj = pair[1]
                val = cur_attr_fn(obj)
                cache = rel_to_cur.get(val, [])
                update_seen(seen, model, attr, single, cache_name, obj, cache)
                set_cache(obj, single, cache, cache_name, attr)
    def _perform_search(self):
        """
        Generates the query using the validated constraint formset.  Also compiles a natural
        language string (``self.natural_string``) in the format of
        "N [Model]s with [field] [operator] '[term]'".

        """

        natural_string = []

        # 2-tuples of an operator and Q instance, sent through reduce() after it's built
        query_list = []

        for i, constraint_form in enumerate(self.constraint_formset):
            type_operator = constraint_form.cleaned_data['type']
            field_list = constraint_form.cleaned_data['field']
            constraint_operator = constraint_form.cleaned_data['operator']
            term = constraint_form.cleaned_data['term']
            end_term = constraint_form.cleaned_data['end_term']

            verbose_name = self.model_config._fields[field_list]

            # Build this field's query.
            # Search fields bound together in a tuple are considered OR conditions for a single
            # virtual field name.
            query = None

            # Iterate multiple fields defined in a compound column
            for field in field_list:
                value = term
                target_field = resolve_orm_path(self.model, field)

                # Prep an inverted lookup
                negative = constraint_operator.startswith('!')
                if negative:
                    constraint_operator = constraint_operator[1:]

                if constraint_operator == "isnull":
                    value = not negative
                    negative = False

                # Bake the queryset language string
                field_query = LOOKUP_SEP.join((field, constraint_operator))

                q = Q(**{
                    field_query: value,
                })
                log.debug("Querying %s [%d]: %s%s=%r", self.model.__name__, i, field_query,
                          "!" if negative else "", value)

                # Negate if necessary
                if negative:
                    q = ~q

                if query is None:
                    query = q
                else:
                    query |= q

            query_list.append((type_operator, query))

            # Do some natural processing
            if isinstance(value, (tuple, list)):
                value = ' - '.join(map(unicode, value))
            else:
                value = str(value)
            bits = [verbose_name, constraint_form['operator'].value(), value]
            if i != 0: # Skip the leading "and" on the first constraint
                bits.insert(0, constraint_form['type'].value())
            natural_string.append(bits)

        # The first query's "type" should be ignored for the sake of the reduce line below.
        query = reduce(lambda q1, (op, q2): op(q1, q2), query_list[1:], query_list[0][1])

        queryset = self.build_queryset(self.model, query)
        data_rows = self.process_results(queryset)

        self.results = {
            'count': len(queryset),
            'list': data_rows,
            'fields': self._get_display_fields(self.model, self.model_config),
            'natural_string': "where " + ', '.join(map(' '.join, natural_string)),
        }
예제 #46
0
def path_v5(model, paths, *conditions):
    '''
Returns a filter of all objects of type model, subject to the list of conditions. 

The first argument, model, must be a model. It cannot be an instance of a model, or any other object.

The second argument, paths, is a dictionary. paths[condition_model] is a list of selected paths from model to condition_model that should be queried.

The list of conditions can be arbitrarily long, but each condition must be one of the following: 
 * an instance of a model 
 * a 3-tuple (model, field, value), where field is a string which is the name of a field of model, and value is a valid value of that field
 * a 4-tuple (model, field, query_term, value), where field is a string which is the name of a field of model, query_term is a string which is the name of a Django query term (defined in django.db.models.sql.constants.QUERY_TERMS), and value is a valid value of that field

path_v1() is used outside of the scope of this function, to determine paths. Then path_v5 applies the appropriate filter.
    '''
    conditions = list(conditions)
    if not is_useful_model(model):
        raise TypeError("path_v5 argument 1 must be a useful model")
    iter_conditions = range(len(conditions))
    models = [None for i in iter_conditions]
    fields = ['' for i in iter_conditions]
    lookup_type = ['' for i in iter_conditions]
    values = [None for i in iter_conditions]
    for i in iter_conditions:
        if isinstance(conditions[i], tuple) and isinstance(
                conditions[i][-1], Model):
            conditions[i] = conditions[i][-1]
        if isinstance(conditions[i], Model):
            if isinstance(conditions[i], model):
                raise TypeError(
                    "path_v5 argument 3 must not contain any instances of argument 1"
                )
            values[i] = conditions[i]
            models[i] = type(conditions[i])
        elif isinstance(conditions[i], tuple) and iscorrecttuple(
                conditions[i]):
            fields[i] = conditions[i][1]
            models[i] = conditions[i][0]
            if len(conditions[i]) == 3:
                values[i] = conditions[i][2]
            else:
                values[i] = conditions[i][3]
                lookup_type[i] = conditions[i][2]
        else:
            raise TypeError(
                "path_v5 argument 3 must be a list, with each item being either an instance of a model or valid (model, field, value) 3-tuple"
            )

    repeat = False
    use = ''
    kwargs = {}
    all_paths = []
    for i in iter_conditions:
        for path in paths[models[i]]:
            kwargs[LOOKUP_SEP.join(
                (path, fields[i],
                 lookup_type[i])).strip(LOOKUP_SEP)] = values[i]
            all_paths.append(path)
    automatic_conditions_for_registrations = []
    for path in all_paths:
        new_path = passes_through(model, path, StudentRegistration, 'end_date')
        if new_path and isinstance(new_path, basestring):
            automatic_conditions_for_registrations.append(new_path)
    for condition in automatic_conditions_for_registrations:
        condition_being_used = False
        for path in all_paths:
            if condition in path:
                condition_being_used = True
                break
        if not condition_being_used:
            kwargs[condition + LOOKUP_SEP + 'gte'] = datetime.now()
    return model.objects.filter(**kwargs).distinct()