Django:鸭子打字友好的方式接受记录或slug记录的输入

时间:2012-04-03 22:00:35

标签: django duck-typing

我有这样的功能:

def foo(bar):
    ...

现在bar可以是Django记录,也可以是指向记录的slug。我需要这种灵活性,以便我可以在手边有实际记录时调用此函数,或者从一个更通用的函数调用此函数,该函数只有一个字符串可用作为字符串(从数据库中提取)。

我意识到我可以做类似的事情:

def foo(bar):
    try:
        bar.pk
    except AttributeError:
        bar = Bar.objects.get(slug=bar)
    ...

但这似乎并不优雅

如果我可以,我想避免使用isinstance。

3 个答案:

答案 0 :(得分:5)

根据定义,您不使用Duck Typing。 Duck Typing说“如果它像鸭子一样说话,看起来像鸭子,那就是鸭子。”

Duck Typing意味着您可以将两个完全不同的类的对象传递给该方法并使其工作,因为它们都实现了相同的方法/属性(或者优雅地处理丢失的方法/属性)。这意味着该方法从不关心它获取的类型,只是传递它的任何对象都具有它期望使用的属性。

在您的情况下,您希望有时传递一个对象和一个可用于在其他时间查找所述对象的字符串。这与Duck Typing无关。

isinstance是解决这个问题的正确方法。在这种情况下,这是解决问题的最明确方法,其他任何事情都会变得更复杂,更难理解有0个好处。您可以在属性或hasattr上使用try / except,但这可能会让任何未来的开发人员比其他任何事情更困惑。 Duck Typing非常棒,它取代了各种类型的子类来匹配某些特定的函数,但在这种情况下,duck typing不适用。

简而言之。只需使用isinstance。对于你的情况,这是正确的(pythonic)方式。

答案 1 :(得分:1)

我不确定这是处理这种情况的可怕方法,但如果我想做类似的事情,我可能会使用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))

答案 2 :(得分:0)

这是另一种有助于您想要做同样功能的方法。我会假设您使用的模型名称是“项目”。

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):

    ...