45

タプルに依存するDjangoモデルがあります。Djangoプログラムでそのタプル内の定数を参照するためのベストプラクティスは何でしょうか。default=0ここでは、たとえば、より読みやすく、コメントを必要としないものとして「」を指定したいと思います。助言がありますか?

Status = (
    (-1, 'Cancelled'),
    (0, 'Requires attention'),
    (1, 'Work in progress'),
    (2, 'Complete'),
)

class Task(models.Model):
    status = models.IntegerField(choices=Status, default=0) # Status is 'Requires attention' (0) by default.

編集:

可能であれば、番号の使用は絶対に避けたいと思います。どういうわけか、代わりに文字列「注意が必要」を使用すると、より読みやすくなります。

4

10 に答える 10

72

整数値の定数を次のように定義することは非常に一般的です。

class Task(models.Model):
    CANCELLED = -1
    REQUIRES_ATTENTION = 0
    WORK_IN_PROGRESS = 1
    COMPLETE = 2

    Status = (
        (CANCELLED, 'Cancelled'),
        (REQUIRES_ATTENTION, 'Requires attention'),
        (WORK_IN_PROGRESS, 'Work in progress'),
        (COMPLETE, 'Complete'),
    )

    status = models.IntegerField(choices=Status, default=REQUIRES_ATTENTION)

定数をStatusモデルクラス内に移動することで、モジュールの名前空間をよりクリーンに保つことができ、ボーナスとして、モデルTask.COMPLETEをインポートする場所ならどこでも参照できますTask

于 2012-10-10T15:42:04.973 に答える
17
CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
Status = (
    (CANCELED, 'Cancelled'),
    (ATTENTION, 'Requires attention'),
    (WIP, 'Work in progress'),
    (COMPLETE, 'Complete'),
)

class Task(models.Model):
    status = models.IntegerField(choices=Status, default=CANCELED)


他の人が指摘したように、適切な方法はこれらの変数をModelクラス内に配置することであることに注意してください。これは、公式のdjangoのでも同じです。

クラス名前空間の外に配置したい理由は1つだけです。それは、これらのセマンティクスがアプリの他のモデルによって等しく共有されている場合のみです。つまり、それらがどの特定のモデルに属するかを決定することはできません。

これはあなたの特定の例には当てはまらないようですが。

于 2012-10-10T15:40:42.433 に答える
13

Python 3.4以降:Enum

あなたは「できれば数字を使わないようにしたい」と書いています。 実際、名前付き表現は明らかによりPython的です。ただし、裸の文字列はタイプミスの影響を受けやすくなっています。

Python 3.4には、 この状況を支援enumする提供Enumおよび疑似クラスと呼ばれるモジュールが導入されています。IntEnumこれを使用すると、例は次のように機能します。

# in Python 3.4 or later:
import enum  

class Status(enum.IntEnum):
    Cancelled = -1,
    Requires_attention = 0,
    Work_in_progress = 1,
    Complete = 2

def choiceadapter(enumtype):
    return ((item.value, item.name.replace('_', ' ')) for item in enumtype)

class Task(models.Model):
    status = models.IntegerField(choices=choiceadapter(Status), 
                                 default=Status.Requires_attention.value)

そして、DjangoチームがピックアップするEnumと、 choiceadapterDjangoに組み込まれることさえあります。

編集2021-06:との作業の後Enum、私は熱狂していないと言わなければなりません。私の仕事のスタイル(Djangoでは、マイレージは異なる場合があります)では、抽象化が邪魔になる傾向があり、定数の緩いリスト(とにかく存在する別のクラスに埋め込まれていることが多い)を好むことに気付きます。

于 2015-03-22T16:37:36.590 に答える
7

を使用できますnamedtuple。定数にImmutableを使用するのは適切なようです。;-)

>>> from collections import namedtuple
>>> Status = namedtuple('Status', ['CANCELLED', 'REQUIRES_ATTENTION', 'WORK_IN_PROGRESS', 'COMPLETE'])(*range(-1, 3))
>>> Status
Status(CANCELLED=-1, REQUIRES_ATTENTION=0, WORK_IN_PROGRESS=1, COMPLETE=2)
>>> Status.CANCELLED
-1
>>> Status[0]
-1

この場合、 Alasdairの回答のTaskように定数として属性を使用する方が理にかなっていますが、namedtuplesは、変更されないdictやオブジェクトの非常に安価な代替品です。それらをたくさんメモリに入れたい場合は特に便利です。それらは通常のタプルのようなもので、説明と属性へのアクセスのボーナスがあります。__repr__

于 2012-10-10T16:05:20.940 に答える
3

私のアプローチ:

class Task(models.Model):
    STATUSES = { 'cancelled': 'Cancelled',
                 'requires attention': 'Requires attention',
                 'work in progress': 'Work in progress',
                 'complete': 'Complete' }

    status = models.CharField(choices=STATUSES.items(), default='cancelled')

これにより、便利な式を書くことができます。

tasks = Task.objects.filter(status='complete')

また、不要なグローバル変数を作成しないようにすることができます。

