2012-10-10 31 views
31

Tôi có một mô hình Django dựa trên một bộ tuple. Tôi tự hỏi những gì thực hành tốt nhất là để tham khảo các hằng số trong tuple đó cho chương trình Django của tôi. Ở đây, ví dụ: tôi muốn chỉ định "default=0" làm nội dung dễ đọc hơn và không yêu cầu nhận xét. Bất kỳ đề xuất?Thực hành tốt nhất cho các hằng số Python & Django

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. 

EDIT:

Nếu có thể tôi muốn tránh sử dụng một số hoàn toàn. Bằng cách nào đó, việc sử dụng chuỗi 'Yêu cầu sự chú ý' thay vào đó sẽ dễ đọc hơn.

+0

Bạn đã đọc phương pháp thú vị này sử dụng '__metaclass__'? http://tomforb.es/using-python-metaclasses-to-make-awesome-django-model-field-choices –

Trả lời

51

Nó là khá phổ biến để xác định hằng số cho các giá trị số nguyên như sau:

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) 

Bằng cách di chuyển hằng số và Status bên trong lớp, bạn giữ namespace sạch của mô-đun, và như là một tiền thưởng bạn có thể tham khảo Tasks.COMPLETE bất cứ nơi nào bạn nhập mô hình Tasks.

+0

Cũng cấp quyền truy cập vào các biểu diễn chuỗi với: 'def status_str (self): return self.Status [self.status] [1]' – Alveoli

+3

Mô hình cơ bản của @Alveoli django cung cấp chức năng 'get_fieldname_display()' cho mỗi trường trong một mô hình. Vì vậy, get_status_display() đã có sẵn – quin

+0

Bạn có nghĩ rằng nó sẽ yêu cầu thêm bộ nhớ cho các đối tượng của lớp đó? Một ý tưởng tốt hơn là giữ chúng tách biệt và nếu bạn muốn truy cập chúng từ đối tượng, thì bạn có thể định nghĩa một hàm có thể trả về điều này. – Anuj

1

Bạn có thể sử dụng một từ điển cho một cải tiến nhỏ trong rõ ràng:

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

Tôi nghĩ điều này rất giống với việc chỉ có hằng số.Có cách nào để thêm thông tin theo ngữ cảnh có thể đọc được của con người vào ý nghĩa của '1' trong trường hợp này không? Làm thế nào ai đó có thể nói rằng 1 có nghĩa là 'Làm việc trong tiến trình'? –

2

cách tiếp cận của tôi:

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

này cho phép bạn viết các biểu thức thuận tiện:

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

Ngoài ra, nó cho phép bạn không tạo các biến toàn cục không cần thiết.

Nếu bạn thực sự muốn sử dụng trường số nguyên:

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) 

Và:

tasks = Task.objects.filter(status=Task.STATUSES.COMPLETE) 
+0

Tôi nghĩ điều này rất giống với việc chỉ có hằng số. Có cách nào để thêm thông tin theo ngữ cảnh có thể đọc được của con người vào ý nghĩa của '1' trong trường hợp này không? Làm thế nào ai đó có thể nói rằng 1 có nghĩa là 'Làm việc trong tiến trình'? –

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


Hãy ghi nhớ rằng như những người khác chú ý, cách thích hợp là đặt các biến bên trong lớp Mô hình của bạn. Đó cũng là cách django example chính thức thực hiện.

Chỉ có một lý do mà bạn muốn đặt nó bên ngoài không gian tên lớp và đó chỉ là nếu các ngữ nghĩa này được chia sẻ đều nhau bởi các mô hình ứng dụng khác của bạn. tức là bạn không thể quyết định trong đó mô hình cụ thể mà chúng thuộc về.

Mặc dù không có vẻ như đây là trường hợp trong ví dụ cụ thể của bạn.

+1

Được thăng hạng, nhưng tôi thích nó tốt hơn khi các hằng số được chuyển vào lớp như trong câu trả lời của Alasdair. –

+0

@ChrisWesseling Hoàn toàn đồng ý, chỉ không muốn đi chệch khỏi mã ví dụ của OP. – rantanplan

+0

