分类目录归档:编程

SAE Storage for Django

DPress 修正后想着要部署一个在线的演示程序。问题是我自己虚拟主机的资源已经捉襟见肘,于是只能打SAE的主意了。
为保证文件上传功能的正常使用需要将Django默认的Storage实现替换为SAE的storage实现。SAE的官方并没有提供Django的storage实现,需要自己另外实现。在github:gist上有人贴出了SAE Storage for Django的实现(SAE Storage for Django)。github:gist上的这个SAE Storage实现有些bug,我修正后的版本见saestorage
注:

  • SAE Storage单节点的最大容量为2G(注:SAE Storage 服务限制)。这对于一些小站点来说是没什么问题,但如果是一个大型的商业站点这必然会有些不便。难道你需要配置一个storage列表,让程序自行去寻找可用的storage?
  • SAE Storage似乎不太稳定,上传的文件经常会打不开。

“DPress”原地满血复活

GitHub地址: DPress(GitHub)
演示地址: DPress(SAE)

DPress 是我在2009用django写的一个博客系统。此后年久失修,已经跑不起来了。
这两天花了些时间将项目重新修整一下,现在项目又可以重新跑起来了。功能方面新版的DPress有了些变化。

  • 提供对 Markdown 的支持(也只支持Markdown)。
  • 使用 EpicEditor 实现Markdown的实时预览。
  • 使用 DISQUS 实现文章的评论功能。(注:既然已经有了这么好用的评论服务,为什么还要自己写代码呢)
  • 支持代码高亮度。相关的语法说明见:Fenced Code Blocks
  • 支持Tag
  • 支持RSS
  • 使用 Django FileBrowser 提供文件上传的支持。
  • 使用 flatpages 提供对About等自定义页面的管理。
  • 内置GOOGLE ANALYTICS的支持
  • 代码量非常的小
  • 模板和代码分离的比较清晰,要创建一套自己的模板会是一件比较容易的事。

已知bug:直接从网页复制文本时EpicEditor的编辑框内会自动的添加一些看不见的html标签,将导致文本显示不正常。需要粘贴文本时最好先将文本粘贴到写字板中,再重新复制粘贴。

WP7开发初体验 — WP7版“百度.ting”开发记录


项目地址: lb.ting(github)
如果你有WP7手机,可以下载XAP文件体验一下:lb.ting.xap

感觉已经有段时间没有接触过什么新技术了,或许也是想看看自己接触一项全新的技术需要多少时间来熟悉。近来想看看WP7开发。
上周在MSDN简单的看过一些WP7开发文章后决定动手做自己的地一个WP7应用。因为只是想简单的体验一下WP7开发,因此跳过系统学习的阶段直接动手。
这次想做的是百度.ting的WP7客户端。虽然虾米电台、豆瓣电台等电台网站/软件已经一大堆了,但百度.ting还是有自己的优势。百度电台的的音乐码率普遍为128k,比其他电台要好出不少。大多音乐电台支持直接手机访问,直接浏览器访问无法实现音乐的后台播放。
C#刚出时大家评价C#是Delphi和Java的杂交产物。我虽没什么C#的开发经验,但相信凭自己的Java与Delphi经验应付一般的C#代码还是没多少问题的。电台软件的功能以及技术点都不多,我很乐观的想周末在家宅个两天,折腾出地一个基础版本。显然,这个计划已经失败。接触一项新技术的遇到的问题远比我想象的要多,中间也走了不少弯路。
目前第一个初步可用的版本已经完成。我会将代码放到github上。我希望在接下来一段时间里对这个应用再进行一些优化,然后上线到WP7的商店。
下面的文章里会我记录下一些我遇到的问题。

准备工作,知识储备

微软在帮助文档方面做的非常的好,你可以很容易找到自己需要的资料。

开发过程中遇到的相关问题以及解决方案

百度.ting API

直接用FireBug对百度播放器的请求进行分析,可以很轻松的得到大部分API。这些API中唯独缺少电台列表的API。该问题最简单的解决方案是直接分析网页获取到电台列表,然后在程序里写死。另外还可以让程序对电台首页的html代码进行分析,从中提取电台列表。方案一,感觉有些傻。方案二实现起来又过麻烦。于是有了方案三。
百度.ting有官方的Android客户端,而Android程序是比较容易反编译的。使用 Dedexer 反编译后,得到相关API。Android客户端和web版本电台使用的并不是同一套API,而且从接口的参数类型上看服务端似乎用的是Java。

