2012-11-12 30 views
6

Tại sao get_FOO_display() trả lại giá trị số nguyên khi thông tin ghi nhật ký (django)?Tại sao get_FOO_display() trả về giá trị số nguyên khi thông tin ghi nhật ký (django)?

Tôi có trường mô hình đang sử dụng lựa chọn để hạn chế giá trị của nó. Điều này làm việc tốt và tôi có nó hoạt động ở mọi nơi trong ứng dụng, ngoại trừ khi đăng nhập thông tin, khi phương thức get_FOO_display() trả về giá trị nguyên cơ bản thay vì của phiên bản có thể đọc được.

Đây là định nghĩa mô hình (tóm tắt):

THING_ROLE_MONSTER = 0 
THING_ROLE_MUMMY = 1 

ROLE_CHOICES = (
    (THING_ROLE_MONSTER, u'Monster'), 
    (THING_ROLE_MUMMY, u'Mummy'), 
) 

# definition of property within model 
class Thing(models.Model): 
    ... 
    role = models.IntegerField(
     'Role', 
     default=0, 
     choices=ROLE_CHOICES 
    ) 

Nếu tôi chạy này trong (django) vỏ tương tác nó cư xử chính xác như bạn mong chờ:

>>> from frankenstein.core.models import Thing 
>>> thing = Thing() 
>>> thing.role = 0 
>>> thing.get_role_display() 
u'Monster' 

Tuy nhiên, khi tôi sử dụng chính xác cấu trúc tương tự trong chuỗi định dạng/ghi nhật ký tôi gặp sự cố:

logger.info('New thing: <b>%s</b>', thing.get_role_display()) 

lợi nhuận:

New thing: <b>0</b> 

Help!

[UPDATE 1]

Khi tôi chạy khai thác gỗ bên trong vỏ tương tác tôi nhận được đầu ra chính xác:

>>> from frankenstein.core.models import Thing 
>>> import logging 
>>> thing = Thing() 
>>> thing.role = 0 
>>> logging.info('hello %s', b.get_role_display()) 
INFO hello Monster 

[UPDATE 2] Django internals

Theo dõi trên câu trả lời từ @ joao-oliveira dưới đây, tôi đã đào vào bên trong và phát hiện ra sau đây.

Phương pháp _get_FIELD_display tiềm ẩn trong django.db.models trông như thế này:

def _get_FIELD_display(self, field): 
    value = getattr(self, field.attname) 
    return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True) 

Nếu tôi đặt một breakpoint vào mã, và sau đó chạy IPdb tôi có thể thấy rằng tôi có vấn đề:

ipdb> thing.get_role_display() 
u'1' 
ipdb> thing._get_FIELD_display(thing._meta.get_field('role')) 
u'1' 

Vì vậy, sửa chữa đã không thay đổi bất cứ điều gì. Nếu tôi sau đó thử chạy qua mã _get_FIELD_display phương pháp bằng tay, tôi có được điều này:

ipdb> fld = thing._meta.get_field('role') 
ipdb> fld.flatchoices 
[(0, 'Monster'), (1, 'Mummy')] 
ipdb> getattr(thing, fld.attname) 
u'1' 
ipdb> value = getattr(thing, fld.attname) 
ipdb> dict(fld.flatchoices).get(value, value) 
u'1' 

Đó là tương đương với nói:

ipdb> {0: 'Monster', 1: 'Mummy'}.get(u'1', u'1') 
u'1' 

So. Vấn đề chúng tôi có là phương pháp đang sử dụng giá trị chuỗi u'1' để tra cứu mô tả tương ứng trong từ điển lựa chọn, nhưng các phím từ điển là số nguyên và không phải là chuỗi. Do đó chúng tôi không bao giờ có được một trận đấu, nhưng thay vào đó là giá trị mặc định, được đặt thành giá trị hiện tại (chuỗi).

Nếu tôi tự buộc các diễn viên để int, mã làm việc như mong đợi:

ipdb> dict(fld.flatchoices).get(int(value), value) 
'Mummy' 
ipdb> print 'w00t' 

Đây là tất cả tuyệt vời, nhưng không trả lời câu hỏi ban đầu của tôi là tại sao phương pháp get_foo_display không trả lại đúng giá trị hầu hết thời gian. Tại một số điểm, chuỗi (u'1 ') phải được truyền đến đúng kiểu dữ liệu (1).

[UPDATE 3] Câu trả lời

Trong khi một danh dự phải đến Joao cho cái nhìn sâu sắc của mình, tiền thưởng sẽ Josh đã chỉ ra một thực tế cùn rằng tôi đi qua trong giá trị sai lầm khi bắt đầu với. Tôi đặt nó xuống để trở thành một người di cư từ 'thế giới mạnh mẽ', nơi những điều này không thể xảy ra!

Mã mà tôi không đưa vào đây là đối tượng được khởi tạo từ biểu mẫu django, sử dụng số cleaned_data từ số ChoiceField. Vấn đề với điều này là đầu ra từ một ChoiceField là một chuỗi, không phải là một số nguyên. Các bit tôi bị mất là trong một ngôn ngữ lỏng lẻo-typed nó có thể thiết lập một tài sản số nguyên với một chuỗi, và cho không có gì xấu xảy ra.

Sau khi xem xét điều này, tôi thấy rằng tôi đã sử dụng TypedChoiceField, để đảm bảo rằng đầu ra từ cleaned_data luôn là số nguyên.

