﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
17485	Queries with both deferred fields and select_related defer field.name instead of field.attname	Michal Petrucha	Anssi Kääriäinen	"When deferring `ForeignKey` fields in conjunction with `select_related`, Django creates a `DeferredAttribute` for the field's `name` instead of its `attname`. This results in its `ReverseSingleRelatedObjectDescriptor` being overriden and thus stripping the model instance of most of this field's functionality.

Consider the following models (taken from regressiontests.queries):
{{{#!python
class Celebrity(models.Model):
    name = models.CharField(""Name"", max_length=20)
    greatest_fan = models.ForeignKey(""Fan"", null=True, unique=True)

    def __unicode__(self):
        return self.name

class Fan(models.Model):
    fan_of = models.ForeignKey(Celebrity)
}}}

Let's play around with it in the shell for a bit:
{{{
>>> Celebrity.objects.create(name=""Joe"")
<Celebrity: Joe>
>>> obj = Celebrity.objects.all().defer('greatest_fan').select_related('greatest_fan').get()
>>> print obj.__class__.__dict__
{
  '__module__': 'subclassfktest.models',
  '_meta': <Options for Celebrity_Deferred_greatest_fan>,
  'objects': <django.db.models.manager.ManagerDescriptor object at 0x1460810>,
  'MultipleObjectsReturned': <class 'subclassfktest.models.MultipleObjectsReturned'>,
  '_base_manager': <django.db.models.manager.Manager object at 0x1460490>,
  'greatest_fan': <django.db.models.query_utils.DeferredAttribute object at 0x1457750>,
  'DoesNotExist': <class 'subclassfktest.models.DoesNotExist'>,
  '__doc__': 'Celebrity_Deferred_greatest_fan(id, name, greatest_fan_id)',
  '_default_manager': <django.db.models.manager.Manager object at 0x1460ad0>,
  '_deferred': True
}
}}}

We can see that the deferred atribute is at `greatest_fan` instead of `greatest_fan_id` and the `ReverseSingleRelatedObjectDescriptor` is not present at all.

The following is just to show that the model instance is really broken because of this:

{{{
>>> print obj.greatest_fan
None
>>> f = Fan.objects.create(fan_of=obj)
>>> obj.greatest_fan = f
>>> obj.save()
>>> obj2 = Celebrity.objects.get()
>>> f.id
1
>>> print obj2.greatest_fan_id        # Should be 1
None
}}}

An interesting thing, though, is that the instance gives access to the right related object instance even without a working `ReverseSingleRelatedObjectDescriptor`:

{{{
>>> obj2.greatest_fan = f
>>> obj2.save()
>>> obj = Celebrity.objects.all().defer('greatest_fan').select_related('greatest_fan').get()
>>> obj.greatest_fan.id
1
>>> obj.__dict__
{'_greatest_fan_cache': <Fan: Fan object>, 'name': u'Joe', 'greatest_fan_id': None, '_state': <django.db.models.base.ModelState object at 0x146b250>, 'greatest_fan': <Fan: Fan object>, 'id': 1}
}}}

Anyway, the `_id` attribute is still never set to the correct value.

The fix for this behavior is a one-liner, see attachment. It makes sure to defer the field's `attname` even in this case (in all other cases where `deferred_class_factory` is called, `attnames` are used). There are side effects, though.

Actually, this bug was hiding another one for which even tests exist but because of this one they pass.

The first test failure is `test_basic` in `regressiontests.defer_regress.tests.DeferRegressionTest` which is quite obvious, it lists the incorrect model name caused by this bug and is fixed easily (since it is actually an incorrect test).

The other one, though, is not that simple: `test_defer` in `modeltests.defer.tests.DeferTests`. More precisely the following two lines:
{{{#!python
        # DOES THIS WORK?
        self.assert_delayed(qs.only(""name"").select_related(""related"")[0], 1)
        self.assert_delayed(qs.defer(""related"").select_related(""related"")[0], 0)
}}}
where `related` is a ForeignKey.

These have been around for a while and the only reason they passed until now is that `assert_delayed` checks the `attname` of each field whether it is deferred or not. In this case, however, the `attname` is obviously not deferred, since the `name` is in its stead.

The question is, what should we do with these two tests? Do we want to fix them in one go with this bug or file a new ticket and temporarily comment out the two failing test lines?"	Bug	closed	Database layer (models, ORM)	dev	Release blocker	fixed	defer select_related	niwi@… real.human@…	Ready for checkin	1	0	0	0	0	0