//主要接口
//获取频道列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getCategoryList&format=json
//获取频道歌曲列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getChannelSong&format=json
//获取专辑的歌曲列表
http://tingapi.ting.baidu.com/v1/restserver/ting?method=baidu.ting.radio.getArtistChannelSong&format=json
//获取歌曲的详细信息
http://ting.baidu.com/data/music/links?songIds=

JSON解析

最初我希望JSON能被自动解析成LIST/MAP类型的结构,查了一轮并没有找到比较好的解决方案。在C#中JSON都需要和对象进行绑定。如果手动为所有JSON数据创建相关的数据类,无疑是意见很麻烦的时。我相信我并不是遇到该困扰的第一人。果然网上已经有不少C#的JSON工具。
json2csharp 只要你输入json,改网站会自动帮你生成相关的数据类。

配置文件的存储

大多应用程序都需要保存一些自定义的配置数据。在WP7.1后,本地数据的使用变得非常的简单。本地数据库的使用可以参考 如何使用 MVVM 为 Windows Phone 创建本地数据库应用程序
lb.ting最终方案中并没有使用本地数据库。我采用了直接将JSON数据持久化的做法进行数据存储。直接持久化JSON的方案在数据的可靠性比较差,且配置文件格式的修改会成问题。JSON持久化的方案胜在够简单。

后台音频播放

WP7不支持后台进程,为了保证音频的后台播放需要为应用创建对应的后台音频播放代理。按照通常的理解,动态更新播放列表是一个必须的功能。WP7的后台播放代理还就没有更新播放列表的API。在MSDN的例子中播放列表直接写死在程序里。
去网上转了一圈也并没有找到比较好的解决方案。网上的建议是将播放列表保存在数据库或本地文件中,通过创建一个“中间层”的方式同前台UI进行交互。lb.ting最终采用了将播放列表持久化到本地存储区的方案。

baidu图片获取

专辑封面在浏览器可以正常显示,客户端则无法显示专辑图片。怀疑是代码的问题,于是将图片源换为豆瓣,图片正常显示。一通折腾,发现是百度的防盗链引起。将图片的加载换为WebClient实现。

public static void SetImageUrl(Image img, Uri uri, Uri defaultUri)
{
    WebClient wc = new WebClient();
    wc.Headers["Referer"] = "http://www.baidu.com";
    BitmapImage bi = new BitmapImage();
    img.Source = bi;
    wc.OpenReadCompleted += (s, e) =>
    {
        try
        {
            bi.SetSource(e.Result);
        }
        catch (Exception)
        {
            //bi.UriSource = defaultUri;
        }
    };
    wc.OpenReadAsync(uri);
}

CSRF(Cross-site request forgery)以及django中的处理方法

CSRF(Cross-site request forgery) 中文名是跨站请求伪造。
简单的说是用户访问了安全的网站A(比如支付宝)并登录,之后用户又访问了问题网站B。网站B包含恶意代码,在访问网站B的时候,网站B的网页偷偷的向网站A发起post(get)请求进行转账操作。由于你已经登录过网站A,网站A误以为请求是用户自己发起的安全请求。在操作成功后你的钱啥的也不见了。
当前Django的处理方法:
在用户访问网站时为用户生成一个CSRF防范用的token并将token保存到cookie中。每次用户发起请求时需要将token的值作为表单字段一同提交。服务端对用户提交的token值同cookie中的token进行比较,如果相同则认为安全。
由于浏览器对cookie的访问有严格的限制,问题网站B无法访问到网站A的cookie无法正确的设置token。

sentry,异常记录的云托管平台

