I need help doing a linked lookup in admin

31 views
Skip to first unread message

Richard E. Cooke

unread,
Apr 26, 2013, 10:04:13 AM4/26/13
to django...@googlegroups.com
I used the django-contacts project as my starting point for a company contact database.  Its really cool, it keeps "addresses", "phone numbers", etc in seperate db table so you can associate as many as you need to each company or person record.  It uses django.contrib.contenttypes for its relations.  Which is also pretty cool since that lets you attach comments to any other record!

Anyway, I want to build on this by adding an Inventory and Purchasing system.

In my Inventory model I have a field for supplier:

  supplier = models.ForeignKey('contacts.Company', blank=True, null=True)

Which works perfect, you get a pop-up list to pick the lucky vendor from.  Later I might add a filter to limit the choices to a particular type of company - like vendors.  But this is fine for now.

A company record can (and will) have multiple "StreetAddresses" records associated with it through a GenericRelation.  For the PO I want to be able to select one out of that set of addresses.

So for a test, I tried:

  ship_to = models.ForeignKey('contacts.StreetAddress', limit_choices_to={'content_type':27, 'object_id':1, 'location':'shipto'}, blank=True, null=True)

"content_type", "object_id" are the content types fields used to control the Generic Relation.  And those values correspond to pk=1 for records of type "company".  And the "location" field indicates the type of address record.

This works.  I get a list of "ship to" addresses for the company (pk=1).

So, now I want to re-jig this to use the current PO record's setting for "supplier" to automatically limit the address selection.

There might be a way to capitalize on the fact that supplier.street_address  is a GenericRelatedObjectManager seeded with the right values. 

In shell, I read in a PO record with a = PO.objects.get(pk=1)
Then I enter a.supplier.street_address.filter(location="shipto") I get a list of all the "shipto" addresses for the supplier!  Exactly the list I want to be able to pick one from.

But I haven't a clue how to make use of this in the Admin system.  Any ideas appreciated!

I tried making a ForeignKey field to the StreetAddress db, and filter it using values from the supplier record currently in memory:
  ship_to = models.ForeignKey('contacts.StreetAddress', limit_choices_to={'content_type':F('po__supplier__street_address__content_type__id'), 'object_id':F('po__supplier__pk'),}, blank=True, null=True)

In Admin, this returns an empty list of addresses. 

To debug, I went into shell, and tried:

b = StreetAddress.objects.filter(content_type=F('po__supplier__street_address__content_type__id')) 
>>> b
[]

does "F()" write a log someplace that will tell me where this falls apart?  Well, I don't see how it would know what PO record to get "supplier" from.  Or maybe its better to say I don't understand how it determines where to get any of its data from when your walking relations.The Django manual entry for 1.4 is pretty vague on details.

Or, is there a better way to do this?

Thanks in Advance!



Richard E. Cooke

unread,
Apr 29, 2013, 4:46:52 PM4/29/13
to django...@googlegroups.com
I'm getting closer! 

First I found this note in the Content Types docs that explains why what I was doing does NOT work:
https://docs.djangoproject.com/en/1.4/ref/contrib/contenttypes/#django.contrib.contenttypes.generic.GenericForeignKey

Due to the way GenericForeignKey is implemented, you cannot use such fields directly with filters (filter() and exclude(), for example) via the database API.


Then I found this sample code in the admin docs:
https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey


That I adapted to this for my admin form:
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "ship_to":
            kwargs["queryset"] = StreetAddress.objects.filter(po__supplier__street_address__location="shipto")
        return super(POAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Note that while this is my "PO" data model, I used the model belonging to the address data "StreetAddress".  This is because which model does the object.filter() is the one whose __unicode__ function is called to get a representation of each record found for the list selection dialogue.  Which means, for my PO database, it describes each address by its PO number (which are the same number) making it hard to tell the addresses apart!  By using the StreetAddress model, it uses its __unicode__ instead, which makes a mini-summary of each address.  Much clearer.


This is not a perfect solution though because when I try to SAVE an edited PO record I get an error:

MultipleObjectsReturned: get() returned more than one StreetAddress -- it returned 2! Lookup parameters were {'id': u'2'}

I'm going to go back to letting it list all "ship to" addresses for all companies until I figure out what this error even means!

Another problem is its listing both address choices with the same description.  Another mystery to ponder.


Reply all
Reply to author
Forward
0 new messages