views:

50

answers:

3

I remodel my objects using ManyToMany relationship using "through" as it's guided here: link text

class Receipt(models.Model):
  name = models.CharField(max_length=128)
  (...)
  components = models.ManyToManyField(Product, through='ReceiptComponent')
  class Admin:
    pass

  def __unicode__(self):
    return self.name

  def url(self):
    return self.id

class ReceiptComponent(models.Model):
  product = models.ForeignKey(Product)
  receipt = models.ForeignKey(Receipt)
  quantity = models.FloatField(max_length=9)
  unit = models.ForeignKey(Unit)
  class Admin:
    pass
  def __unicode__(self):
    return unicode(self.quantity!=0 and self.quantity or '') + ' ' + unicode(self.unit) + ' ' + self.product.genitive

It looks ok, but I have 2 problems with it:

1) In admin management panel there's no easy connection with receipt = If I have to add a new component - I should go to components and make component connected to receipt - maybe it's the only solution - but it would be more intuitive in receipts

2) I can't print it using templates:

views.py:

(...)
def detail(request, receipt_id):
    receipt = get_object_or_404(Receipt, pk=receipt_id)
    components = receipt.components.all()
    return render_to_response('receipt.html',{'receipt' : receipt, 'components' : components,}
(...)

receipt.html:

<h1>{{ receipt.name }}</h1>
{% for component in components.all %}
<div class='component'>{{ component }}</div>
{% endfor %}
A: 

1) Have you tried inlines?

2) remove .all in your template, you don't need it. May be you will also need components = list(receipt.components.all())

Guard
A: 

1) looks perfect! (hint for other users: inline != inlines ;D

2) removing .all causes an exception:

Caught an exception while rendering: 'ManyRelatedManager' object is not iterable

but I understand that for good quality code I should move it from template to code:

views.py:

def detail(request, receipt_id):
  receipt = get_object_or_404(Receipt, pk=receipt_id)
  components = receipt.components.all()
  additionals = receipt.additionals.all()

  return render_to_response('drinkbook/receipts/receipt.html',{'receipt' : receipt, 'components' : components, 'additionals' : additionals, })

template:

h1>{{ receipt.name }}</h1>
{% for component in components %}
<div class='component'>{{ component }}</div>
{% endfor %}
{% if receipt.additionals %}Ponadto:
{% for additional in additionals %}
<div class='additional'>{{ additional }}</div>
{% endfor %}
{% endif %}
<p>{{ receipt.desc|safe }}</p>

Ok. It works now, but the result of component is Product.unicode not ReceiptComponent.unicode (which is a child of Product). Why?

SledgehammerPL
strange. One answer disappears...
SledgehammerPL
no... ;D the answer goes up ;D
SledgehammerPL
A: 

What you did with .all is exactly what I meant -- you had it initially in 2 places, .all() in view and .all in template.

The reason of your 'error' is quite evident - components is the m2m field to Product. This is what your wrote in your code. This components is the set of products, not intermediary model ReceiptComponent.

UPD: simply leave your models as are, and use receiptcomponent_set from the Receipt

Guard
Exactly!components = receipt.receiptcomponent_set.all()solves the problemPython D
SledgehammerPL