views:

1551

answers:

5

I know this question has been posted multiple times but I still couldn't find a definite answer to this problem. So, here I go:

class Invoice(models.Model):
    program = models.ForeignKey(Program)
    customer = models.ForeignKey(Customer, related_name='invoices')
    participants = models.ManyToManyField(Participant, related_name='participants_set')
    subtotal = models.DecimalField(max_digits=10, decimal_places=2, default='0.00', blank=True, null=False)
    pst = models.DecimalField("PST", max_digits=10, decimal_places=2, default='0.00', blank=True, null=False)
    gst = models.DecimalField("GST", max_digits=10, decimal_places=2, default='0.00', blank=True, null=False)
    total = models.DecimalField(max_digits=10, decimal_places=2, default='0.00', blank=True, null=False)

    def save(self, **kwargs):
        super(Invoice, self).save(**kwargs)
        items = self.participants.count()
        subtotal = Decimal(self.program.fee) * items
        pst = self.program.is_pst and Decimal(PST)*subtotal or Decimal('0.00')
        gst = self.program.is_gst and Decimal(GST)*subtotal or Decimal('0.00')
        total = (subtotal + pst) + gst
        self.subtotal = subtotal
        self.pst = pst
        self.gst = gst
        self.total = total
        super(Invoice, self).save(**kwargs)

Everything works fine except self.participants.count() doesn't work. Any idea what could be the problem. Any help much appreciated.

A: 

Instead of overriding the save method, I'd recommend using the pre-save signal. Besides making your code a bit cleaner, it helps avoid strange issues like these :)

mikl
A: 

What version of Django? Your call to self.participants.count() should work.

1.0.2 stable

Does the count method raise an exception, or does it give the wrong value?

Always gives 0.

Instead of overriding the save method, I'd recommend using the pre-save signal.

I'll give it a try. Thanks

A: 

I think what's happening is because you are trying a participants count during a save, the query may not be finding everything. If you're depending on this number at database creation time, I don't think the many-to-many table will be synced up correctly because the Invoice doesn't have an ID assigned yet.

Conversely, the other participants may not be saved to the database. Either way, depending on this number during a save won't work, regardless of using signals. I would recommend having a separate method that does this computation. It's cleaner, it improves save performance, and you can call it without saving.

Quartz
Sounds like a good idea to separate the participants altogether and just record the quantity (number of participants). That way the Invoice model would be more generic.
A: 

I had a similar problem. I had a model that supported del.icio.us style tags. The save function would parse a list of the tags (e.g. "python django web") and convert them into individual tag object instances by calling a helper function *update_tags()* (see below for a simplified example). However, the ManyToManyField would not reflect the changes when I edited the object within the admin interface.

class Article(models.Model):
    tag_string = models.CharField(max_length=255, null=True, blank=True) #del.icio.us style tags, like: django python software
    tags =  models.ManyToManyField(Tag, blank=True)

    def save(self, force_insert=False, force_update=False):
        super(Article, self).save(force_insert, force_update)

        self.update_tags() #The result of this function didn't seem to be saved in the ManyToManyField

It turns out that the admin interface was overriding the changes to the ManyToManyField. The solution was simply to remove the ManyToManyField from the admin.ModelAdmin:

class ArticleAdmin(admin.ModelAdmin):
    exclude = ['tags']
Luke Murphey
+1  A: 
self.participants.all().count()
bloodzomboid