Best way to test handle optional FKs in model __str__?

15 views
Skip to first unread message

Michael H

unread,
Jan 17, 2015, 10:02:57 AM1/17/15
to django...@googlegroups.com
Hello,

In my model I have:

def __str__(self):
   
   
return _(u'#{0}, {1}, {2}').format(
       
self.pk,
       
self.lead and self.lead.customer_name or None,
       
self.lead and self.lead.customer_phone or None
   
)

Where self.lead is an optional FK:

lead = models.ForeignKey('Lead', blank=True, null=True,)

If I don't use the and/or syntax above, and the FK doesn't exist, I get:

AttributeError: 'NoneType' object has no attribute 'customer_name'

While the above and/or syntax works, I'm left wondering if there's a better way to handle this scenario?

I have several models where the FK may or may not exist upon save. Can anyone give me tips on how best to handle?

Thanks!
M

Vijay Khemlani

unread,
Jan 17, 2015, 3:02:05 PM1/17/15
to django...@googlegroups.com
I don't think there is a way in Python to do that directly

you could have a utility method that catches the exception, for example

def get_fk_field(obj, fk_field):
    try:
        return getattr(obj, fk_field)
    except AttributeError:
        return None

so that your call would be 

return _(u'#{0}, {1}, {2}').format(
        self.pk,
        get_fk_field(self.lead, 'customer_name')
        get_fk_field(self.lead, 'customer_phone')
    )

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/e5e2a0ea-8fb0-4acb-a84d-edccc64d7a62%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Micky Hulse

unread,
Jan 17, 2015, 9:01:06 PM1/17/15
to django...@googlegroups.com
Hi Vijay!

Thank you so much for the reply/help, I really appreciate it. :)

On Sat, Jan 17, 2015 at 7:01 AM, Vijay Khemlani <vkhe...@gmail.com> wrote:
> I don't think there is a way in Python to do that directly
> you could have a utility method that catches the exception, for example

Great, that's what I wanted to know. Thank you for pushing me in the
right direction and for the sample code.

Have a great day!

Cheers,
M

Micky Hulse

unread,
Jan 17, 2015, 10:40:48 PM1/17/15
to django...@googlegroups.com
On Sat, Jan 17, 2015 at 12:58 PM, Micky Hulse <micky...@gmail.com> wrote:
> Great, that's what I wanted to know. Thank you for pushing me in the
> right direction and for the sample code.

Thanks again Vijay! After doing a bit of research, I slightly modified
your code (I needed an easy way to check nested attributes):

from operator import attrgetter
def get_fk_field(obj, path):
'''
Returns (nested) attribute if it exists.
Examples:
get_fk(self, 'vehicle.manufacturer.title')
get_fk(self, 'vehicle.model')
'''
try:
attr = attrgetter(path)
return attr(obj)
except AttributeError:
return None

From what I know, operator.attrgetter works in Python 2.7 and above.

Anyone, pease let me know if the above code could be optimized.

Thanks again!
M

James Schneider

unread,
Jan 17, 2015, 10:53:14 PM1/17/15
to django...@googlegroups.com

You can reroll your original example like this:

def __str__(self):

    pk = self.pk
    customer_name = None
    customer_phone = None

    try:
        customer_name = self.lead.customer_name
        customer_phone = self.lead.customer_phone
    except AttributeError:
        pass

    return _(u'#{0}, {1}, {2}').format(

        pk,
        customer_name,
        customer_phone,
    )

Much more pythonic, IMO.

-James

Micky Hulse

unread,
Jan 17, 2015, 10:58:17 PM1/17/15
to django...@googlegroups.com
Hi James! Thank you for the reply and for the example code!

On Sat, Jan 17, 2015 at 2:52 PM, James Schneider
<jrschn...@gmail.com> wrote:
> You can reroll your original example like this:
> Much more pythonic, IMO.

Ah, that's great! I was actually wondering what the pythonic way of
doing this might be. While it's more lines, I like that it is more
readable/understandable. Much easier to pick up the code later on.

Much appreciated!

Cheers,
M

James Schneider

unread,
Jan 17, 2015, 11:00:37 PM1/17/15
to django...@googlegroups.com

Yes, it's a bit more verbose, but much easier to follow. Hope it helps.

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Micky Hulse

unread,
Jan 17, 2015, 11:04:47 PM1/17/15
to django...@googlegroups.com
On Sat, Jan 17, 2015 at 3:00 PM, James Schneider
<jrschn...@gmail.com> wrote:
> Yes, it's a bit more verbose, but much easier to follow. Hope it helps.

Definitely! Thanks again!

Python rocks. :)

Cheers,
M

Tim Chase

unread,
Jan 18, 2015, 2:14:22 AM1/18/15
to django...@googlegroups.com, vkhe...@gmail.com
On 2015-01-17 12:01, Vijay Khemlani wrote:
> I don't think there is a way in Python to do that directly
>
> you could have a utility method that catches the exception, for
> example
>
> def get_fk_field(obj, fk_field):
> try:
> return getattr(obj, fk_field)
> except AttributeError:
> return None

I'm pretty sure that getattr() takes an optional 3rd field for the
default, so you could use

def __str__(self):
return _(u'#{0}, {1}, {2}').format(
self.pk,
getattr(self.lead, "customer_name", None),
getattr(self.lead, "customer_phone", None),
)

-tkc



Micky Hulse

unread,
Jan 18, 2015, 3:17:20 AM1/18/15
to django...@googlegroups.com
On Sat, Jan 17, 2015 at 6:15 PM, Tim Chase
<django...@tim.thechases.com> wrote:
> I'm pretty sure that getattr() takes an optional 3rd field for the
> default, so you could use

Great point, that's very useful. Thanks for clarifying!

It looks like attrgetter doesn't have similar functionality, but
there's this (found after a quick Google search):

<https://github.com/bradjasper/attrgettersetter>

Thanks for example code Tim!

Cheers,
m

James Schneider

unread,
Jan 18, 2015, 4:10:11 AM1/18/15
to django...@googlegroups.com

You can definitely do it that way if all you need to do is check if values exist and provide a default if they don't. I do this all the time for my __str__() methods as well.

You also mentioned similar situations that may require more logic checks (variables depending on each other or printing computed values), then the try/except strategy is likely more flexible.

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.

Micky Hulse

unread,
Jan 18, 2015, 4:18:23 AM1/18/15
to django...@googlegroups.com
On Sat, Jan 17, 2015 at 8:09 PM, James Schneider
<jrschn...@gmail.com> wrote:
> You can definitely do it that way if all you need to do is check if values
> exist and provide a default if they don't. I do this all the time for my
> __str__() methods as well.
> You also mentioned similar situations that may require more logic checks
> (variables depending on each other or printing computed values), then the
> try/except strategy is likely more flexible.

Thanks for reply and advice James, I really appreciate it. :)

It's funny that I haven't had this problem before … Either I've never
gone too far down the __str__() FK chain and avoided AttributeErrors
or I've always had FKs so that were required (so, no error on save).

Anyway, I really appreciate the reply and tips/advice.

Thanks everyone! Django sure does have an awesome open source community.

Cheers,
m
Reply all
Reply to author
Forward
0 new messages