cap_mkt_tools
cap_mkt_tools

Reputation: 98

In Django ForeignKey relationship does not get saved correctly when saving two objects

I have two models that represent a one-to-many relationship. The InterestRateCurve object can have many InterestRate objects. The issue is that when I have a function that creates two curves where each curve has one rate the rate of the second curve overwrites the rate of the first curve. So, I end up in the database with two rows for the curve and one row for the rate. When I execute a program that saves one curve and one rate and then execute a separate program that saves another curve with a rate then it's all fine.

I use Python 2.7 and Django 1.4.

MODELS:

class InterestRate(models.Model):
    type = models.CharField(max_length=20)
    term = modelFields.tgTimePeriodField(max_length=20)
    numTerms = models.IntegerField()
    mid = models.FloatField()
    curve = models.ForeignKey('InterestRateCurve')

    class Meta:
        unique_together = ('type', 'term', 'numTerms', 'curve')

    def __unicode__(self):
        return "%s/%s/%s/%s" % (self.type, self.term, self.numTerms, self.curve)

    def __str__(self):
        return "%s/%s/%s/%s/%s" % (self.type, self.term, self.numTerms, self.mid, self.curve)


class InterestRateCurve(models.Model):
    ccy = modelFields.tgCurrencyField(max_length=20)
    index = models.CharField(max_length=20)
    term = modelFields.tgTimePeriodField(max_length=20)
    numTerms = models.IntegerField()
    asOf = modelFields.tgDateField()
    cvid = models.CharField(max_length=20, blank=True, default='')
    #rates needs to be explicitly maintained inside class for convenience
    rates = []

    class Meta:
        unique_together = ('ccy', 'index', 'term', 'numTerms', 'asOf', 'cvid')

    def __unicode__(self):
        return "%s/%s/%s/%s/%s/%s" % (self.ccy, self.index, self.term, \
                                      self.numTerms, self.asOf, self.cvid)

    def save(self):
        try:
            curve = InterestRateCurve.objects.get(ccy=self.ccy, index=self.index,\
                                                  term=self.term, numTerms=self.numTerms,\
                                                  asOf=self.asOf, cvid=self.cvid)
            #Delete all rates before saving the new rates
            oldRates = curve.interestrate_set.all()
            for oldRate in oldRates:
                oldRate.delete()
            for rate in self.rates:
                rate.curve = curve
                rate.save()
        except InterestRateCurve.DoesNotExist:
            super(InterestRateCurve, self).save()
            curve = InterestRateCurve.objects.get(ccy=self.ccy, index=self.index,\
                                                  term=self.term, numTerms=self.numTerms,\
                                                  asOf=self.asOf, cvid=self.cvid)
            for rate in self.rates:
                rate.curve = curve
                rate.save()

    def addRate(self, rate):
        rate.curve = self
        exists = False
        for r in self.rates:
            if r.type == rate.type:
                if r.term == rate.term:
                    if r.numTerms == rate.numTerms:
                        exists = True
                        continue
        if exists == False:
            self.rates.append(rate)

The following code works fine:

        date = tgDate(month=9,day=12,year=2011)
        curve = InterestRateCurve(ccy='USD', index='LIBOR', term='M', 
                                  numTerms=3, asOf=self.asOf, cvid='TESTSAVE2CURVES')
        curve.save()
        curve = InterestRateCurve.objects.get(ccy='USD', index='LIBOR', term='M', 
                                              numTerms=3, asOf=date, cvid='TESTSAVE2CURVES')
        rate = InterestRate(term='M', numTerms=1, type='Deposit', mid=0.01, curve=curve)
        curve.addRate(rate)
        curve.save()

This execution results in one curve and one rate saved.

In a seperate execution I run:

        date = tgDate(month=9,day=13,year=2011) #DATE DIFFERS
        curve = InterestRateCurve(ccy='USD', index='LIBOR', term='M', 
                                   numTerms=3, asOf=date, cvid='TESTSAVE2CURVES')
        curve.save()
        curve = InterestRateCurve.objects.get(ccy='USD', index='LIBOR', term='M', 
                                              numTerms=3, asOf=date, cvid='TESTSAVE2CURVES')
        rate = InterestRate(term='M', numTerms=1, type='Deposit', mid=0.01)
        curve.addRate(rate)
        curve.save()

This execution results in another curve and another rate saved.

However, the following execution does not work correctly:

    def testSave2Curves(self):
        date = tgDate(month=9,day=12,year=2011)
        curve = InterestRateCurve(ccy='USD', index='LIBOR', term='M', 
                                  numTerms=3, asOf=date, cvid='TESTSAVE2CURVES')
        curve.save()
        curve = InterestRateCurve.objects.get(ccy='USD', index='LIBOR', term='M', 
                                              numTerms=3, asOf=date, cvid='TESTSAVE2CURVES')
        rate = InterestRate(term='M', numTerms=1, type='Deposit', mid=0.01, curve=curve)
        curve.addRate(rate)
        curve.save()
        date1 = tgDate(month=9,day=13,year=2011)
        curve1 = InterestRateCurve(ccy='USD', index='LIBOR', term='M', 
                                   numTerms=3, asOf=date1, cvid='TESTSAVE2CURVES')
        curve1.save()
        curve1 = InterestRateCurve.objects.get(ccy='USD', index='LIBOR', term='M', 
                                               numTerms=3, asOf=date1, cvid='TESTSAVE2CURVES')
        rate1 = InterestRate(term='M', numTerms=1, type='Deposit', mid=0.01)
        curve1.addRate(rate1)
        curve1.save()

This results in two curves saved but only one rate saved. It seems that the second rate overwrites the first rate that was previously saved.

Upvotes: 2

Views: 539

Answers (1)

j0ker
j0ker

Reputation: 4139

The problem is the specification of rates = [] as a class variable of InterestRateCurve. Because of this definition, every object of InterestRateCurve accesses the same list of rates.

After you created your first curve and rate in your example, InterestRateCurve.rates contains the one created rate. Then you create a second curve and save it. InterestRateCurve.save() is executed, jumps in the except branch and does:

for rate in self.rates:
    rate.curve = curve
    rate.save()

Remember that self.rates (= InterestRateCurve.rates) contains your first rate? The loop sets the foreign key of this rate to your second curve and saves it. Now your first curve has no related rate anymore. The rate of your first curve belongs now to the second curve.

After that, you create a second rate with exactly the same attributes as your first rate. Since you specified in the InterestRate model:

class Meta:
    unique_together = ('type', 'term', 'numTerms', 'curve')

... there can only be one rate with exactly the same attributes. I'm not sure if the old one is deleted or the new one is not saved. However, the result is that you have only one rate object in the database, which belongs to your second curve.

Hope you understand what I mean. You should look at Python's pdb module. That's a debugger which allows you to step through your code and is extremely helpful with such problems.

Upvotes: 1

Related Questions