I have a function like this:
def foo(bar):
...
Now bar
can either be a Django record or the slug pointing to a record. I need this flexibility so that I can call this function when I have the actual record at hand or am calling this from a more generic function that only has bar available as a string (pulled from a database).
I realize I can do something like:
def foo(bar):
try:
bar.pk
except AttributeError:
bar = Bar.objects.get(slug=bar)
...
But this doesn't seem elegant at all.
If I can I'd like to avoid using isinstance.
foo_with_slug
that does the lookup and then calls your foo
method - Sam Dolan 2012-04-03 22:05
isinstance
because you like avoiding isinstance... just to be clear - MattH 2012-04-03 22:08
isinstance
is bad form if only because then it requires an inheritance tree as opposed to simply requiring an attribute. It is FAR less ducky than his current solution - cwallenpoole 2012-04-03 22:09
bar
is being saved as a foreign key to another record (i.e. Foo.object.create(name=..., bar=bar)
- Jordan Reiter 2012-04-03 22:21
You are by definition not using Duck Typing. Duck Typing says that "if it talks like a duck and looks like a duck, it's a duck."
Duck Typing means you can pass two objects of completely different classes to the method and have it work because they both implement the same methods/attributes (or handles missing ones gracefully). This means that the method never cares about about what type it gets, just that whatever object you pass it has the attributes it expects to use.
In your case you want to pass an object sometimes and a string that can be used to look up said object other times. This has nothing to do with Duck Typing.
isinstance is the right way to solve this. In this case this is the clearest way to solve the problem, anything else is going to be more complicated, harder to understand have 0 benefits. You could use try/except on an attribute or hasattr, but that's likely to leave any future developers more confused than anything else. Duck Typing is great it replaces casting various subclasses around to match some particular function, but duck typing doesn't apply in this case.
In short. Just use isinstance. For your case it's the right (pythonic) way to do it.
django.db.models.fields.related
, it uses isinstance
for validating foreign key assignments. Thank you John for explaining Duck Typing in this way, it will help me differentiate the kinds of isinstance
-avoiders. I was struggling to see why it would be beneficial to this situation and you've cleared that up - MattH 2012-04-04 10:23
isinstance
is practically verboten in pythonic programming - Jordan Reiter 2012-04-04 13:35
isinstance
hatred I've seen. I can see the point, but I think this line from the article should be emphasized: Sometimes, of course, violating this promise is worth the payoffs --- isinstance, like goto, is not pure evil. But it is a trap for new programmers. Beware! Don't use isinstance unless you know what you're doing. It can make your code non-extensible and break it in strange ways down the line.MattH 2012-04-04 14:01
I'm not sure that that is a terrible way of handling that, but if I wanted to do something similar, I would probably use hasattr
:
def foo(bar):
if hasattr(bar,"pk"):
bar.pk
else:
# I include the str in case some other object with a __str__ happens
# to come through.
bar = Bar.objects.get(slug=str(bar))
str(bar)
with unicode(bar)
and get the same result along with unicode support - Jordan Reiter 2012-04-03 22:18
This is another way which will help in other functions you want to do the same. I'll asume the model's name you are using is 'Item'.
def slug_resilient_decorator(class_type):
def slug_resilient_wrapper(obj):
if obj.has_attr('pk'):
return obj
else:
return class_type.objects.get(slug=obj)
return wrapper
@slug_resilient_decorator(Item)
def slug_resilient_detail_view(obj):
...