Re: [Django] #35741: Checking for whether a reverse OneToOne relation exists is ugly

12 views
Skip to first unread message

Django

unread,
Sep 7, 2024, 4:57:04 PM9/7/24
to django-...@googlegroups.com
#35741: Checking for whether a reverse OneToOne relation exists is ugly
-------------------------------------+-------------------------------------
Reporter: Chase Adams | Owner: (none)
Type: New feature | Status: new
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: OneToOne | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Chase Adams:

Old description:

> The official docs suggest this methodology for checking if a reverse
> OneToOne relation exists:
>
> {{{
> >>> from django.core.exceptions import ObjectDoesNotExist
> >>> try:
> ... p2.restaurant
> ... except ObjectDoesNotExist:
> ... print("There is no restaurant here.")
> ...
> There is no restaurant here.
> }}}
> ( https://docs.djangoproject.com/en/5.1/topics/db/examples/one_to_one/ )
>
> This is verbose and not consistent with ForeignKey reverse relations,
> which always have the xxx_set present on models with reverse FK
> relations.
>
> This is an ancient issue that has been discussed a lot:
> https://stackoverflow.com/questions/25944968/check-if-a-onetoone-
> relation-exists-in-django
>
> But it still comes up because it isn't obvious that the reverse OneToOne
> field if the forward relationship does not exist.
>
> Perhaps this has come up before, but I would suggest that a constant
> reverse relation field be added that allows the following syntax instead:
>
> {{{
> if not p2.restaurant
> print("There is no restaurant here.")
> }}}
>
> It would also be a good idea to enable assignment to the reverse OneToOne
> relation even when no relation exists:
>
> {{{
> p2.restaurant = p1.restaurant
> p2.save() # A DB transaction here that sets p1.restaurant.p to p2
> }}}
>
> That could also enable a small performance optimization in certain cases
> where p1 is loaded, but p1.restaurant is not, since p1 would have the id
> of p1.restaurant even when select_related is not used, and the ID could
> be used within the p2.save() transaction instead of having two separate
> transactions in the alternative below:
>
> {{{
> p1.restaurant.p = p2 # One transaction here to look up .restaurant so .p
> can be accessed
> p1.restaurant.save() # Another transaction here to save p1.restaurant.
> }}}
>
> While OneToOne is not as commonly used as other relations, I believe the
> verbosity and clunkiness of the current design would benefit from a
> refresh.
>
> The main concept I'd suggest for informing a better design would be that
> both the forward and reverse relations should be symmetrical in usage. In
> other words, for anything you can do on the forward relationship
> (checking if it exists, assigning it, etc), the same is possible on the
> reverse. This would be more implicit with the idea of a "One to One"
> relationship, where structurally neither model is a parent but instead
> both models share a peer relationship, and peers should have equal
> capabilities.

New description:

The official docs suggest this methodology for checking if a reverse
OneToOne relation exists:

{{{
>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
... p2.restaurant
... except ObjectDoesNotExist:
... print("There is no restaurant here.")
...
There is no restaurant here.
}}}
( https://docs.djangoproject.com/en/5.1/topics/db/examples/one_to_one/ )

This is verbose and not consistent with ForeignKey reverse relations,
which always have the xxx_set present on models with reverse FK relations.

This is an ancient issue that has been discussed a lot:
https://stackoverflow.com/questions/25944968/check-if-a-onetoone-relation-
exists-in-django

But it still comes up because it isn't obvious that the reverse OneToOne
field does not exist if no forward relationship exists for an instance.

Perhaps this has come up before, but I would suggest that a constant
reverse relation field be added that allows the following syntax instead:

{{{
if not p2.restaurant
print("There is no restaurant here.")
}}}

It would also be a good idea to enable assignment to the reverse OneToOne
relation even when no relation exists:

{{{
p2.restaurant = p1.restaurant
p2.save() # A DB transaction here that sets p1.restaurant.p to p2
}}}

That could also enable a small performance optimization in certain cases
where p1 is loaded, but p1.restaurant is not, since p1 would have the id
of p1.restaurant even when select_related is not used, and the ID could be
used within the p2.save() transaction instead of having two separate
transactions in the alternative below:

{{{
p1.restaurant.p = p2 # One transaction here to look up .restaurant so .p
can be accessed
p1.restaurant.save() # Another transaction here to save p1.restaurant.
}}}

While OneToOne is not as commonly used as other relations, I believe the
verbosity and clunkiness of the current design would benefit from a
refresh.

The main concept I'd suggest for informing a better design would be that
both the forward and reverse relations should be symmetrical in usage. In
other words, for anything you can do on the forward relationship (checking
if it exists, assigning it, etc), the same is possible on the reverse.
This would be more implicit with the idea of a "One to One" relationship,
where structurally neither model is a parent but instead both models share
a peer relationship, and peers should have equal capabilities.

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

Django

unread,
Sep 7, 2024, 4:59:10 PM9/7/24
to django-...@googlegroups.com
a peer relationship, and being peers implies having equal capabilities.

--
--
Ticket URL: <https://code.djangoproject.com/ticket/35741#comment:3>

Django

unread,
Sep 7, 2024, 5:00:18 PM9/7/24
to django-...@googlegroups.com
> both models share a peer relationship, and being peers implies having
> equal capabilities.

This would be more implicitly sound with the idea of a "One to One"
relationship, where structurally neither model is a parent but instead
both models share a peer relationship, and being peers implies having
equal capabilities.

--
--
Ticket URL: <https://code.djangoproject.com/ticket/35741#comment:4>

Django

unread,
Sep 9, 2024, 2:42:39 AM9/9/24
to django-...@googlegroups.com
#35741: Checking for whether a reverse OneToOne relation exists is ugly
-------------------------------------+-------------------------------------
Reporter: Chase Adams | Owner: (none)
Type: New feature | Status: closed
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: OneToOne | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Sarah Boyce):

* resolution: => duplicate
* status: new => closed

Comment:

Duplicate of #10227, #26633, #3016, #11053
--
Ticket URL: <https://code.djangoproject.com/ticket/35741#comment:5>
Reply all
Reply to author
Forward
0 new messages