2012-04-03 7 views
1

のレコードのレコードまたはスラグを受け入れるのダックタイピングに優しい方法で私はこのような機能を持っている:barジャンゴ:入力

def foo(bar): 
    ... 

は、いずれかのDjangoのレコードまたはレコードを指しているスラグすることができます。実際のレコードがあるときにこの関数を呼び出すことができるように、またはデータベースから取り出した文字列としてしか利用できないより一般的な関数から呼び出すことができるように、この柔軟性が必要です。

私はのような何かを行うことができます実現:

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

しかし、これはすべてのでエレガントないないようです。

私はisinstanceを使用しないでください。

+0

※isinstance *を使用しないでください。* - 特に理由はありますか? – MattH

+0

なぜ2つの異なる機能を作成しないでください。 'foo_with_slug'という新しいものがルックアップを行い、' foo'メソッドを呼び出します。 – sdolan

+0

習慣の力、私は思いますか?私は実際には、私の関数が実際には、文字列や文字列を取ることができるかどうかなど、ダックタイピングによって実際には欺かれる*ときにそれを使うのが好きです。 –

答えて

5

あなたはダックタイピングを使用していない定義です。ダックタイピングは、「もしアヒルのように話し、アヒルのように見えたら、それはアヒルです」と言います。

ダックタイピングは、全く異なるクラスの2つのオブジェクトをメソッドに渡し、両方が同じメソッド/属性を実装する(または欠落しているものをうまく処理する)ため、機能することを意味します。これは、メソッドがどのタイプを取得するかについて決して気にしないことを意味します。つまり、渡すオブジェクトには、使用すると予想される属性があります。

場合によっては、オブジェクトを時々渡したい場合や、そのオブジェクトを別の時間にルックアップするために使用できる文字列を渡したい場合があります。これはDuck Typingとは関係ありません。

isinstanceはこれを解決する正しい方法です。この場合、これは問題を解決する最も明白な方法であり、他のものはより複雑になり、理解しにくいという利点はありません。あなたは属性やhasattrに対してtry/exceptを使うことができますが、それは将来の開発者を何か他のものよりも混乱させる可能性があります。 Duck Typingは、さまざまなサブクラスをいくつかの特定の関数にマッチさせる代わりにキャストを置き換えることは素晴らしいですが、この場合はダックタイピングは適用されません。

要するに、ちょうどisinstanceを使用してください。あなたの場合は、それを行うには正しい(ピジョン)方法です。

+2

'django.db.models.fields.related'をチェックしただけで、外部キーの割り当てを検証するためにisinstanceを使います。このようにダックタイピングを説明してくれてありがとうJohnさん、ありがとうございました。それは、私が「isinstance」回避者の種類を区別するのに役立ちます。私はこの状況に有益である理由を知りたいと苦労していました。 – MattH

+0

@MattH合意。私は、非常に多くの "isinstance有害と見なされた"(http://www.canonical.org/~kragen/isinstance/)タイプの記事に触れました。この記事では、「isinstance」の使用はPythonプログラミングでは実質的に動詞であると仮定しました。 –

+1

@JordanReiter:その記事では、私が見た 'isinstance'憎しみのいくつかについて説明します。私はその点を見ることができますが、私は記事のこの行を強調する必要があると思います。*時々、この約束を破ることは価値があります。しかし、それは新しいプログラマーのための罠です。注意してください!あなたが何をしているのか分からない限り、isinstanceは使用しないでください。あなたのコードを拡張不可能にして、奇妙な方法で破損させることができます。* – MattH

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)) 
+0

Unicodeサポートには何が起こったのですか? – MattH

+0

@MattH私はこれがユニコードをより良く扱うことができると思います。 – cwallenpoole

+0

何かが見つからないかぎり、 'str(bar)'を 'unicode(bar)'に置き換えて、ユニコードのサポートと同じ結果を得ることができます。 –

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

    ...