Categories

Links

django小提示-FormPreview中的BooleanField

这个问题得确是django的 bug
问题在于django 总是将BooleanField的值处理成True,False,但有个地方存在不一致的地方,就是
CheckboxInput的value_form_datadict方法

代码
  1. def value_from_datadict(self, data, files, name):  
  2.       if name not in data:  
  3.           # A missing value means False because HTML form submission does not  
  4.           # send results for unselected checkboxes.  
  5.           return False  
  6.       return super(CheckboxInput, self).value_from_datadict(data, files, name)  


这个方法会导致一些错误,因为 data是个字符串的词典,因此,这个方法会返回bool的False(当data中没有is_test时)或字符串的'False'(当存在,但值为False时)
第一个是hash 值的计算,一开始的bool类型的False同unicode字符串的False是不同 的. 这样会导致当不核选时需要提交两次
其次是当render的时候,可以查看CheckboxInput的render方法,里面有个check_test,默认被设置为bool,这样,当调用bool('False')时,返回的就是True了

所以,修改上面的方法(或继承新建一个Checkbox2Input),让其根据实际情况返回bool值就ok了
比方说(这只是个quick and dirty的解决方法,英语好的可以提交bug,让django团队来处理这个问题)

代码
  1. def value_from_datadict(self, data, files, name):  
  2.      if name not in data:  
  3.          # A missing value means False because HTML form submission does not  
  4.          # send results for unselected checkboxes.  
  5.          return False  
  6.      #print data[name],"value_form_datadict",type(data[name])  
  7.      result=super(CheckboxInput, self).value_from_datadict(data, files, name)  
  8.      return True if result in ['on','ON','True','true'] else False  

由于存在这两个问题,因此光传入一个check_test lambda 还不足以解决问题,我一开始就以为这个能解决,呵呵

[2007-12-06 11:59:12 | Author:jiangjianxiao ] [] 0 comments

django小提示-json