Cảm ơn tất cả.

+0

Để rõ ràng, đây có phải là việc ghi nhật ký vào một tệp hoặc đăng nhập vào một dịch vụ (ví dụ: HipChat) không? IIRC, logger đánh giá lười biếng, và có thể không xem xét một cuộc gọi dịch vụ 'đủ' để thực sự đánh giá get_FOO_display –

+0

Đây là đăng nhập vào một dịch vụ, qua HTTP, sử dụng các yêu cầu. –

+0

Mã này ở đây - https://gist.github.com/3176710 –

Trả lời

11

tôi thật sự xin lỗi nếu điều này nghe có vẻ c ondescending, nhưng bạn có chắc chắn 100% rằng bạn đang thiết lập giá trị cho số nguyên 1 và không phải là chuỗi '1'?

Tôi đã đi sâu qua bên trong và chạy một số thử nghiệm và cách duy nhất mà vấn đề bạn đang gặp phải có nghĩa là nếu bạn đang đặt giá trị thành chuỗi. Xem bài kiểm tra đơn giản của tôi tại đây:

>>> from flogger.models import TestUser 
>>> t = TestUser() 
>>> t.status = 1 
>>> t.get_status_display() 
u'Admin' 
>>> t.status = '1' 
>>> t.get_status_display() 
u'1' 

Kiểm tra mã xem của bạn hoặc bất kỳ mã nào thực sự đặt giá trị và kiểm tra đầu ra của trường trực tiếp.

Như bạn dán từ mã mô hình nội bộ:

def _get_FIELD_display(self, field): 
    value = getattr(self, field.attname) 
    return force_unicode(dict(field.flatchoices).get(value, value), strings_only=True) 

Nó chỉ đơn giản nhận được giá trị hiện tại của lĩnh vực này, và lập chỉ mục vào từ điển, và trả về giá trị của thuộc tính nếu một tra cứu không tìm thấy .

Tôi đoán trước đây không có lỗi nào, vì giá trị bị ép buộc thành một số nguyên trước khi được chèn vào cơ sở dữ liệu.

Chỉnh sửa:

Về cập nhật của bạn đề cập đến hệ thống kiểu của python. Thứ nhất, bạn nên sử dụng TypedChoiceField để đảm bảo biểu mẫu xác minh loại mà bạn mong đợi. Thứ hai, python một ngôn ngữ được đánh máy mạnh mẽ, nhưng IntegerField thực hiện việc xâu chuỗi của riêng nó với int() khi chuẩn bị cho cơ sở dữ liệu.

Các biến không được nhập, nhưng các giá trị bên trong chúng là. Tôi đã thực sự ngạc nhiên khi các IntegerField được xâu chuỗi vào một int cũng. Good lessen để tìm hiểu ở đây - kiểm tra những điều cơ bản đầu tiên!

+0

Không condescending ở tất cả - Tôi chỉ là về để repost về chủ đề chính xác này - như có truy tìm mọi thứ thông qua điều này là chính xác những gì đang xảy ra. Tôi đang sử dụng form.cleaned_data để thiết lập giá trị, và nó được sử dụng chuỗi, không phải là số nguyên. –

+0

@ HugoRodger-Brown rất vui vì bạn đã đến đó, có thể đáng đọc chỉnh sửa của tôi về hệ thống kiểu python. –

+0

Josh, bạn đã lưu ass của tôi hôm nay! rất nhiều cảm ơn từ rất nhiều tôi – doniyor

0

thử điều này:

class Thing(models.Model): 

    THING_ROLE_MONSTER = 0 
    THING_ROLE_MUMMY = 1 

    ROLE_CHOICES = (
     (THING_ROLE_MONSTER, u'Monster'), 
     (THING_ROLE_MUMMY, u'Mummy'), 
    ) 

    role = models.IntegerField('Role', default=0,choices=ROLE_CHOICES) 
+0

Điều này không làm gì khác cho tôi. Tôi đã đăng một bản cập nhật cho câu hỏi sau khi đi sâu hơn vào bên trong django. –

2

Đã không cố gắng mã của bạn, cả @ như-nó trả lời xin lỗi, nhưng _get_FIELD_display từ models.Model là curried trong các lĩnh vực để thiết lập get_Field_display chức năng, do đó, thats lẽ tại sao you'r nhận ra rằng

thử gọi _get_FIELD_display:

logging.info('hello %s', b._get_FIELD_display(b._meta.get('role'))) 
+0

Tôi đã thay đổi điều này một chút để sử dụng _meta.get_field ('vai trò') dưới dạng đơn giản .get() không hoạt động. Tuy nhiên, vẫn nhận được kết quả tương tự - số nguyên cơ bản, chứ không phải tên hiển thị. –

+0

Tôi đã đăng một bản cập nhật cho nội dung sau đây từ câu trả lời của bạn. Tôi cũng đã bình chọn câu trả lời của bạn vì nó thực sự hữu ích, tuy nhiên tôi chưa đánh dấu câu trả lời là câu trả lời vì vấn đề vẫn tồn tại. –

+0

Tôi xin lỗi, tôi không thể tìm thấy bất kỳ điều gì khác và tôi không đọc toàn bộ mã của bạn, nhưng nếu nó hoạt động trên repl, nó phải là mã tệp của bạn, gây ra hàm của bạn và cho đầu ra của nó để ghi nhật ký, vì vậy các hành vi phải giống như tôi đoán – jxs

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