Is there a way to keep models that reference each other twice?

My problem is keeping new models to reference each other and not just search related_name

like:

class Many:
    owner = models.ForeignKey('One')

class One:
    current = models.OneToOneField('Many')

      

By default they have null=False

and please correct me if I am wrong, they cannot be used until I change one of the relations:

    current = models.OneToOneField('Many', null=True)

      

The reason is that you cannot assign a model to a relationship if it has already been saved. Otherwise, the result ValueError: 'Cannot assign "<...>": "..." instance isn't saved in the database.'

.

But now, when I create a couple of these objects, I need to save twice:

many = Many()
one = One()
one.save()
many.owner = one
many.save()
one.current = many
one.save()

      

Is this the correct way to do this, or is there another way to save twice?

+3


source to share


1 answer


There is no way to get around it, you need to save one of the objects twice.

This is because at the database level you need to store an object in order to get its id. Can't tell sql database "to store these 2 objects and assign ids to these fields on another object". So if you need to do it manually, you have to Insert the first object with NULL for FK, return its ID, Insert the second object with the ID of the first, return its ID, and then UPDATE the first object to set the FK. You have to encapsulate all of this in a transaction.

So what you do with the ORM is the closest you can get. You can add the following:

1) Use transaction for changes, for example:



from django.db import transaction

with transaction.atomic():
    many, one = Many(), One()
    one.save()
    many.owner = one
    many.save()
    one.current = many
    one.save(update_fields=['current']) # slight optimization here

      

2) Now this is encapsulated in the transaction you want to delete null=True

. But you cannot, as this is unfortunately checked immediately. [edit: it looks like Oracle can support deferring NOT NULL checks, so if you are using Oracle you can try dumping null=True

and it should work.]

You probably want to check how your code reacts if later when reading the db, if for some reason (manual editing, listening inserted somewhere, ...) one.current.owner != one

.

+3


source







All Articles