Check if a OneToOne relation exists in Django
Asked Answered
E

5

39

Now I'm using django 1.6

I have two models relates with a OneToOneField.

class A(models.Model):
    pass

class B(models.Model):
    ref_a = models.OneToOneField(related_name='ref_b', null=True)

First see my code that points out the problem:

a1 = A.objects.create()
a2 = A.objects.create()
b1 = B.objects.create()
b2 = B.objects.create(ref_a=a2)

# then I call:
print(a1.ref_b)  # DoesNotExist Exception raised
print(a2.ref_b)  # returns b2
print(b1.ref_a)  # returns None
print(b2.ref_a)  # returns a2

Now the problem is, if I want to check a A object, to judge whether it exists a B objects referencing it. How can I do?

The valid way I tried is only to try and catch an exception, but is there any other prettier way?


My effort:

1 - The below code works, but is too ugly!

b = None
try:
    b = a.ref_b
except:
    pass

2 - I also tried to check the attributes in a, but not working:

b = a.ref_b if hasattr(a, 'ref_b') else None

Do you meet the same problem, friends? Please point me a way, thank you!

Esbjerg answered 20/9, 2014 at 3:44 Comment(3)
I think this will work for you, please keep in mind change your class name at least in 2 later. b = hasattr(a_class_object, 'b class') and a.ref_b or None. I am using same and working for me.Tolidine
@Yogesh I don't quite catch, could you please post a more detail solution?Esbjerg
hasattr seems to work fine for me on Django 1.8.17 with Python 2.Fouts
E
53

So you have a least two ways of checking that. First is to create try/catch block to get attribute, second is to use hasattr.

class A(models.Model):
   def get_B(self):
       try:
          return self.b
       except:
          return None

class B(models.Model):
   ref_a = models.OneToOneField(related_name='ref_b', null=True)

Please try to avoid bare except: clauses. It can hide some problems.

The second way is:

class A(models.Model):
    def get_B(self):
       if(hasattr(self, 'b')):
           return self.b
       return None

class B(models.Model):
    ref_a = models.OneToOneField(related_name='ref_b', null=True)

In both cases you can use it without any exceptions:

a1 = A.objects.create()
a2 = A.objects.create()
b1 = B.objects.create()
b2 = B.objects.create(ref_a=a2)

# then I call:
print(a1.get_b)  # No exception raised
print(a2.get_b)  # returns b2
print(b1.a)  # returns None
print(b2.a)  # returns a2

There is no other way, as throwing the exception is default behaviour from Django One to One relationships.

And this is the example of handling it from official documentation.

>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.
Extine answered 20/11, 2014 at 14:54 Comment(0)
C
15

Individual model classes provide a more specific exception called DoesNotExist that extends ObjectDoesNotExist. My preference is to write it this way:

b = None
try:
    b = a.ref_b
except B.DoesNotExist:
    pass
Cronyism answered 26/1, 2016 at 0:28 Comment(1)
why not put b = None in the except clause? (to save a line)Robb
M
14

hasattr works fine with Django1.11 ! You may use getattr for shorter version:

getattr(self, 'field', default)

In your case

b = getattr(a, 'ref_b', None)

https://docs.python.org/3.6/library/functions.html#getattr

Marilynnmarimba answered 4/7, 2018 at 9:34 Comment(0)
J
2

Much cleaner to use custom django Field based on OneToOneField, this approach which will allow you to use direct access - just a.ref_b and result would be instance or None

from django.db.models.fields.related import ReverseOneToOneDescriptor
from django.core.exceptions import ObjectDoesNotExist
from django.db import models

class SingleRelatedObjectDescriptorReturnsNone(ReverseOneToOneDescriptor):
    def __get__(self, *args, **kwargs):
        try:
            return super().__get__(*args, **kwargs)
        except ObjectDoesNotExist:
            return None

class OneToOneOrNoneField(models.OneToOneField):
    """A OneToOneField that returns None if the related object doesn't exist"""
    related_accessor_class = SingleRelatedObjectDescriptorReturnsNone

so the example will look like

class A(models.Model):
    pass

class B(models.Model):
    ref_a = OneToOneOrNoneField(related_name='ref_b', null=True)
Joule answered 22/3, 2023 at 12:13 Comment(0)
C
0

Extending mr. Coffee answer you can use context lib suppress method

import contextlib

class A(models.Model):
    def get_B(self):
       with contextlib.suppress(Exception):
           return self.b

class B(models.Model):
    ref_a = models.OneToOneField(related_name='ref_b', null=True)
Crutch answered 26/7, 2023 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.