django提供了基于json,yaml,xml的串行化支持. 这里我们主要来讲json
django对json 的支持有两个层次
第一个层次在django.utils.simplejson包中 ,JSONEncoder 提供了常见python数据类型像tuple,list,dict,string,float,decimal等的支持,但这里,并不包含对自定义python对象的json支持
第二个层次在django.core.serializers 包中,这个包提供了对json,yaml(如果你安装了pyyaml的话,xml的串行化支持. 注意,在这里,只能串行化querySet,而且,你只能在django项目内部使用(当然你可以通过手动将项目路径加入到sys.path并设置    os.environ["DJANGO_SETTINGS_MODULE"] = myproject.settings' 来在非django项目中使用)


第一个层次可以在其他非django 项目中使用,你需要继承JSONEncoder,并覆盖default方法,比方说你有一个Employee对象

class Employee:
    def __init__(self,name):
        self.name=name

from django.utils.simplejson.encoder import JSONEncoder
class ObjectJSONEncoder(JSONEncoder):
    def default(self,o):
        if hasattr(o,"__dict__")
             return o.__dict__
        return super(ObjectJSONEncoder,self).default(o)

# test

data=[1,{'a':'a'},Employee('jjx')]
import django.utils.simplejson

print django.utils.simplejson.dumps(data,cls=ObjectJSONEncoder)


[1, {"a": "a"}, {"name": "jjx"}]


当使用第二个层次时,你可以通过传递fields 参数选择输出的字段,也可以传入一个io对象

from django.core.serializers import seralize
from cStringIO import StringIO
stream=StringIO()

seralize('json',User.objects.all(),stream=stream,fields=['username'])
print stream.getvalue()

当将json输出到浏览器时,你可以传递mimetype参数为text/json或text/javascript
return HttpResponse(json_str,mimetype="text/json")

update django snippets 上有 个JsonResponse类, 可以参考
http://www.djangosnippets.org/snippets/154/

from django.core.serializers import serialize
from django.db.models.query import QuerySet
from django.http import HttpResponse
from django.utils import simplejson

class JsonResponse(HttpResponse):
def __init__(self, object):
if isinstance(object, QuerySet):
content = serialize('json', object)
else:
content = simplejson.dumps(object)
super(JsonResponse, self).__init__(content, mimetype='application/json')
[2007-11-28 16:37:21 | Author:jiangjianxiao ] [] 0 comments

django小提示-maxlength,max_length

在django 0.96,CharField的的最大长度使用maxlength
在svn的代码中,这个已经修改为max_length(这样,更符合python一般的命名规范,当然,事实上python并没有像ruby一样对命名规范有严格的要求,比方说unittest,wxpython就使用两个不同的命名规范),但实际上,原先的代码并不会出错,django是如何达成的呢

这里,django 使用了metaclass的功能

1. 首先,查看Field对象的定义,你会发现
class Field(object):
    __metaclass__ = LegacyMaxlength

LegacyMaxLength定义在django.utils.maxlength 模块中,这个metaclass非常简单
class LegacyMaxlength(type):
    def __init__(cls, name, bases, attrs):

        super(LegacyMaxlength, cls).__init__(name, bases, attrs)
        #对构造函数进行修饰
        cls.__init__ = remove_maxlength(cls.__init__)
        #增加maxlength属性    
        cls.maxlength = property(get_maxlength, set_maxlength)

remove_maxlength巧妙的运用了decorate ,对class 构造进行进行处理,流程很简单,就是从参数中取max_length,maxlength,如果有maxlength,则给你个已过期的警告,如果你同时设置了max_length和maxlength,则给你来个错误(因为 django这样无法判断你用 那个),然后将maxlenth设置给max_length,并创建maxlength属性

这样Field class的代码本身就非常干净,并没有为保持兼容性而同时存在maxlength和max_length两个属性. (虽然实际生成的代码还是有两个的,一个是max_length field还有一个是通过metaclass生成的maxlength property)

这里django 巧妙的利用metaclass实现deprecated 功能的.

在django的代码中,对于类似dsl的功能,django主要利用metaclass和descriptor来达成的,比方说model ,django构造了一个ModelBase metaclass,然后让Model使用这个元类,像对于关系处理, ForeignKey最终是将使用ReverseSingleRelatedObjectDescriptor 这样的 descriptor对象,也就是说,当你遇到
class Customer(models.Model):
    shipping_address=models.ForeignKey(Address)

最后,这个类被解释执行后的shipping_address实际上是一个叫shipping_address的ReverseSingleRelatedObjectDescriptor 的实例.

descriptor和metaclass的基本知识,我推荐参阅这这几个链接
How-To Guide for Descriptors
http://users.rcn.com/python/download/Descriptor.htm#static-methods-and-class-methods
我强烈推荐这个文档, 看后你会知道descriptor 包括property,staticmethod,classmethod到底是如何运作的,也会理解bound(bind)在动态语言中的作用.
Python 中的元类编程 -将面向对象编程推向新的高度
http://www.ibm.com/developerworks/cn/linux/l-pymeta/index.html
Python 中的元类编程,第 2 部分- 理解继承的奥秘和实例创建
http://www.ibm.com/developerworks/cn/linux/l-pymeta2/index.html

[2007-11-30 15:17:07 | Author:jiangjianxiao ] [] 0 comments

django小提示-打印sql语句

当settings.Debug=True时,django 使用的是django.db.backets.util.CursorDebugWrapper ,这样,当你执行execute,executemany时,django会把你执行的语句加入到BaseDatabaseWrapper派生对象的queries列表,每次加入的是一个dict,包括sql和time
这样,你可以在任何时候打印connection对象的queries属性或是直接在CursorDebugWrapper的execute和executemany中加入print语句

def execute(....):
    ...
    print smart_unicode(sql) % convert_args(params),"%.3f" % (stop - start)

在django中connection对象实际上就是BaseDatabaseWrapper的派生类
看这行
connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)

再看backend模块是怎么来的
    _import_path = 'django.db.backends.'
    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])

显然,如果你的DATABASE_ENGINE为mysql的话,这个导入语句就是
__import("django.db.backends.mysql.base",{},{},[''])

通常,mysqldb的cursor执行结果为一个tuple ,虽然这样效率较高,但有时 也不方便,你可以让 在创建真实的mysqldb connection对象时传入cursorclass参数

修改django.db.backends.mysql.base.DatabaseWrapper
def _cursor(...):
            #keargs['cursorclass']=Database.DictCursor # 加上这行
            self.connection = Database.connect(**kwargs)

或者在settings.py中增加
import MySQLdb
CURSOR_CLASS=MySQLdb.Cusor # 或者CURSOR_CLASS=MySQLdb.DictCursor

然后
def _cursor(...):
           keargs['cursorclass']=settings.CURSOR_CLASS # 加上这行
            self.connection = Database.connect(**kwargs)

你也可以修改
  cursor = self.connection.cursor() 为
  cursor = self.connection.cursor(settings.CURSOR_CLASS)
效果是一样的

[2007-11-28 11:05:03 | Author:jiangjianxiao ] [] 0 comments

Total 4 Display 1 of 4
Powered by Google App Engine