#34458: Calling defer('user_id') instead of defer('user') will raise
AttributeError: 'ForeignKey' object has no attribute 'field'
-------------------------------------+-------------------------------------
               Reporter:  Andrew     |          Owner:  nobody
  Cordery                            |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  4.2
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 Calling defer() with the column name of a foreign key ('user_id') instead
 of the field name ('user') will raise an {{{AttributeError: 'ForeignKey'
 object has no attribute 'field'}}}

 I believe this is either a regression in 4.2 or an undocumented change of
 behavior.  Generally speaking, I'd prefer this new requirement to always
 use the field name as it reduces ambiguity/improves refactoring etc.
 However, it needs documentation and a better error message.


 Here is an example traceback:

 {{{
 In [11]: User.objects.defer('organization_id')
 Out[11]:
 ---------------------------------------------------------------------------
 AttributeError                            Traceback (most recent call
 last)
 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/IPython/core/formatters.py:708, in
 PlainTextFormatter.__call__(self, obj)
     701 stream = StringIO()
     702 printer = pretty.RepresentationPrinter(stream, self.verbose,
     703     self.max_width, self.newline,
     704     max_seq_length=self.max_seq_length,
     705     singleton_pprinters=self.singleton_printers,
     706     type_pprinters=self.type_printers,
     707     deferred_pprinters=self.deferred_printers)
 --> 708 printer.pretty(obj)
     709 printer.flush()
     710 return stream.getvalue()

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/IPython/lib/pretty.py:410, in RepresentationPrinter.pretty(self,
 obj)
     407                         return meth(obj, self, cycle)
     408                 if cls is not object \
     409                         and
 callable(cls.__dict__.get('__repr__')):
 --> 410                     return _repr_pprint(obj, self, cycle)
     412     return _default_pprint(obj, self, cycle)
     413 finally:

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/IPython/lib/pretty.py:778, in _repr_pprint(obj, p, cycle)
     776 """A pprint that just redirects to the normal repr function."""
     777 # Find newlines and replace them with p.break_()
 --> 778 output = repr(obj)
     779 lines = output.splitlines()
     780 with p.group():

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/query.py:374, in QuerySet.__repr__(self)
     373 def __repr__(self):
 --> 374     data = list(self[: REPR_OUTPUT_SIZE + 1])
     375     if len(data) > REPR_OUTPUT_SIZE:
     376         data[-1] = "...(remaining elements truncated)..."

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/query.py:398, in QuerySet.__iter__(self)
     383 def __iter__(self):
     384     """
     385     The queryset iterator protocol uses three nested iterators in
 the
     386     default case:
    (...)
     396            - Responsible for turning the rows into model objects.
     397     """
 --> 398     self._fetch_all()
     399     return iter(self._result_cache)

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/query.py:1881, in QuerySet._fetch_all(self)
    1879 def _fetch_all(self):
    1880     if self._result_cache is None:
 -> 1881         self._result_cache = list(self._iterable_class(self))
    1882     if self._prefetch_related_lookups and not self._prefetch_done:
    1883         self._prefetch_related_objects()

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/query.py:91, in ModelIterable.__iter__(self)
      88 compiler = queryset.query.get_compiler(using=db)
      89 # Execute the query. This will also fill compiler.select,
 klass_info,
      90 # and annotations.
 ---> 91 results = compiler.execute_sql(
      92     chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size
      93 )
      94 select, klass_info, annotation_col_map = (
      95     compiler.select,
      96     compiler.klass_info,
      97     compiler.annotation_col_map,
      98 )
      99 model_cls = klass_info["model"]

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py:1547, in
 SQLCompiler.execute_sql(self, result_type, chunked_fetch, chunk_size)
    1545 result_type = result_type or NO_RESULTS
    1546 try:
 -> 1547     sql, params = self.as_sql()
    1548     if not sql:
    1549         raise EmptyResultSet

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py:734, in SQLCompiler.as_sql(self,
 with_limits, with_col_aliases)
     732 try:
     733     combinator = self.query.combinator
 --> 734     extra_select, order_by, group_by = self.pre_sql_setup(
     735         with_col_aliases=with_col_aliases or bool(combinator),
     736     )
     737     for_update_part = None
     738     # Is a LIMIT/OFFSET clause needed?

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py:84, in
 SQLCompiler.pre_sql_setup(self, with_col_aliases)
      78 def pre_sql_setup(self, with_col_aliases=False):
      79     """
      80     Do any necessary class setup immediately prior to producing
 SQL. This
      81     is for things that can't necessarily be done in __init__
 because we
      82     might not have all the pieces in place at that time.
      83     """
 ---> 84     self.setup_query(with_col_aliases=with_col_aliases)
      85     order_by = self.get_order_by()
      86     self.where, self.having, self.qualify =
 self.query.where.split_having_qualify(
      87         must_group_by=self.query.group_by is not None
      88     )

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py:73, in
 SQLCompiler.setup_query(self, with_col_aliases)
      71 if all(self.query.alias_refcount[a] == 0 for a in
 self.query.alias_map):
      72     self.query.get_initial_alias()
 ---> 73 self.select, self.klass_info, self.annotation_col_map =
 self.get_select(
      74     with_col_aliases=with_col_aliases,
      75 )
      76 self.col_count = len(self.select)

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py:256, in
 SQLCompiler.get_select(self, with_col_aliases)
     254     select_idx += 1
     255 assert not (self.query.select and self.query.default_cols)
 --> 256 select_mask = self.query.get_select_mask()
     257 if self.query.default_cols:
     258     cols = self.get_default_columns(select_mask)

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/query.py:768, in Query.get_select_mask(self)
     766 opts = self.get_meta()
     767 if defer:
 --> 768     return self._get_defer_select_mask(opts, mask)
     769 return self._get_only_select_mask(opts, mask)

 File ~/.local/share/virtualenvs/axi-v3J2FyDc/lib/python3.11/site-
 packages/django/db/models/sql/query.py:724, in
 Query._get_defer_select_mask(self, opts, mask, select_mask)
     722     field = relation.field
     723 else:
 --> 724     field = opts.get_field(field_name).field
     725     field_select_mask = select_mask.setdefault(field, {})
     726 related_model = field.model._meta.concrete_model

 AttributeError: 'ForeignKey' object has no attribute 'field'
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34458>
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/010701874d000dc2-6e989e50-3401-4dfb-b568-baea97b38690-000000%40eu-central-1.amazonses.com.

Reply via email to