Mặc dù nó được sử dụng chỉ trong mô hình, đôi khi nó rất hữu ích để đặt chúng bên ngoài lớp học. Có thể trong một tập tin hằng số. Tôi phải làm điều đó để tránh nhập khẩu vòng tròn. Để gọi Task.CANCELED tôi phải nhập nó. Kể từ khi nó được gọi ở nhiều nơi và các mô hình đang thay đổi rất nhanh có cơ hội nhập khẩu vòng tròn nếu chúng ta nhập khẩu nhiệm vụ trong tất cả các tập tin cần thiết. –

7

Bạn có thể sử dụng số namedtuple, sử dụng Biến đổi cho hằng số có vẻ phù hợp. ;-)

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

Sử dụng thuộc tính trên Task như hằng số như trong Alasdair's answer ý nghĩa hơn trong trường hợp này, nhưng namedtuples là sản phẩm thay thế rất rẻ cho dicts và đối tượng mà không thay đổi. Đặc biệt rất tiện dụng nếu bạn muốn có trong số đó trong bộ nhớ.Chúng giống như các bộ dữ liệu thông thường với phần thưởng là mô tả __repr__ và thuộc tính truy cập.

+3

Nếu bạn cần thêm [thuyết phục] (http://pyvideo.org/video/367/pycon-2011--fun-with-python--39-s-newer-tools) '[11:35 - 26:00 ] ' – kreativitea

0

tôi không sử dụng Django, nhưng tôi làm điều gì đó giống như sau một chút khá dưới Kim tự tháp và 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) 

Vì vậy, bản chất là thế này:

  • Có một lớp cơ sở "hằng số" và một lớp khác cho mỗi loại. lớp học xác định từ khóa cho một giá trị trong ALLCAPS
  • Tôi cũng ném vào bản rõ văn bản _pairs vào lớp học. tại sao? bởi vì tôi có thể cần phải xây dựng một số bảng DB với họ, hoặc tôi có thể muốn chúng cho các thông báo lỗi/trạng thái. Tôi sử dụng các con số và không phải là tên biến ALLCAPS như là một sở thích cá nhân.
  • tôi khởi tạo biến số lớp mapping cơ bản monkeypatches lớp bằng cách biên dịch trước một loạt biến trong một dict bởi vì ...
  • lớp được bắt nguồn từ lớp cơ sở đó, cung cấp chức năng classmethod để tìm kiếm giá trị hoặc làm những thứ tiêu chuẩn khác mà bạn thường cần phải làm với hằng số.

Đó không phải là phương pháp tiếp cận một kích thước phù hợp, nhưng tôi thường thực sự thích điều này. Bạn có thể dễ dàng sử dụng dict để định nghĩa các cặp, có chức năng 'ánh xạ' thiết lập một số thuộc tính khác, chẳng hạn như cung cấp cho bạn các giá trị cặp như k, v hoặc v, k hoặc bất kỳ định dạng lạ nào bạn có thể cần.

mã của tôi sau đó có thể trông như thế này:

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] 

bất cứ khi nào bạn cần sử dụng các hằng số trong một cách khác, bạn chỉ cần thêm hoặc thay đổi classmethods của các cơ sở.

0

Đôi khi tôi phải tạo một số danh sách lựa chọn rất lớn. Tôi không muốn gõ như một con khỉ, vì vậy tôi thay vì để tạo ra một funcion như thế này:

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

Và sử dụng như thế này:

my_choices = """ 
choice1 
choice2 
choice3 
""" 
MY_CHOICES = choices(my_choices) 
print(MY_CHOICES) # ((1, choice1), (2, choice2), (3, choice3)) 
6

Python 3.4 trở lên: Enum

Bạn viết "Nếu có thể, tôi muốn tránh sử dụng một số hoàn toàn". và thực sự là một đại diện được đặt tên rõ ràng là nhiều hơn. Một chuỗi trần, tuy nhiên, dễ bị lỗi chính tả.

Python 3.4 giới thiệu một module gọi là enum cung cấp EnumIntEnum pseudoclasses giúp với tình trạng này. Với nó, ví dụ bạn có thể làm việc như sau:

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

và một khi đội Django nhặt Enum, các choiceadapter sẽ thậm chí được xây dựng thành Django.

Các vấn đề liên quan