最近打算为公司的应用增加异常跟踪功能,所以又去看了看 sentry
sentry 最早是 disqus 发布的一个开源的django异常跟踪APP。一段时间没见发现sentry已经发展成了一个支持 Python、PHP、Node.js、Java 的异常跟踪中心,而且还推出了对于的云服务 getsentry.com
getsentry.com 按月收费,不同方案的区别在于日志的保存时间以及每日的记录事件数的配额。和大多云服务一样 getsentry.com 也提供免费方案,免费情况下每日有100个事件配额。 由于 getsentry.com 本身是一个开源产品,部署成本也不高。 getsentry.com 的服务是否真能让广大用户接受还很难说,但这个做法让人不得不佩服disqus的想象力,连一个异常跟踪平台也能做到这个程度。
sentry 作为一个开源项目有着不错的代码质量,sentry代码中有不少可以借鉴的东西。

  • tox 似乎越来越多的python项目有在使用tox。tox是一个python虚拟环境管理工具,主要用于为测试用例创建虚拟环境。tox可以为不同的python版本单独创建虚拟环境,以测试在不同python版本下的工作情况。
  • 实现了一个自己的BaseManager,增加get_from_cache方法。用缓存来加快查询速度。
  • twitter-bootstrap sentry也是用bootstrap作为前端框架。因为有对bootstrap做过一些调整,所以UI不至于太过bootstrap。
  • celery、nose、gunicorn……

django的js/css压缩组件Django Compressor

为了加快网站的加载速度,我们通常要多js和css进行压缩处理。这些js和css的压缩工作如果都手动处理,费时费力。
Django Compressor 可以实现js/css的自动压缩。Django Compressor在易用性方面做的非常好,按照 文档 做简单的设置后就可以正常工作。强烈建议大家去将文档完整的看一遍(文档很短)。
使用的时候,只需要将css/js放到 compress 标签中 Django Compressor 即可自动进行处理。在debug模式时, Django Compressor 不会对做任何处理。在非debug模式时,Django Compressor会自动对js/css进行压缩,并将压缩后的问题输出到django的 STATIC_ROOT 目录。所以请务必保证 STATIC_ROOT 目录进行了正确的设置。

{% load compress %}
{% compress  [ [block_name]] %}

{% endcompress %}
{% compress css %}

{% endcompress %}

coffeescript、less 支持

在开发阶段coffeescript和less可以直接使用js来处理,在正式发布时处于加载速度的考虑需要预先编译成js和css。 Django Compressor 提供 COMPRESS_PRECOMPILERS 设置,根据type类型进行预处理。

COMPRESS_PRECOMPILERS = (
    ('text/coffeescript', 'coffee --compile --stdio'),
    ('text/less', 'lessc {infile} {outfile}'),
    ('text/x-sass', 'sass {infile} {outfile}'),
    ('text/x-scss', 'sass --scss {infile} {outfile}'),
)

时间线网站“似水流年”上线

网站地址: http://jstwind.com
在很早前就想做这么一个关于时间线的网站了,并在今天年初开始真正开始行动。中途因为各种原因将这件事情给搁置了,直到最近才重新启动。目前还只是完成了基础功能,细节方面也未能调教到自己满意的程度。不过不管怎么说,网站总算是基本可用了。欢迎大家上去看看,去制作自己感兴趣的时间线。
目前网站上只有一个关于 乔布斯 的时间线。事件的编辑比我预想的还要花时间,这也让我更希望能早些加入时间线的协作编辑功能。
这是我在继 LBForum 之后真正有在用心做的一个私人项目。项目后台代码方面的工作量倒还好,主要的工作都花在了前端上。事件的编辑是一项非常繁琐的工作,因此花费了不少时间来使用AJAX来提高事件编辑的易用性。项目代码托管在 github 上。项目地址在以前的博客里发布过,如果你感兴趣的话可以去找找。
最后把用到的主要技术和服务列一下,接下来一段时间可能会将其中用到一些技术写成博客:

  • 网站部署在 webfaction ,采用apache+mod_wsgi的方式进行部署。
  • 由于webfaction在国内的访问速度并不理想,静态资源托管在sina的 SAE 平台上。因为申请了SAE的开发者认证,每月有1.5w的免费云豆资源,闲着也是闲着。
  • 域名在 godaddy 购得。在使用优惠码后,一年的费用不足$2。
  • 域名的DNS解析用的是 DNSPod 。 DNSPod在易用性方面确实做的很不错。
  • 使用 腾讯企业邮箱 。 或许很多人对腾讯比较反感,但腾讯的产品确实都还做的不错。使用腾讯企业邮箱的重要原因之一是免费。
  • 服务端的开发技术依旧用的是 Django 。使用自己熟悉的技术可以大大的加快开发进度。
  • 前端用的是 twitter-bootstrap 。bootstrap目前已经有烂大街的趋势了,不过这东西确实好用。为减少bootstrap的痕迹,下一步考虑给jstwind换个配色方案。
  • JS的基础库用 JQuery 。现在JQuery基本上已经是JS标准的一部分了。
  • 时间线的JS控件用的是 Timeline JS 。虽然这个JS组件还有不少让我不太满意的地方,但这个组件胜在漂亮,而且作者非常勤奋,github上提的issue基本上在一天内就会有回复。
  • 文件上传用的是 jQuery-File-Upload 。纯JS实现,不依赖Flash即可实现文件的批量上传。缺点是如果客户用的浏览器版本太老就无法使用批量上传功能了。
  • Django的第三方APP方面用的就比较多了,其中包括 django-userena、django-taggit等,以及部分我自己写的与修改的APP。第三方APP的详细列表可以查看项目中的 requirements.txt 文件

