Django也会智能Lazyload?
今天在本地很快实现了最新评论的功能。在右手边的最新评论点击连接时会去到文章的页面,并定位到评论所在的锚点上,也就是说,我需要在页面上取得评论所在文章的ID,也就是需要这样:{{comment.comment_to.id}}。这时候,我仅仅需要获得文章的ID,但不知道Django是否会到数据库去把相应的文章查出来(我没有使用Select_relate)之后再拿ID呢?不过Django好现没有可以设置打印执行Sql语句的开关,并不能通过配置来看到运行时Django查了多少次数据库。而这样的功能,我使用Java的Hibernate时就有的。
我Google了一下,发现Django本身是没有提供日志和Sql打印功能的。而有人做了些工作,如这位仁兄写了个中间件,可以在页面上打印出该次请求一共执行了多少条语句,每条语句的执行时间。而这里有个叫Django-logging的项目,为Django提供Logging,包括Sql的打印。等会就试验一下。Django天生就是Lazyload的,需要用到数据的时候才去加载,我希望,我的担心是多余的。一会尝试使用Log来看看测试结果吧。
结果一:Select_related对允许为空的外键不起作用。
我的查询语句是这样:entries = blog.entry_set.filter(status=1).select_related(depth=1).order_by('-id') 查询一个Blog下面所有的Entry,并把有关联的数据一并抓取出来(根据ForeignKey)。也就是说,在查询Entry的同时查出Blog,作者,分类等信息。
但是情况是,我在页面找印这些文章的时候,需要把文章的分类也打显示出来,这时候,Django却跑到数据库执行了N次查询分类的语句。暴汗。这是经典的Hibernate的延迟加载用得不恰当的情况啊!我一看打印出来的查询语句,有查相关的作者、Blog,但偏偏没有查分类。最后我回到Model里比较了一下,我的分类属性上面设置了null=true,原意是让文章可以没有分类。最后我尝试着去掉Null=true,一运行。那N条查询不见了。靠。。原来Select_related在外键允许空的情况下是行不通D。
结果二:Django不会智能LazyLoad。要手动去Select_related。
回到对Django有疑问的情景,recent_comments = Comment.objects.filter(blog=blog).order_by('-comment_time')[:10] 查出本BLog最新的十条评论。我的假设是,Comment本身保存有Entry的ID,所以在仅仅是获取Entry的ID的时候不需要去数据据里找,Hibernate的LazyLoad就是这样做的,使用动态代理的解决方案。我想在Python这样的动态语言的解决方案更佳,但事实证明我错了。上面的语句又产生了经典的1+N的问题。我在页面根据评论取EntryID的时候{{comment.entry.id}}还是去加载了一次数据,搞笑的是,这句查询需要得到的数据对我有用的是ID,但是查询条件就是ID。我只好把查询语句改成:
recent_comments = Comment.objects.select_related(depth=1).filter(blog=blog).order_by('-comment_time')[:10]。显示去Select_related。宁愿直接去Join相关的表,也不要做1+ N的查询。显然这很浪费,仅仅为了一个ID,要Join好几个表。
WYMeditor
WYMeditor。又一个RichText editor,我怎么说又呢?猛地看上去我以为又一个HtmlArea或者FCKeditor或者Tinymce。看到Demo和features才知道,这东西蛮有个性的。打算在本站使用一下的。Features:
- 完全兼容XHTML strict + CSS
- 没有Font,你不能对字体颜色、大小,字体对进调整,WYMeditor是基于CSS的
- 被设计用来易于与你的应用相结合
- 不需安装,100%的Javascript代码,无插件,无扩展
- 很简单的Javascript代码,很易懂
- 继续保持简单
- 我们致力于测试友好的代码,保证新功能的稳定性及有效性
- 图像、连接、表格支持
- 通过CSS支持皮肤
- 免费并且开源,完全适合你的需要
N小时后。
测评结果:
一、很固执,wymeditor.js一定要位于一个名叫wymeditor的文件夹下面,不然一律罢工,报错XmlHelper找不到,这个问题很怪异也很变态,花了我好多时间才找到原因。
二、提交时不更新TextArea的内容,要手动写程序或者在Submit的Button指定Class为wymupdate,我试过第二种方法,由于我的Button还需要使用Onclick方法,没戏。这样很变态。
三、指定CSS的时候,要按规则对CSS进行注释,偶笨,不得法,注释的CSS搞得整个Editor出错不能用。
总的来说,指导思想很好(What You See Is What You Mean),只是目前处于Beta阶段。我还是先使用最熟悉的FCK吧。等待Wymeditor的成熟!
放弃Mod_fastcgi+apache?
尝试了一晚上,Apache没有重启成功。Mod_fastcgi在2003年的时候就已停止了开发。那时只是支持到Apache2.0.X版,现在使用的都2.2.X了,官方虽然提供了一个Patch,但我本地没有C编译环境,于是不了了之。此外,Fastcgi本身有些问题,线程出问题的时候可能关闭不了,不过中国(没有错吧?)有人解决了这个问题,那是另一个项目“fcgid”。用这个可以直接代替Fcgi。
寻找另一种部署FastCGI的方式。。lighttpd?
Django + mod_python
Django + mod_python的部署方式的基础是我的上一篇文章。前提python、Django、Apache、Mod_python安装完备。
部署方式有两种:一是加直接在apache的httpd.conf下面加一个一个Location的元素:
<Location "/mysite/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE myblog.settings
PythonDebug On
PythonPath "[r'F:\\develope\\projects\\myblog'] + sys.path"
</Location>
主要留意两个参数:SetEnv DJANGO_SETTINGS_MODULE 指定你的Django应用的settings.py。PythonPath Python程序的路径,如果你的Django应用不在Sys.path里面,那么加上你的应用路径,注意,如果你的项目路径为:D:\\projects\myblog那么,Python Path应该填D:\\projects。
这样,你可以通过http://localhost/mysite来访问你的应用,不过有一个问题,Django会把“mysite”当作请求的URL的一部分。你必须要把URLConf里的配置全加上mysite。显然,这种做法并不是很理想的。
为解决上面的URL问题,可以通过VirtualHost来配置使用另一个虚拟机作为Django应用的所在地。所不同的是,还是可以从根目录下访问应用,不过使用了不同的端口:
Listen 127.0.0.1:81
<VirtualHost 127.0.0.1:81>
<Location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE myblog.settings
PythonPath "[r'F:\\develope\\projects\\myblog'] + sys.path"
PythonDebug On
</Location>
</VirtualHost>
这一次,可以通过http://localhost:81/来访问应用了。
下面的文章讲述如何处理静态文件与FastCGI + Django
安装Mod_python
Mod_python的使用要求与Apache结合。我使用的Apache版本为2.2,python为2.5,配套的Mod_python为3.3.1。安装及调通的步骤如下:
一、安装Apache。到http://httpd.apache.org/下载最新版本的Apache,如果是Windows环境,直接下载可执行文件安装完就可以了。测试Apache是否正常工作,直接访问http://localhost就可以,当看到It works表示安装成功了。
二、安装Python。下载Python2.5版,直接安装即可。
三、安装Mod_python。到http://www.modpython.org/下载合适版本的Mod_python,注意Apache与Python的版本。在Windows下直接执行安装完之后,找到 C:/Program Files/Apache Software Foundation/Apache2.2\conf\httpd.conf(看apache安装的地方而定) 文件进行编辑,找到LoadModule部分并加上这句:LoadModule python_module modules/mod_python.so,这时候,安装Mod_python已经完成了。下面来进行测试Mod_python的工作情况。
四、测试。在本地建立一个独立的测试目录,如 D:/site,然后修改httpd.conf加入下面代码:
Alias /py d:/site/
<Directory d:/site/>
Order deny,allow
Allow from all
AddHandler mod_python .py
PythonHandler mptest
PythonDebug On
</Directory/>
上面的代表是这样的意思:定义一个别名,当访问localhost/py时,实际上是去d:/site/找文件。Directory里面定义了一些规则,注意前两行很有必要,否则有可能出现拒绝访问的情况,AddHandler mod_python .py意指针对.py文件指定一个Handler。PythonHandler mptest意指Python的Handler是mptest.py。我们在d:/site/下面添加一个mptest.py文件:
from mod_python import apache
def handler(req):
req.content_type = 'text/plain'
req.write('hello world')
return apache.OK
重启Apache,访问http://localhost/py/xyz.py 得到的结果是hello world。只要是.py结束的请求都会调用mptest.py来处理。
That's all。
使用Django生成RSS
感谢Django强大的syndication feed framework,我可以很轻松地实现RSS的生成工作。Fallever的RSS可以从每个Blog的右下角找到。Django的官方文档见这里。文档中的第一个Example并不适合我,Fallever现在的情况是提供每个Blogger一个RSS文件,因此需要传参数,情况比较类似“A complex example”那一段。
使用Django的SFF来生成RSS太简单了。有下面几个步骤:
一、配置URLConf:
(r'^feeds/(?P.*)/rss.xml$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds})
URL表达式中,url参数实际上允许很多个“/”存在。在Fallever中的用法是:/feeds/blog/{{shortcut}}/rss.xml.那么Django会把“feeds/”后面那一大截当作URL的参数传进去生成的机器(姑且这样叫吧),Shortcut是代表一个Blog的简称。注意,"feeds/"作为RSS路径的开头是必须的。
二、写feeds。
你已经留意到了URLConf里面的第三个参数吧。feeds是一个字典,Key是Feed的类型,Value是处理该类型的类。Fallever的第一个字典是:
feeds = {'blog':recent_blog_entry}
这个字典说“feeds/”后面凡是跟着blog字样的路径全由recent_blog_entry这个类来处理。在blog后面跟着的参数从哪里可以找回来呢,我一开始就有这样的问题,原来在这里:重写Feeds类的get_object(self, bits):bits参数就是把blog后面的参数切成了数组,如/feeds/blog/jeff/rss.xml切割后bits参数是:['jeff','rss.xml']在这里我们只需要拿我们有用的元素就可以了。rss.xml这一节东西我是用来哄IE的。
三、在recent_blog_entry类里重写Feeds的几个方法:title,link,description,items,items_link。具体的在这里不说了,还有需要为这一次的Feed加上两个简单的模板。都去参考文档看吧 :)
Fallever更新日志
今天晚上Fallever加上两个Powered by的Logo,Mysql和Django。在考虑是不是把Python也加进来。嘿。
在今晚顺便把RSS提供出来、本次提供的RSS包括:个人博客的最近更新、个人博客分类最近更新。然后,是时候调整一个后台的结构,建立一个完整的可用的博客系统。
接下来会有很多东西要做,捞捞说过,如果我把它在BlogCN上的东西全搬过来,那他以后就在这里安家。我说你不介意我暂时界面简陋、短期内开发进度不快等等你就等着吧,到时帮你搬过来。捞说,我年年25,不急。