Indeed, O2O relationships can get tricky if you want to use commands such as dumpdata
and loaddata
, for example, to backup and restore select objects in your database.
We have a similar issue with our software and I found that a possible working solution would be to override the save()
method on django.core.serializers.base.DeserializedObject
to actually get a handle into the moment just before the "double" object is saved. At this point, you may decide to throw away the default O2O relation created by Django and let the framework save the new one or update it with the stored values on your XML or JSON file.
You'd have to put the overwritten method somewhere Django picks up before the loaddata
command is executed though. One possibility is to create your own command that in turn calls loaddata
. In the command module, you install the override. The following details on this solution are implied:
- Tested with Django 1.8.x
- Our O2O field is attached to the Django
User
model
- For the sake of simplicity in this example, I'll call the attached O2O field
Attached
.
# Overrides deserialization to affect OneToOneFields for Users correctly
import django.core.serializers.base
from django.contrib.auth.models import User
from your.attached.models import Attached #model with O2O field to User
_original_save = django.core.serializers.base.DeserializedObject.save
def save(self, *args, **kwargs):
if isinstance(self.object, Attached):
# if the user in question has an attached object, delete it
user = User.objects.get(pk=self.object.user_id)
if hasattr(user, 'attached'): user.attached.delete()
# use the built-in function for all other cases
_original_save(self, *args, **kwargs)
django.core.serializers.base.DeserializedObject.save = save
You may modify the code above for, instead of deleting the existing object, updating it if you, at the if hasattr(...)
clause, avoid the deletion, update the existing object with the values coming from your serialized object and skip the call to _original_save()
. It will make the code a bit more tied to model though, as you may have to define which fields to update on the existing object. The solution shown above makes no assumption on the contents of the model.