﻿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
34433	OneToOneField can only be saved one way	Alexis Lesieur	nobody	"Hi!
I encountered this unexpected (to me) behavior for work, and I have been able to replicate on a bare django app (albeit with slightly different symptoms).

The TLDR is that is model A has a OneToOneField to model B. The field had to be saved from the instance of model A, and that's not only not documented anywhere I could find, but counter-intuitive, and contradicts how other fields like ForeignKeys work.

**Setup:**
{{{
❯ python --version
Python 3.11.2

❯ pip freeze | grep -i django
Django==4.1.7

❯ django-admin startproject mysite

❯ cd mysyte/
❯ django-admin startapp myapp
❯ vim myapp/models.py
# partially re-using your example from https://docs.djangoproject.com/en/4.1/topics/db/examples/one_to_one/
```
from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):
        return ""%s the place"" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(
        Place,
        on_delete=models.CASCADE,
    )
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self):
        return ""%s the restaurant"" % self.place.name
```

❯ vim mysite/settings.py

[...]
INSTALLED_APPS = [
    'myapp.apps.MyappConfig',
[...]

❯ python manage.py makemigrations
❯ python manage.py migrate
}}}

Creating the initial objects:
{{{
❯ python manage.py shell
❯ from myapp.models import Place
❯ from myapp.models import Restaurant

❯ p1 = Place(name=""1st place"", address=""1st address"")
❯ p2 = Place(name=""2nd place"", address=""2nd address"")
❯ r1 = Restaurant(place=p1)
❯ r2 = Restaurant(place=p2)
❯ p1.save()
❯ p2.save()
❯ r1.save()
❯ r2.save()
❯ p3 = Place(name=""3rd place"", address=""3rd address"")
❯ p3.save()
}}}

This should give us a two restaurants with their respective places, and an additional place we can use to play.

First, what works:
{{{
❯ r1.place = p3
❯ r1.save()

❯ Restaurant.objects.get(id=1).place
<Place: 3rd place the place>

❯ p3.restaurant
<Restaurant: 3rd place the restaurant>

❯ Place.objects.get(id=1).restaurant
[...]
RelatedObjectDoesNotExist: Place has no restaurant.
}}}
This is all expected. `r1` now uses `p3`, which means that `p1` has no restaurant assigned.

Now I would expect, to be able to do the other way. Assign a new restaurant to a place, save, and be all good.
However that doesn't work.
First using plain `.save()` which fails silently:
{{{
❯ p1 = Place.objects.get(id=1)
❯ p1.restaurant = r1
❯ p1.save()

❯ Restaurant.objects.get(id=1).place
<Place: 3rd place the place>  # this should be p1
}}}

And when explicitly asking to save the field:
{{{
❯ p1.save(update_fields=[""restaurant""])
❯ ValueError: The following fields do not exist in this model, are m2m fields, or are non-concrete fields: restaurant
}}}

NB: on my use case for work (django 3.2.18) I was also getting the following error:
{{{
UniqueViolation: duplicate key value violates unique constraint ""response_timelineevent_pkey""
DETAIL:  Key (id)=(91) already exists.
}}}
I'm not sure why it's different, but it doesn't work either way.

This is problematic for a few reasons IMO:
- Unless I missed it, the docs really don't advertise this limitation.
- `.save()` ""fails"" silently, there is no way to know that the change didn't take, especially when this happens:
{{{
❯ p1 = Place(name=""1st place"", address=""1st address"")
❯ p2 = Place(name=""2nd place"", address=""2nd address"")
❯ p3 = Place(name=""3rd place"", address=""3rd address"")
❯ p1.save()
❯ p2.save()
❯ p3.save()
❯ r1 = Restaurant(place=p1)
❯ r1.save()
❯ r2 = Restaurant(place=p2)
❯ r2.save()

❯ r1.place
<Place: 1st place the place>
❯ p3.restaurant = r1
❯ r1.place
<Place: 3rd place the place>
❯ p3.save()
❯ Restaurant.objects.get(id=1).place
<Place: 1st place the place>
}}}
which leads to thinking the change is working and affecting both objects, when it's not.

It's also problematic as foreigh keys work this way: (from my work example)
{{{
❯ me = ExternalUser.objects.get(id=1)
❯ other = ExternalUser.objects.get(id=2)
❯ p = PinnedMessage.objects.get(id=11)

❯ p.author
<ExternalUser: first.last (slack)>  # i.e. `me`

❯ [p.id for p in me.authored_pinnedmessage.all()]
[1, 3, 5, 11]

❯ p.author = other
❯ p.save()

❯ [p.id for p in ExternalUser.objects.get(id=1).authored_pinnedmessage.all()]
[1, 3, 5]

❯ me.authored_pinnedmessage.add(p)
❯ me.save()

❯ PinnedMessage.objects.get(id=11).author
<ExternalUser: first.last (slack)>
}}}


Hopefully this is all enough explanation / details.
Let me know if you need anything else from me!
Thank you for your help."	Bug	new	Database layer (models, ORM)	4.1	Normal				Unreviewed	0	0	0	0	0	0
