I'd go for a separate table instead, with something like class BillingInfo(models.Model): customer = models.OneToOneField(Customer) billing_address = models.OneToOneField(Address)
and then just have logic in the views and templates to account for existence/nonexistence of a given customer's billing address. On Dec 28, 9:21 am, Dan Gentry <dgen...@gmail.com> wrote: > Just looking at the models, I'd like to make a couple of suggestions. > > Instead of using a Foreign Key relationship in Customer to indicate > the billing address, I would include a flag called 'billing_address' > in the Address table that would be set to True for the customer > selected address. > > An override of save() in the Address model is then used to enforce the > 'only one billing address' rule. > > def save(self): > ''' This will turn off the billing address flag for all other > addresses for this customer if the new record is selected ''' > if self.billing_address: > old_billing_address = > Address.objects.filter(customer=self.customer).filter(billing_flag= > True) > for s in old_billing_address: > s.billing_flag = False > s.save() > super(Address, self).save() > > Hope this helps, > > Dan > > On Dec 28, 6:02 am, Bart Nagel <b...@tremby.net> wrote: > > > > > > > > > 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.