这篇文章将简单地描述一下博客的搭建过程。博客源代码见这里
首先要感谢stevelosh, 博客的基本设计参考了他的博客,来源于stevelosh.com 这篇文章。
博客从2013.8开始搭建,暑假主要处于学习阶段,学期途中插空写了一点,2014寒假把基本框架完成了。
对于想基于代码学习/搭建博客的行为,本人不能够更欢迎,但是请至少做到:
在运行之前修改settings.py
,再不济,请至少把这里的email替换成你自己的email
否则,你的代码运行时的出错信息将被发送到我的邮箱,不利于你进行调试,也给我带来极大困扰。不胜感激!!
设计
在博客搭建之初,我就决定要尽可能少写前端代码。最简单的方法自然是使用CMS。实际上有不少使用Django的CMS,比如最著名的django CMS。不过调研一番之后发现一个问题:没有一个支持Python 3。考虑到博客里必然有一堆Unicode字符,我还是希望能用Python 3的,所以就放弃了CMS。
(2014.6.25 EDIT: 目前发现django CMS已经能支持Python3.4了,至少3.0.2版本以及更新的都可以。参见3.0.2 release)
好在马上找到了替代品:HTML模版。最后使用了css3templates 里面的一个模板。模板基本只包含HTML/CSS/JS代码,这正是我需要的东西,因为后端本来就是想从零开始写的。
文件路径结构
用Django建网站,static
和 media
到底怎么放置?这个问题没有标准答案,反正我没有用 STATICFILES_DIRS
,也就是把所有static files都放在了appname/static/appname
里面。这样的好处是文件结构比较清晰,坏处是如果app多了同样的文件可能要存很多份,会减慢网站加载速度。下面是我的文件夹结构,供参考,文件均未列出:
my_blog
├─media
│ ├─content
│ │ └─BlogPost
│ │ ├─2009
│ │ ├─2013
│ │ ├─2014
│ │ └─images
│ └─files
├─My_Blog
│ ├─css3two_blog
│ │ ├─migrations
│ │ ├─static
│ │ │ └─css3two_blog
│ │ │ ├─documentation
│ │ │ ├─fonts
│ │ │ ├─images
│ │ │ └─js
│ │ └─templates
│ │ └─css3two_blog
│ ├─mytemplatetags
│ │ └─templatetags
│ ├─my_blog
│ └─templates
│ └─admin
└─static
├─admin
│ ├─css
│ ├─img
│ │ └─gis
│ └─js
│ └─admin
└─css3two_blog
├─documentation
├─fonts
├─images
└─js
画这个东西的时候才知道Windows/Linux下有个命令叫 tree
,大好评!
注意在开发的时候,my_blog/static
里面可以不放东西,只要 DEBUG=True
就行。如果要接受文件上传,那么在 urlpatterns
后面添加:
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
实际部署时该怎么来就怎么来。
有一个 app 叫做 mytemplatetags
,它是专门用来装 custom template tags/filters 的,参见 官方文档:Custom template tags and filters.
主机
在哪里买主机是个需要好好思考的问题。使用VPS的话,可选择的范围很大。但是第一次建网站,没什么经验,怕配置不好。更重要的是用VPS不够短平快,而我希望博客能尽快投入使用。然后就查到了 WebFaction。虽然我并不想做广告,但真的向大家推荐这个主机服务,尤其是怕麻烦的人。Webfaction是个PaaS服务,支持许多语言/框架,而且服务很完善,不论是在它的 Q&A Community 提问还是open a ticket,工作人员的解答都很迅速。虽然是美国的主机(也可以选东南亚的),但是国内访问速度不错。
至于其它选择,外国人用 heroku 的比较多。国内主机不太了解,SAE
据说不错。
版本控制
显然是Git+Github,没什么好说的。印象深刻的是有一次在主机上玩火敲了这个命令 find . -not -name 'xxx' | xargs rm
,结果大家都懂。还好WebFaction重建一个Django app就是点击几下的事情,代码又在Github上,所以很容易就恢复了。
还有,settings.py
这个文件最好ignore掉,因为部署和开发在设置上差异很大,所以这个文件在本地和服务器上需要保存不同的版本。顺便分享一下 .gitignore
吧:
.pydevproject
.project
.settings/*
db.sqlite3
css3two_blog/migrations/*
css3two_blog/static/css3two_blog/css/unused/*
*.pyc
为什么settings.py
没有包含在内呢?这个问题比较复杂,在我的另一篇文章 Django Best Practice: How to deal with settings.py in Git 里面有详细讲述。
使用Markdown
这是跟stevelosh学的,而且似乎越来越多的个人博客都在这么做。我的每一篇文章都是用markdown写的,然后渲染成html。关于如何渲染有不同的做法,一开始尝试了pygments
+ django markup
,虽然可以实现代码高亮不过样式比较难看。目前采用的方法是把markdown文本发送给Github API,得到 gfm
格式的html。需要注意的是得到的 html 是没有样式的,所以又在网上找了别人写的 gfm 的 css,添加到页面里,显示出来的页面就和 Github 的 README 差不多了。
这部分的工作有一定难度,需要设计出一套 markdown 和 html 文件的保存机制,并且能正常应对 admin
界面中的修改/删除操作。具体可见
models.py
,admin.py
其它
- 使用
south
来应对 Model
的变更
- 尽量把逻辑放在
models
和 views
来完成,因为Django的 templates
实在很难用
- 虽然用了 HTML 模版,但是在前端仍然花了很多时间
- 博客完全可以用
Hyde
搭建,这样加载起来能快一些
初版:2014-2-5
EDIT:2014-2-20 加入文件路径结构小节,增补其它小节内容
EDIT:2014-6-25 django CMS 已经支持 Python3.4
本人看过的东西应该算不少,但是一些理论上必看的作品却没看过。有些作品确实很好,我也知道,但是因为没看过,所以就没有列上去。
最喜欢的...
项目 |
作品/人 |
最喜欢的动画 |
永生之酒 |
最喜欢的国产动画 |
秦时明月 |
最喜欢的漫画 |
现视研 |
最喜欢的国产漫画 |
拜见女皇陛下 |
最喜欢的监督 |
渡边信一郎 |
最喜欢的电影导演 |
细田守 |
最喜欢的轻小说家 |
成田良悟 |
最喜欢的漫画家 |
木尾士目 |
最喜欢的OST |
TARI TARI/江户盗贼团/FLCL |
最喜欢的▇▇▇▇ |
的良米兰 |
最喜欢的电影 |
狼的孩子雨和雪 |
最喜欢的打斗方式 |
读或死 |
最喜欢的歌曲 |
光と影のロマン |
动画排名(个人喜好程度)
排名 |
作品 |
1 |
永生之酒 |
2 |
现视研 |
3 |
反叛的鲁鲁修 |
4 |
命运石之门 |
5 |
死亡笔记 |
动画排名(作品质量)
排名 |
作品 |
1 |
星际牛仔 |
2 |
永生之酒 |
3 |
FLCL |
4 |
空之境界 |
5 |
皇家国教骑士团 |
其它,想起来什么说什么
STEINS GATE:想从逻辑上理解 SG 的一切努力都是徒劳 我的豆瓣影评
甲贺忍法帖:催泪
皇家国教骑士团:完美动画
EVA_Retake:本子的巅峰
黑轻音三部曲:最喜欢的本子
FLCL:唯一配得上“天才”之名的作品
城平京:剧本总是设定惊艳,情节不错,结尾平淡。动画化的作品在当年都是人气大作,但仅限当年。
死亡笔记:尝试了两次都没有把第二部看完
可能是动画史上最帅的镜头:Spike:Bang!
Fate/Zero:各方面都不错,就是很难脱颖而出
黑之契约者:第一季简直完美,第二季除了最后一集也很完美。最后一集告诉你什么叫坑爹,考虑到之前剧集的质量,说是落差最大的结尾也不为过。~~黑银的本子质量好高!~~
皇帝的新娘:听朋友推荐看的漫画,绝对让人大开眼界。日和里面剑圣大和那个故事的真人版
甲斐谷忍:不看他的作品人生不完整,斗智漫画的巅峰
心理测量者:每个情节你都在至少一本科幻小说里见过
道子与哈金:以前没有,未来也不可能有比它更文艺的动画,意外地很好看
俺妹:第一部特别喜欢,第二部...
冰菓:受FFF团祝福的CP。米泽穗信写的时候绝对打死也想不到冰菓作为动画能有这样的人气(只能说京都太厉害)。他的本格推理作品《算计》也很不错
Another:原作在我看来根本就是不算是推理小说,不明白为何在十一区人气那么高
机战:最讨厌的一类作品,Code Gease是看过的唯一一部
后宫:高中的时候很喜欢,近几年几乎不看,太无聊。猫娘就是那时候开始追的
OST:《Tari Tari》、《江户盗贼团》、《放浪息子》、《Gunslinger Girl》这四部动画的OST在我看来是最好的,可以循环放着听
今敏:《东京教父》还没看,其它的作品没一部看懂的...
此时此刻的我:想弄死男主
冈本伦:最geek的漫画家
魔女猎人罗宾:一部很特殊的动画 我的豆瓣影评
Gunslinger Girl/神枪少女:一部日本再也拍不出的动画 我的豆瓣影评
School Rumble/校园迷糊大王:最佳校园漫画,没有一个路人,动画没拍完很遗憾
星舰驾驶员:大概是我见过的人气和质量反差最大的动画,硬核神作 我的豆瓣影评
穿越时空的少女:我看过最糟糕的时间旅行题材作品 我的豆瓣影评
从未完结追到完结的漫画(更新中):
Since this is a videochat plugin, the core part is of course imeplementing video chatting between users. It's really simple actually.
If you have heard of WebRTC, you should be aware that modern web browsers have great power. I'll make a brief introduction to WebRTC here, but if you want to truely understand it, further searching and reading is necessary.
WebRTC is a free, open project that enables web browsers with Real-Time Communications (RTC) capabilities via simple Javascript APIs.WebRTC allows web pages to access local multimedia devices like a webcam and microphone, and transmit these media streams to another WebRTC capable browser via a peer-to-peer network channel. These media streams can also be accompanied by a powerful data channel that lets developer exchange arbitrary data between two peers! Its mission is to enable rich, high quality, RTC applications to be developed in the browser via simple Javascript APIs and HTML5.
Visit apprtc.appspot.com (one of WebRTC's official examples to show how it works), you'll find that it's just the application I use for my plugin. All I did is putting the website into an <iframe>
, and that's it, incrediblely simple but works flawlessly!
View the code for more information.
Though the appspot site works really well, it has some constraints. If two people have entered the same room
(same url), then the room gets full for it only supports a peer-to-peer channel. To avoid such mess, everytime a user comes to the VideoChat page, a random number will be generated and act as the query string of apprtc.appspot.com
, which prevents you from entering a full or half-full room.
Frankly speaking, although it's a plugin to implement video chatting, the most difficult part is not on videochat, but on how to invite your friends to talk to you.To do that, I use elgg built-in User pickers
view.
Next step is to find out how elgg actually sends a message, it's rather easy once you have the experience using Chrome Devtools or firebug.It turns out that a message is an HTTP POST request.
e.g.
There is one thing left which is implementing your own sending/inviting machanism and making a redirection once your friends clicks the link in your msg. I'll let code do the talking:
li += '<button id="invite" float="left" height=10 width=100>invite</button>';
$('<li>').html(li).appendTo(users);
These two lines add the invite
button.
var redirect_url = base_url + "videochat/vc?r=" + rand_num;
var body = '<a href="' + redirect_url + '">请和我签订契约,加入视频聊天吧!</a>';
var msg_params = {
__elgg_ts:elgg.security.token.__elgg_ts,
__elgg_token:elgg.security.token.__elgg_token,
recipient_guid: info.guid,
subject:'有朋友邀请您参与视频聊天',
body: body,
};
$('#invite').click(function(){
$.ajax({
url: base_url + 'action/messages/send',
type: 'POST',
data: msg_params,
success: function(msg){
alert('your msg has been sent');
}
})
});
This part send the HTTP POST request we mentioned above.Note that the content of an invite message contains a link, redirecting the invitee to the videochat page. The random number is also passed as a query string parameter, forcing the message invitee enter the same room as inviter. The code below shows how it is done.
if ( window.location.search != "" ) // has query string.
rand_num = window.location.search.substr(3); //?r=num
else
rand_num = Math.floor(Math.random()*900000+100000);
$('#video').prop('src', "https://apprtc.appspot.com/?r=" + rand_num);
some other things you should pay attention to
- I use
ajax
to prevent current page from reloading after sending a request. Why? Because two people can chat if and only if their iframe
elements have the same src
attribute (i.e. same room). If you reload, as mentioned before, a new random number will be generated thus making the inviter enter a different room.
- When sending HTTP request,
__elgg_ts
and __elgg_token
are necessary parameters. This is a machanism elgg uses to validate message. Luckily these two variables are stored in global scope:
- If you try to search friends in another page rather than the videochat Page, you still get a
invite
button, however when clicked, you will be alerted:
This is done by detecting the existense of that random number since it only exists in the videochat page.
if (typeof rand_num != "undefined"){
//add invite button click EventListener, see above code
}
else
alert('do this on VideoChat page!');