I'm new to Django. I'm finding it very impressive so far but have hit 
a cluster of problems. I'm using the SVN version.

Sorry for the length of this, I'm just trying to explain as fully as I 
can what I'm trying to do, what I have and what's going wrong.

The relevant parts of my model, in the simplest terms, look like this.

    from django.db import models

    class Address(models.Model):
        customer = models.ForeignKey("Customer")
        street = models.CharField(max_length=128)
        city = models.CharField(max_length=128)

        def __unicode__(self):
            return "%s, %s" % (self.street, self.city)

    class Customer(models.Model):
        last_name = models.CharField(blank=True, max_length=64)
        first_name = models.CharField(blank=True, max_length=64)
        billing_address = models.ForeignKey("Address", related_name="+")

        def __unicode__(self):
            return "%s %s" % (self.first_name, self.last_name)

So customers can have many addresses, and one of those addresses is 
pointed to by the customer as being the billing address.

I then have the Customer admin page set up so that Address entries are 
edited inline on the same form.

1. The billing address should be required, but obviously when it's a 
   new Customer there won't be any addresses on file, so there will be 
   no choices on the billing address dropdown.

   So I need a way to accept a blank selection for billing address, 
   maybe have it labelled as "use first address given below", and then 
   just complain if no addresses are given below.

   Later when there needs to be something to stop the billing address 
   from being deleted.

2. Related to the previous, there needs to be a constraint so there 
   must be at least one Address for each customer.

3. When editing an existing customer, only that customer's addresses 
   should be shown in the dropdown for billing address.

Here's what I've tried...

I set the billing address field to be optional for now.

Problem 3 seemed easiest so I decided to tackle that first, and made a 
bunch of customers and addresses so I could test with the database 
somewhat populated.

I found the ForeignKey.limit_choices_to in the documentation but since 
there's no "self" when I'm setting up the database fields I've no idea 
how I'd tell it to limit the options to something like 
self.addresses_set.all(), let alone have that updated as addresses are 
added, removed, edited in the inline form below.

I first posted this problem on Stacko Overflow 
(http://stackoverflow.com/questions/8637912/) and a suggestion was to 
use a ModelChoiceField. I had a look at the documentation and it 
wasn't obvious what the difference is between that an ForeignKey, plus 
it looks like I'd have exactly the same problem as above.

So I'm totally stuck on that one.

Next I had a go at the other two problems. It seemed to me (bearing in 
mind I'm a newbie here) it'd be best to add that logic to the 
Customer.clean() method -- I could check the number of addresses which 
had been entered there and raise an Exception if it was zero. At the 
same time I could set the billing address, if not already set, to the 
first address given. All sounds simple enough, but apparently not.

In the Customer.clean() method, I can't seem to get at what was posted 
as addresses. In there, self.address_set.all().count() is zero. I 
don't really see why -- I can get at the other data which was posted 
to the form as an object, why not the child objects which are being 
posted too?

Perhaps just too early. Following a suggestion in the same Stack 
Overflow thread mentioned above, I figured out how to set up a 
listeners for the pre_save and post_save signals and inspected the 
count of addresses at those points. It's still zero in both cases. 
Even after the save. That was very confusing but from what I've found 
while Googling it's something to do with the database transaction 
having not been finished yet. It seems counter-intuitive. Ideally I'd 
like to get at the Address objects before they're committed to the 
database so that I can roll back if necessary (in the case that there 
are no addresses), but after they're committed would do if there was 
no other way -- I could change the Customer object as necessary and 
re-save it or delete it. Not sure how I'd let the user know in that 
case.

But no -- empty address_set.all() at post_save time. I found a monkey 
patch to add a signal for post_transaction 
(https://gist.github.com/247844) and with a small tweak (possibly my 
Python or my Django is too old or new or something) it worked. I set 
up the listener and now I can get at the Address objects from the 
Customer object and edit the Customer if necessary (adding the first 
address as the billing address). But at this point it's too late to 
throw pretty exceptions if something goes wrong, which is a shame.

There's another problem there too. Once the billing address is set to 
one of the addresses and then the customer's addresses are later 
edited again I get horrific errors saying that the billing address is 
set to an unacceptable option. I think what's happening is that the 
addresses are being deleted and recreated, and so the reference in the 
billing address field now points to a non-existent primary key in the 
addresses table.

Since the above doesn't let me warn the user if they haven't entered 
any addresses, I needed another approach to let me do that. What I 
came up with was setting up a custom ModelForm for the Customer 
object's admin interface, and checking in the clean() method there. 
The best I could think to do was to print out dir() of various things 
to find likely looking methods and to see what data I had. The only 
reference to the addresses being entered I could find was in the 
self.data dict there. And it's messy, but I got a solution 
half-working by looking at self.data["address_set-TOTAL_FORMS"] and 
checking the number. But that doesn't cover all possibilities -- it 
might not exist (easy to deal with) and, slightly harder, some of the 
forms sent might have the "delete" box checked. So I had to count up 
to the number of forms and look for "address_set-%n-DELETE" keys. It's 
a mess and there's still a case it doesn't catch -- when a form is 
sent with the default (untouched) values and so doesn't actually end 
up creating an address. I'd see it as being an address and let it 
through, but then the billing address wouldn't have anything to point 
to. I didn't try to write code to handle that case because it's just 
getting way too messy.

So this solution is not working for me.

In fact, looking back, none of my solutions for any of the problems 
are adequate. I'm stuck.

Please help! Any suggestions are appreciated. Hopefully there are just 
some really easy things I've missed in the documents which will solve 
everything, but if the solution is a bit more in depth, so be it.

--bart nagel

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to