本当に整数フィールドを使用したい場合:

class Task(models.Model):

   class STATUS:
      CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
      choices = {
        CANCELED: 'Cancelled',
        ATTENTION: 'Requires attention',
        WIP: 'Work in progress',
        COMPLETE: 'Complete'
      }


   status = models.CharField(choices=STATUSES.choices.items(), default=STATUSES.CANCELED)

と:

tasks = Task.objects.filter(status=Task.STATUSES.COMPLETE)
于 2012-10-10T15:25:00.663 に答える
3

考えられるアプローチの1つは、タプルを組み合わせてpython範囲関数を使用することです。

class Task(models.Model):
    CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
    Status = (
        (CANCELLED, 'Cancelled'),
        (REQUIRES_ATTENTION, 'Requires attention'),
        (WORK_IN_PROGRESS, 'Work in progress'),
        (COMPLETE, 'Complete'),
    )

    status = models.IntegerField(choices=Status, default=REQUIRES_ATTENTION)
于 2018-11-21T10:37:21.113 に答える
1

わかりやすくするために辞書を使用できます。

Status = {
    -1: 'Cancelled',
    0: 'Requires attention',
    1: 'Work in progress',
    2: 'Complete',
}

class Task(models.Model):
    status = models.IntegerField(choices=Status.items(), default=Status[0])
于 2012-10-10T15:24:05.077 に答える
0

私はDjangoを使用していませんが、PyramidとTwistedの下で次のようなことをかなり行っています...

def setup_mapping( pairs ):
    mapping = {'id':{},'name':{}}
    for (k,v) in pairs:
        mapping['id'][k]= v
        mapping['name'][v]= k
    return mapping

class ConstantsObject(object):
    _pairs= None
    mapping= None

    @classmethod
    def lookup_id( cls , id ):
       pass

    @classmethod
    def lookup_name( cls , name ):
       pass

class StatusConstants(ConstantsObject):
    CANCELLED = -1
    REQUIRES_ATTENTION = 0
    WORK_IN_PROGRESS = 1
    COMPLETE = 2

    _pairs= (
        (-1, 'Cancelled'),
        (0, 'Requires attention'),
        (1, 'Work in progress'),
        (2, 'Complete'),
    )
    mapping= setup_mapping(_pairs)

したがって、本質はこれです:

  • 基本の「定数」クラスと、タイプごとに別のクラスがあります。クラスは、キーワードをALLCAPSの値に定義します
  • 私も平文_pairsをクラスに投げ込みます。なぜ?それらを使用していくつかのDBテーブルを構築する必要がある場合や、エラー/ステータスメッセージ用にそれらが必要な場合があるためです。個人的な好みとして、ALLCAPS変数名ではなく数字を使用します。
  • 私はmappingクラス変数を初期化します。これは基本的に、dict内の変数の束をプリコンパイルすることによってクラスをmonkeypatchesします。理由は...
  • クラスはその基本クラスから派生します。この基本クラスは、値を検索したり、定数で頻繁に行う必要があるその他の標準的なことを実行したりするためのclassmethod機能を提供します。

これは万能のアプローチではありませんが、私は一般的にこれが本当に好きになりました。dictを使用してペアを簡単に定義し、「マッピング」関数に他の属性を設定させることができます。たとえば、ペア値のタプルをk、v、v、k、または必要になる可能性のある奇妙な形式で提供します。

私のコードは次のようになります。

status_id = sa.Column(sa.Integer, sa.ForeignKey("_status.id") , nullable=False , default=constants.StatusConstants.CANCELLED )

status_name = constants.StatusConstants.lookup_id(status_id)    
status_name = constants.StatusConstants.mapping['id'][status_id]

別の方法で定数を使用する必要があるときはいつでも、ベースのクラスメソッドを追加または変更するだけです。

于 2012-10-10T16:07:26.943 に答える
0

時々私はいくつかの巨大な選択リストを作成しなければなりません。私は猿のようにタイプするのが好きではないので、次のような関数を作成します。

def choices(labels):
    labels = labels.strip().split('\n')
    ids = range(1, len(labels)+1)
    return zip(ids, labels)

そして、このように使用します:

my_choices = """
choice1
choice2
choice3
"""
MY_CHOICES = choices(my_choices)
print(MY_CHOICES) # ((1, choice1), (2, choice2), (3, choice3))
于 2014-08-24T04:08:15.380 に答える
0

Finally定数がhttps://github.com/python/mypy/pull/5522を介してPythonに追加されました

インストールする:

pip install mypy typing_extensions

使用例:

from typing_extensions import Final

DAYS_IN_A_WEEK: Final = 7
DAYS_IN_A_WEEK = 8  # I really want more days in a week!

mypyタイプチェッカーを実行する必要があります:

mypy --python-version=3.6 --strict week.py
week.py:4: error: Cannot assign to final name "DAYS_IN_A_WEEK"

詳細については、https ://dev.to/wemake-services/1-minute-guide-to-real-constants-in-python-2bpkを参照してください。

于 2020-04-02T08:38:42.827 に答える