#25150: Iterable objects are converted to lists during SQL handling
-------------------------------------+-------------------------------------
     Reporter:  ekoyle               |                    Owner:  nobody
         Type:  Uncategorized        |                   Status:  new
    Component:  Database layer       |                  Version:  1.8
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  1                    |  Patch needs improvement:  1
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by ekoyle):

 Sorry for the lack of information in my original report.

 The problem is that my object, although it can be used as an iterator,
 shouldn't be used as such.  It is a netaddr.IPNetwork object, and it can
 act as an iterator (it iterates over addresses in the network); however,
 it represents a network and not a list of addresses.

 When cast to a list, we have problems around line 1200:

 {{{
         else:
             col = targets[0].get_col(alias, field)
             condition = self.build_lookup(lookups, col, value)  #
 Traceback here, b/c a list is not valid for the lookup we are doing
             lookup_type = condition.lookup_name
 }}}

 However we don't want to iterate over the object at all (it will cause
 some seriously strange side effects to replace a network with a list of
 addresses).

 We are using a django extension to handle postgresql INET and CIDR types
 (django-netfields which we have updated to work with django 1.8), but the
 problem is happening outside the netfields code (it is cast to a list in
 django/db/models/sql/query.py):

 Object in question:

 {{{
 from netfields import CidrAddressField

 class Network(models.Model):
     network = CidrAddressField(primary_key=True)
 }}}

 Example of failing code:

 {{{
 In [1]: import netaddr

 In [2]: net = netaddr.IPNetwork('192.168.0.0/16')

 In [3]: Network.objects.filter(network=net)
 ---------------------------------------------------------------------------
 TypeError                                 Traceback (most recent call
 last)
 <ipython-input-3-961aa006d914> in <module>()
 ----> 1 Network.objects.filter(network=net)

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/manager.pyc in manager_method(self, *args,
 **kwargs)
     125         def create_method(name, method):
     126             def manager_method(self, *args, **kwargs):
 --> 127                 return getattr(self.get_queryset(), name)(*args,
 **kwargs)
     128             manager_method.__name__ = method.__name__
     129             manager_method.__doc__ = method.__doc__

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/query.pyc in filter(self, *args, **kwargs)
     677         set.
     678         """
 --> 679         return self._filter_or_exclude(False, *args, **kwargs)
     680
     681     def exclude(self, *args, **kwargs):

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/query.pyc in _filter_or_exclude(self, negate,
 *args, **kwargs)
     695             clone.query.add_q(~Q(*args, **kwargs))
     696         else:
 --> 697             clone.query.add_q(Q(*args, **kwargs))
     698         return clone
     699

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/sql/query.pyc in add_q(self, q_object)
    1302         existing_inner = set(
    1303             (a for a in self.alias_map if
 self.alias_map[a].join_type == INNER))
 -> 1304         clause, require_inner = self._add_q(where_part,
 self.used_aliases)
    1305         self.where.add(clause, AND)
    1306         for hp in having_parts:

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/sql/query.pyc in _add_q(self, q_object,
 used_aliases, branch_negated, current_negated, allow_joins, split_subq)
    1330                     child, can_reuse=used_aliases,
 branch_negated=branch_negated,
    1331                     current_negated=current_negated,
 connector=connector,
 -> 1332                     allow_joins=allow_joins,
 split_subq=split_subq,
    1333                 )
    1334                 joinpromoter.add_votes(needed_inner)

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/sql/query.pyc in build_filter(self, filter_expr,
 branch_negated, current_negated, can_reuse, connector, allow_joins,
 split_subq)
    1201             else:
    1202                 col = targets[0].get_col(alias, field)
 -> 1203             condition = self.build_lookup(lookups, col, value)
    1204             if not condition:
    1205                 # Backwards compat for custom lookups

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/sql/query.pyc in build_lookup(self, lookups,
 lhs, rhs)
    1094                     lhs = self.try_transform(lhs, name, lookups)
    1095                     final_lookup = lhs.get_lookup('exact')
 -> 1096                 return final_lookup(lhs, rhs)
    1097             lhs = self.try_transform(lhs, name, lookups)
    1098             lookups = lookups[1:]

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/lookups.pyc in __init__(self, lhs, rhs)
      94     def __init__(self, lhs, rhs):
      95         self.lhs, self.rhs = lhs, rhs
 ---> 96         self.rhs = self.get_prep_lookup()
      97         if hasattr(self.lhs, 'get_bilateral_transforms'):
      98             bilateral_transforms =
 self.lhs.get_bilateral_transforms()

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/django/db/models/lookups.pyc in get_prep_lookup(self)
     132
     133     def get_prep_lookup(self):
 --> 134         return
 self.lhs.output_field.get_prep_lookup(self.lookup_name, self.rhs)
     135
     136     def get_db_prep_lookup(self, value, connection):

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/netfields/fields.pyc in get_prep_lookup(self, lookup_type, value)
      39                 # Argument will be CIDR
      40                 return str(value)
 ---> 41             return self.get_prep_value(value)
      42
      43         return super(_NetAddressField, self).get_prep_lookup(

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/netfields/fields.pyc in get_prep_value(self, value)
      48             return None
      49
 ---> 50         return str(self.to_python(value))
      51
      52     def get_db_prep_lookup(self, lookup_type, value, connection,

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/netfields/fields.pyc in to_python(self, value)
      24
      25         try:
 ---> 26             return self.python_type()(value)
      27         except AddrFormatError as e:
      28             raise ValidationError(e)

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/netaddr/ip/__init__.pyc in __init__(self, addr, implicit_prefix,
 version, flags)
     921                 module = _ipv4
     922                 value, prefixlen = parse_ip_network(module, addr,
 --> 923                     implicit_prefix, flags)
     924             except AddrFormatError:
     925                 try:

 /home/esk/src/django-openipam/env/local/lib/python2.7/site-
 packages/netaddr/ip/__init__.pyc in parse_ip_network(module, addr,
 implicit_prefix, flags)
     816                 % module.family_name)
     817     else:
 --> 818         raise TypeError('unexpected type %s for addr arg' %
 type(addr))
     819
     820     if flags & NOHOST:

 TypeError: unexpected type <type 'list'> for addr arg
 }}}

--
Ticket URL: <https://code.djangoproject.com/ticket/25150#comment:3>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.45a17e31440c05633d1004f554b6915f%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to