8

As stated here objects are created automatically for models with OneToOne relationship to other models. So if I have Model1 with O2O to Model2, and will create object of Model2 with pk=1, then object of Model1 with model2_id=1 will be created autmatically. Then, if Ill dump data from DB to json, I will have two records for these objects. And if I will try to load this data to DB with loaddata - it will fail, because object for Model2 will be created twice and it will cause unique index violation and IntegrityError.
Does anybody found sane solution for this?

p.s.
I use Django 1.3.7

Gill Bates
  • 14,330
  • 23
  • 70
  • 138
  • i'd recommend to update django if you have to possibility to do so – Alp Oct 30 '13 at 09:01
  • 4
    Don't think there will be multiple objects in the dump data or created multiple times. Can you show us what dump data gives you? And error of loaddata as well? – Rohan Oct 30 '13 at 11:05
  • Agree with @Rohan. Are you certain their is no data in the database? – Brian Dant Nov 01 '13 at 23:28
  • Are you also certain that it's not just an issue with the index? Related: http://stackoverflow.com/a/19136200/785400 – Brian Dant Nov 01 '13 at 23:29
  • First of all, the automatic O2O is only relevant in a case of model inheritance, not all O2O relationships. Secondly, usually the problem with moving a database using dumpdata (and I'm talking out of exhausting experience) is with other things like different definitions of the max_length and other type of fields or already-populated db. Can you show us your error and some more information on the process that failed for you? – yuvi Nov 04 '13 at 10:25

4 Answers4

4

I did something similar, notwith JSON but with xml, and my django is 1.7, so maybe it does not work for you.

  1. You can use natural keys when you are referencing serialized objects. This will prevent mixing up items if the index is already used by an other objject.
  2. You can use dependencies to define the order of serialization (and therefore deserialization).

Maybe similar posts like this one are helpful as well.

Community
  • 1
  • 1
OBu
  • 4,977
  • 3
  • 29
  • 45
2

When you dump your data fixtures, make sure you use the --natural argument:

python manage.py dumpdata myapp --indent=4 --natural

https://docs.djangoproject.com/en/dev/ref/django-admin/#django-admin-option---natural

It looks its being deprecated in 1.7 for --natural-foreign

Adam Spence
  • 3,040
  • 1
  • 21
  • 17
0

You may end up using South, which makes data migrations both easy and powerful:

http://south.readthedocs.org/en/latest/about.html

Coming with Django 1.6, a new migrations module will replace the old database commands and make South obsolete.

Alp
  • 29,274
  • 27
  • 120
  • 198
  • migrations are coming with 1.7... But I don't think they are helping in this case (neither will south) – OBu Nov 01 '13 at 23:10
  • can you show me a source for the fact that migrations are coming with 1.7? – Alp Nov 02 '13 at 08:47
  • 1
    We had some discussions about it at https://code.djangoproject.com/ticket/21142 and there is a kickstarter project stating the same from the south author at http://www.kickstarter.com/projects/andrewgodwin/schema-migrations-for-django. I tested it in the 1.7 alpha version, but since the bug mentioned in the first link is not fully resolved, yet, it did not work out for me. – OBu Nov 02 '13 at 12:42
0

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.

André Anjos
  • 4,641
  • 2
  • 27
  • 34