#30309: Remove hasattr reference in One-to-One documentation example
-------------------------------+--------------------------------------
     Reporter:  David Beitey   |                    Owner:  nobody
         Type:  Uncategorized  |                   Status:  closed
    Component:  Documentation  |                  Version:  2.2
     Severity:  Normal         |               Resolution:  invalid
     Keywords:                 |             Triage Stage:  Unreviewed
    Has patch:  1              |      Needs documentation:  0
  Needs tests:  0              |  Patch needs improvement:  0
Easy pickings:  0              |                    UI/UX:  0
-------------------------------+--------------------------------------

Comment (by David Beitey):

 Replying to [comment:3 felixxm]:
 > `hasattr()` is not throwing an exceptions, just in case I checked with
 Python 3.6/3.7 and Django 2.1/2.2.
 >
 > Did you check this?
 >
 > FYI: `RelatedObjectDoesNotExist` is a subclass of `AttributeError`.

 Okay, I dug into the situation and what's happening isn't related to
 Python version.

 `RelatedObjectDoesNotExist` gets raised when the value of a OneToOneField
 is None/null.  However, if there is data in the field but it isn't present
 in the related table,  `[model_identifier].DoesNotExist` gets raised.  The
 latter exception doesn't inherit from `AttributeError` and isn't swallowed
 with `hasattr()`.

 Since the behaviour of `hasattr()` is to return False when there's no
 related object, that seems to be what should happen in both situations --
 with None as a value or a field value pointing at a non-existant related
 object.  In other words, it feels as though `RelatedObjectDoesNotExist`
 should be being raised when accessing the attribute on the model in this
 way, rather than at the query level (see traceback below).

 Here's my example:

 {{{
 from django.db import models

 class Person(models.Model):
     login = models.CharField(db_column='login', max_length=100)
     # and more...

    class Meta:
         managed = False
         db_table = 'SYS_PEOPLE'

 class Author(models.Model):
     person = models.OneToOneField(Person,
                                   db_column='login',
                                   primary_key=True,
                                   on_delete=models.CASCADE)
     # and more...

     class Meta:
         managed = False
         db_table = 'SYS_AUTHORS'

 # manage.py shell
 from app.models import Author
 author = Author(person_id='fake')
 author.person # raises DoesNotExist
 hasattr(author, 'person')   # raises DoesNotExist

 author = Author(person_id=None)
 author.person  # raises RelatedObjectDoesNotExist
 hasattr(author, 'person')   # Swallows
 }}}

 Tracebacks:

 {{{
 Traceback (most recent call last):
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/db/models/fields/related_descriptors.py", line 163, in
 __get__
     rel_obj = self.field.get_cached_value(instance)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/db/models/fields/mixins.py", line 13, in get_cached_value
     return instance._state.fields_cache[cache_name]
 KeyError: 'person'

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "./manage.py", line 17, in <module>
     execute_from_command_line(sys.argv)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/core/management/__init__.py", line 381, in
 execute_from_command_line
     utility.execute()
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/core/management/__init__.py", line 375, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/core/management/base.py", line 316, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/core/management/base.py", line 353, in execute
     output = self.handle(*args, **options)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/core/management/commands/shell.py", line 92, in handle
     exec(sys.stdin.read())
   File "<string>", line 4, in <module>
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/db/models/fields/related_descriptors.py", line 177, in
 __get__
     rel_obj = self.get_object(instance)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/db/models/fields/related_descriptors.py", line 297, in
 get_object
     return super().get_object(instance)
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/db/models/fields/related_descriptors.py", line 144, in
 get_object
     return qs.get(self.field.get_reverse_related_filter(instance))
   File "/home/user/local/share/virtualenvs/app-1933afW/lib/python3.7/site-
 packages/django/db/models/query.py", line 399, in get
     self.model._meta.object_name
 app.models.person.DoesNotExist: Person matching query does not exist.
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30309#comment:4>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/065.c50253eda26231a1692ac09fa93f8482%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to