Reputation: 18745
How to define OneToOne
relationship to the same Model
?
I have a model called Order
which can be paired with another one Order
. Now I'm trying to figure out how to handle models for this relationship.
My ideas:
class Order(models.Model):
paired_order = models.OneToOneField(self)
OR:
class Pairing(models.Model):
order1 = models.ForeignKey(Order, related_name='pairing')
order2 = models.ForeignKey(Order, related_name='pairing')
What do you think? Which is more efficient?
I want to have simple calling of paired ones. So I would do something like:
order.paired_order
OR:
order.pairing.paired
I want this relation symmetrical so for each pair of orders I call the same thing and get paired order.
Pairing model would be a good solution because I can add additional information to this relationship, but there is a problem that I would have to detect which order is it, so I couldn't call order.pairing.order1
because I don't know whether I'm not calling the same order.
EDIT:
>>> from _app import models
>>> order1 = models.Order(flight_number="xxx")
>>> order2 = models.Order(flight_number="yyy", paired_order=order1)
>>> order1.paired_order.flight_number
RETURNS None object has not ....
The problem is that when I set order1 is a paired order for order2, I want the same thing in opposite direction. So order1.paired_order = order2
do this as well order2.paired_order = order1
.
Upvotes: 6
Views: 5337
Reputation: 439
Having this problem myself, the term 'symmetrical' was key to finding the answer: https://code.djangoproject.com/ticket/7689
class Order(models.Model):
paired_order = models.OneToOneField(self)
def save(self, *args, **kwargs):
super(Order, self).save(*args, **kwargs)
self.paired_order.paired_order = self
super(Order, self.paired_order).save()
Upvotes: 3
Reputation: 5496
Pairing model would be a good solution because I can add additional information to this relationship.
In that case, you could model that group of "orders" (you've called it Pairing) and add a shortcut to retrieve the paired order.
class OrderPair(models.Model):
pass
# additional information goes here
class Order(models.Model):
pair = models.ForeignKey(to="OrderPair", related_name="orders")
# you'll have to add custom validation
# to make sure that only 2 orders can belong to the same "OrderPair"
@property
def paired_order(self):
return self.pair.orders.exclude(id=self.id).first()
Once you've got this working, you might also want to cache the paired order to avoid too many queries. In that case, you don't want a related name so you can use +
(the less explicit thing in Django ever).
class Order(models.Model):
...
cached_paired_order = models.ForeignKey(to='self', related_name='+')
@property
def paired_order(self):
if self.cached_paired_order:
...
else:
...
Upvotes: 3
Reputation: 1662
The ForeignKey
accepts as an argument not just a class, but also a string name of the form ForeignKey('ModelNameInSameModelsPyFile')
or ForeignKey('app_name.ModelName
).
In your case, it could be like
class Order(models.Model):
paired = models.ForeignKey('Order', null=True)
You can read more at https://docs.djangoproject.com/en/1.8/ref/models/fields/#foreignkey
Upvotes: 1