迁移到python3

python3自2008发布以来,已经历经了快4个年头。python3发布初期的速度慢,第三方开发库少的问题已得到了很好的改善。似乎已经没有太多的理由死抱着python2不放了。
考虑到目前的大多系统还都跑在python2.x上,直接迁移到python3还是有些冒进。最理想的方式是新代码都可实现python2&python3的兼容,日后可以平滑升级。下面的一些资料可以帮助你实现到python3的迁移。

使用反向代理为SAE“绑定”顶级域名

朋友的VPS服务器近期经常出现内存超标的问题。想我的SAE免费资源闲着也是闲着,于是将上面的博客迁移到SAE平台。
SAE商店中的wordpress可以免费安装使用。受SAE平台的限制,SAE版的wordpress有部分功能限制,比如可用的主题也就那么2~3款。另外一个比较大的问题是无法绑定域名。解决该问题的方法之一即是使用反向代理。
我的虚拟主机上用的是apache,下面是相关设置。
httpd.conf

LoadModule rewrite_module modules/mod_rewrite.so
LoadModule wsgi_module modules/mod_wsgi.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

.htaccess

RewriteEngine On
RewriteCond %{HTTP_HOST} ^domain.com$
RewriteRule ^(.*)$ http://domain.sinaapp.com/$1 [P]

SAE部署Django1.3应用问题总汇

花了些工夫将碎片网部署到了SAE,中途遇到各类问题。感觉SAE看上去很美,实际上却并不是太成熟(至少python版如此)。
下面记录下我遇到的一些主要问题以及解决方法。

django版本问题

Django1.4都即将发布了,SAE平台自带的SAE版本依旧为1.2x。为使用django1.3版本,你需上传自己的django。具体做法可参考SAE手册中的runtime.html#virtualenv

日志模块出错

最先遇到的是日至模块的问题。错误显示AdminEmailHandler中构造某个类时带了is_mail这个参数,但目标类的构造函数根本就不支持。好在日至模块不是必须。将日至处理模块换成django.utils.log.NullHandler解决问题。

'null’: { 'level’:'DEBUG’, 'class’:'django.utils.log.NullHandler’, },

local_thread问题

在index.wsgi中加入

import threading
from django.utils import _threading_local
threading.local = _threading_local.local

settings文件

SAE默认安装了不少python包,其中包括django-userena。糟糕的是SAE将django-userena的demo项目也加到了python路径,而且加载的优先级比项目代码还高。直接导致os.environ[‘DJANGO_SETTINGS_MODULE’] = ‘settings’找到的是userena的settings文件。最终将settings文件改名为qnotes_settings.py解决该问题。

其他问题

  • 昨天部署上线后经常出现数据库错误,错误提示为Caught OperationalError while rendering: (1045, ‘access deny’)  。SAE数据库不支持长连接,30s后主动超时。但django本就会在每次请求后自动关闭数据库连接,理论上不应当出现类似问题。该问题今天莫名其妙的自己好了。
  • 服务器非常不稳定,经常长时间的无法访问,不定期的可以正常访问。
  • SAE可能hack了python的包加载机制的原因,SAE的python包的加载行为有些奇怪。本应当最先加载当前目录下的包,实际上却不一定。