Sunday, January 6, 2008

getting to pesky foreign key data in django

Optimizing databases is a good practice and should be done for any enterprise applications, but as all things go there is a price to pay. The price you pay is when calling back and displaying the data into one can be real PITA. django is a damn good framework and allows you to do work fast with a pedal-to-the-metal feel but then certain amounts of work still has to be done when you have foreign keys defined.

Say you have a database with two tables : contacts and address. The relationship of the two tables are of the one to many kind, I could never really wrap my head around the one-many many-one many-many thingy so let me just say it in layman so I understand it better myself. "One contact can have one or more than one address"

class Contacts(models.Model):
..... bla bla

class Address(models.Model):

customer_id=models.ForeignKey(Contacts,edit_inline=models.STACKED,num_in_admin=2)


So in the code above you have basically linked both the tables together using a ForeignKey relationship. the 'edit_inline' ... mumbo jumbo just tells django to add the address field into the main Contacts form during creation and modification and not as a pop up link ( This is important and could be a clincher for normal users ).

Calling all the data back for display

Okay so now you are honky dory with a great admin form for you to add data into your database, now the problem starts when you want to call all the data back for display in your template.

Don't worry django comes with little helper functions, you just have to do a few more steps to display your foreignkey data.

For my case, in my view.py I do something like this :


from stock.models import Contacts

def main(request,id):
contact_list = Contacts.objects.get(id=id)
return render_to_response('stock/mytemplate.html',{'contacts':contact_list})

Probably the html formatting would made my code not valid in python, but just check ur identation especially after the function definition.

The few lines of code above basically expects you to send it an id ( from the url using the GET method ) and then get the Contacts object and then packs up that data object and sends you merrily along to your template with that.

Okay, a bit about the Contact objects. Inside your contact objects if you have a foreign key relationship it automatically create a method for you to access the data. The method is called _set. So following our example, we know that we have a relationship with the table Address, so we can expect that each contact object will have a method called 'address_set'. Still okay so far ? Lets go ....to get the data you normally call it like this :

contact_list.address_set.select_related()

This returns an orm object inside a list, so on your template you will call it like this :

{% for address in contact.address_set.select_related %}


Address: {{address.address|escape}}




{% endfor %}



That will bring up and display all your address for that particular contact. Done ! Easy peasy, well for me at least just comment me if think more clarification...

8 comments:

Alexey Blinov said...

Excellent writeup. The Django documentation does not make it very clear how to get the fk data inside the template and I was worried that I have to do it in code before I render the templates. Thanks for the tip.

Prudhvi Krishna said...

Thanks, that really helped me.

THE HIRE said...

Great explanation! Thanks.

Unknown said...

Excellent clarification loved it !

Now if I could only work out how to get the suburb only of the first address (using your example) without using a "for" to loop through all addresses and an "if" to only accept the first one.....

Joey said...

Hi, Thanks for the tip..

Instead of doing a for loop i've found you can do this.

{{querysetname.childojb_set.select_related|join:","}}

this join should comma separate the child objects if there are more than one, in our case there shouldn't be any.

http://i.nt.ro

Unknown said...

thanks a lot man..excellent explanation... i'm breaking my head for a whole 2 days for this...

reiven said...

I was looping in circules throgh the django documentacion about how to do this :) thanks!!!!

daveism said...

I tried this it did not work.