2010-12-22 69 views
8

如何確保只有在字段中的數據已從數據庫加載時才調用自定義字段的* to_python()*方法?Django自定義字段:只從數據庫的值運行to_python()?

我正在嘗試使用自定義字段來處理單個模型屬性的Base64編碼/解碼。一切似乎都正常工作,直到我實例化一個新的模型實例,並設置它的明文值的屬性...在這一點上,Django試圖解碼字段,但因爲它是明文失敗。

自定義字段實現的魅力在於,我認爲我可以在那裏處理100%的編碼/解碼邏輯,因此我的代碼中沒有其他部分需要知道它。我究竟做錯了什麼?

(注意:這僅僅是一個例子來說明我的問題,我不如何,我應該或不應該使用base64編碼需要諮詢)

def encode(value): 
    return base64.b64encode(value) 

def decode(value): 
    return base64.b64decode(value) 


class EncodedField(models.CharField): 
    __metaclass__ = models.SubfieldBase 

    def __init__(self, max_length, *args, **kwargs): 
     super(EncodedField, self).__init__(*args, **kwargs) 

    def get_prep_value(self, value): 
     return encode(value) 

    def to_python(self, value): 
     return decode(value) 

class Person(models.Model): 
    internal_id = EncodedField(max_length=32) 

...和它打破當我在交互式shell中執行此操作時。爲什麼在這裏調用to_python()?

>>> from myapp.models import * 
>>> Person(internal_id="foo") 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/usr/local/lib/python2.6/dist-packages/django/db/models/base.py", line 330, in __init__ 
    setattr(self, field.attname, val) 
    File "/usr/local/lib/python2.6/dist-packages/django/db/models/fields/subclassing.py", line 98, in __set__ 
    obj.__dict__[self.field.name] = self.field.to_python(value) 
    File "../myapp/models.py", line 87, in to_python 
    return decode(value) 
    File "../myapp/models.py", line 74, in decode 
    return base64.b64decode(value) 
    File "/usr/lib/python2.6/base64.py", line 76, in b64decode 
    raise TypeError(msg) 
TypeError: Incorrect padding 

我曾預計我將能夠做這樣的事......

>>> from myapp.models import * 
>>> obj = Person(internal_id="foo") 
>>> obj.internal_id 
'foo' 
>>> obj.save() 
>>> newObj = Person.objects.get(internal_id="foo") 
>>> newObj.internal_id 
'foo' 
>>> newObj.internal_id = "bar" 
>>> newObj.internal_id 
'bar' 
>>> newObj.save() 

......我究竟做錯了什麼?

回答

0

當您第一次爲該字段賦值時,您是否僅獲得TypeError?你可以只寫一個try /除了它周圍:

def to_python(self, value): 
    try: 
    return decode(value) 
    except TypeError: 
    return value 

這不是最乾淨的解決方案,但你可以試試,看看它是否讓你與現場工作你期待的方式。

+0

是的,這也發生在我身上......我希望有一個更清潔的解決方案。我對Django一般都很陌生,特別是這是我第一次使用自定義字段,所以我的假設是要麼在我的自定義字段的聲明中丟失了某些內容,要麼有一個完全不同的,更合適的滿足我的需求。 – 2010-12-22 15:14:41

+1

原來,這實際上是我能找到的唯一解決方案。失望的是沒有更好的方法來做到這一點。 – 2010-12-24 20:40:29

+4

如果您存儲的數據恰好是有效的Base64輸入(例如`b'abcd'`),則這將失敗。然後不會產生TypeError,結果是錯誤的。 – 2011-08-09 00:20:53

3

(從http://davidcramer.posterous.com/code/181/custom-fields-in-django.html
https://docs.djangoproject.com/en/dev/howto/custom-model-fields/#converting-database-values-to-python-objects

看來你需要能夠進行測試,如果它是一個實例,並用這個問題是他們是同一類型(字符串VS B64編碼字符串)。所以,除非你能detirmine的區別,我建議要確保你總是:

Person(internal_id="foo".encode('base64', 'strict')) 

Person(internal_id=base64.b64encod("foo")) 

或一些這樣的編碼。

編輯: -我在看https://github.com/django-extensions/django-extensions/blob/f278a9d91501933c7d51dffc2ec30341a1872a18/django_extensions/db/fields/encrypted.py,並認爲你可以做類似的事情。

3

我有完全相同的問題,但帶有JSON數據。我想以JSON格式將數據存儲在數據庫中。但是,如果您嘗試存儲已經序列化的JSON對象,它將被返回反序列化。所以問題是,進來的東西並不總是出來的。特別是如果你試圖將一個數字作爲一個字符串存儲,它將作爲一個int或者float來返回,因爲它在被存儲之前被to_python反序列化。

解決方案很簡單,儘管不太優雅。只需確保將「數據類型」與數據一起存儲,在我的情況下,它是JSON數據,所以我在前面加上「json:」,因此您總是知道數據是否來自數據庫。

def get_prep_value(self, value): 
    if value is not None: 
     value = "json:"+json.dumps(value) 
    return value 
def to_python(self, value): 
    if type(value) in (str, unicode) and value.startswith("json:"): 
     value = value[5:] 
     try: 
      return json.loads(value) 
     except: 
      # If an error was raised, just return the plain value 
      return value 
    else: 
     return value 

話雖這麼說,這很煩人,你不能期望一致的行爲,或者說你不能告訴to_python是否在用戶設定值或從DB值運行。

相關問題