python的环境与ruby还是有很大不同的,而且框架多如牛毛,将遇到的坑记在这里。
ps: django跟屎一样。
直觉上很容易当作virtualenv当作rvm的存在,但实际上python的版本往往并非大问题,更多需要对付的还是三方库。virtualenv其实更多扮演的是Bundler的角色,但在操作上有所不同。
Bundler通过Gemfile列出当前项目需要的gem并将它们安装到系统,在代码中通过Bundler.require(:all)引入gem,并在bundle exec中设置环境变量之类,以选择正确版本的gem。
virtualenv会创建一个目录,将特定版本的python以及需要的三方库安装或者link在里面。通过一段bash来进入特定的venv,退出时通过一条deactivate命令。而并无bundle exec的对应物。
项目目录下的manage.py相当于rails中Rakefile的作用
- 启动本地服务器:
./manage.py runserver - 依据model来初始化数据库:
./manage.py syncdb
需要留意的是django下似乎并无migration的对应物,syncdb仅在第一次初始化时有效,如果Model的结构有所更改,应尝试下south。
django中View相当于Rails中Controller的一个action。urls.py指明了url到view之间的映射关系。
view可以是函数,也可以是对象。django内置的一些View类(如CreateView、UpdateView、ListView之类)就是通用的view了,写代码换成改参数。文档见 https://docs.djangoproject.com/en/1.2/ref/generic-views/
app是django的一个核心概念,admin是app就算了,甚至静态文件都是app的存在。大体与rails engine的性质差不多。
静态文件无法显示? 在urls.py下面加上:
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
...
urlpatterns += staticfiles_urlpatterns()
django中相当于url_for的函数叫做reverse()。取urls.py中定义的名字为参数,返回url。
如果不想敲这个奇葩的reverse(),可以在模板中使用{% url url_name %}命令。
可以使用FileWrapper:
response = HttpResponse(FileWrapper(file(path)), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(path)
return response
与Rails不同,django将validtion的逻辑独立到了Form对象中。Form对象下面是Field对象,Field对象检查validation的方法叫做clean(),总的检查就是full_clean()。一般不需要直接调用它们,需要的是is_valid()。在view中写起代码来像这样:
form = SubmissionForm(request.POST)
if form.is_valid():
form.save()
return redirect('/success')
如果要额外的保存一个字段? 比如submission.author = current_user这种。直接给form设置字段是不可行的,需要这样save两次。第一个save只是为了得到model对象:
form = SubmissionForm(request.POST)
form.problem_id = problem.id
if form.is_valid():
submission = form.save(commit=False) # the key is commit=False
submission.author_id = request.user.id
submission.save()
作为rails中flash的对应物,django提供了一个message模块 https://docs.djangoproject.com/en/dev/ref/contrib/messages/
如果按照文档中的设置依然什么都没有不显示,那么修改view中所有的render_to_response方法加上context_instance=RequestContext(request)这个参数。
return render_to_response('oj/user.html', locals(),
context_instance=RequestContext(request)
Model中常见到null=True, blank=True这两个参数,意思相近,但不是同一个层次上的东西。null用于指示生成schema时允许字段为null,而blank用于指示表单的生成与验证。
注意django中只在一个model中指定many-to-many的关系,不然会创建出两个many-to-many关系。比如:
class Problem(models.Model):
solved_by_users = models.ManyToManyField('UserProfile')
class UserProfile(models.Model):
...
随后,两边一边是:problem.solved_by_users,一边是:userprofile.problem_set
扩展auth模块的User。不能直接加字段,只能建一个one-to-one的关联。
http://nerdydevel.blogspot.com/2012/07/extending-django-user-model.html
django在syncdb时并不会设置数据库的编码,所以在manage.py syncdb之前务必跑一下:
ALTER DATABASE ... CHARACTER SET utf8 COLLATE utf8_general_ci
不然会有Incorrect string value的错误出现。
可以在后台加一些测试数据之后,一股脑dump出来
./manage.py dumpdata --format=json myapp > /path/to/myapp/fixtures/initial_data.json
需要用的时候,先将数据库reset掉,再syncdb:
./manage.py reset myapp
./manage.py syncdb
安装
pip install south,然后在django的settings.py的INSTALLED_APPS下面加上south。
使用
- 初始化:
./manage.py schemamigration app_name --initial - 更改model之后,生成migration文件:
./manage.py schemamigration app_name --auto - 或者人工编写migration文件:
./manage.py schemamigration app_name --empty - 执行migration:
./manage.py migrate
从没见过django这般蛋疼的模板系统,换到jinja2可能会好一点。这里可以使用coffin,pip install coffin之后,在django的view中加入from coffin.shortcuts import render_to_response将django默认的那个render_to_response替换掉。
virtualenv的作用仅仅是隔离不同的环境。环境在这里指的就是Python执行文件位置和一堆库的组合。Ruby里也有, https://github.com/nkryptic/sandbox , https://github.com/fesplugas/ruby-virtualenv 。Python里面和RVM对应的是PythonBrew http://pypi.python.org/pypi/pythonbrew 。
和
Bundler.require更接近的是pkg_resources.require。命令行下用easy_install -m可以同时安装同一个库的多个版本。因为pkg_resources.require要求的参数就是字符串,在这个基础上,你自己定义个EggFile,像Bundler那么干也是可以的。但现在更常见的做法是用pip。用
pip的话,你在开发的时候,有啥依赖的库就直接装上,等到开发结束后,用pip freeze -l得到一个列表,(编辑之后)保存成requirements.txt。下一次安装这些依赖的时候,只需要运行pip install -r requirements.txt就可以了。但pip最大的缺陷是Windows上用不了。virtualenv最大的坑是命令行,老版本只有--no-site-packages参数,默认行为和新版本--system-site-packages一样,新版本加了--system-site-packages参数,但默认行为和老版本--no-site-packages一样,尽管API是没变。这导致了之前直接通过命令行调用的脚本都废了。