迅雷如今已经变成迅雷浏览器了(迅雷9)

我还一直在用迅雷极速版,简直是业界良心:

  1. 下载速度拉满,同时对比迅雷9,迅雷9就没有速度,不知道是不是限速了
  2. 界面清爽没有广告
  3. 专注下载,没有其他额外功能

不难看出在那个时期公司内部还是有一批人专心做产品的,可是后期由于商业化不足被迫转型。

回到正题,最近使用过程中,下美剧,经常有显示版权受限,确实挺头疼
在网上终于找到了解决办法,分享给大家,原理就是通过hosts屏蔽部分连续迅雷服务器的请求

只需要hosts添加如下:

1
2
3
127.0.0.1 hub5btmain.sandai.net
127.0.0.1 hub5emu.sandai.net
127.0.0.1 upgrade.xl9.xunlei.com

理论上就可以,先用着吧,貌似没有什么好的替代工具

说起我的小博客的历史啊真的很久了。最早之前是在10年暑假,那时候自己学习ACM,在POJ上面做题目。每天都做几道,当时想着把好不容易把这些题目写出来,到时过不了多久在看到的时候就忘了,这样多浪费啊,于是我开始谋划我的博客,当时wordpress很火,我也就开始了我的博客生涯,买了个90元的博客主机,买了个域名,开始了我的做题笔记。

后来在12年的时候,那时学习python,准备往web方向走,虽然django的文档还是很多,但是必定需要一个实际项目入手,我决定将我的博客重写,于是用django实现了一个版本,写出第一版后就没怎么改过了,现在在去看我当时写的代码,真的是很不规范,很多代码现在我都看不下去,不过必定是我的一个作品,呵呵。具体项目在JoveSky,虽然这是我的地一个django项目,但是相对来说很多功能都实现了,当初选择markdown作为书写格式,也为这次迁移减少了很多麻烦。

当初python的那个版本,一直挂在我朋友的SAE上,也就一直没要钱,但是不知道怎么回事,速度很不稳定,后来想想,其实我的博客,真的也只有文字,那么还是专注于写作吧,于是我选择了使用静态博客。既然是静态博客,当然要找一个python写的了,(不行就自己写个,^_^)

搜索了半天,找到了python的wiki,https://wiki.python.org/moin/StaticSiteGenerator

简单看了下有一些不错的,其中我比较有名的是Pelican,但是他的模板渲染用的是jinja2,这个我不是很熟,最近跟转到后台那边去了,也正好在用mako,所以想找个mako模板的后面我该起自己的主题也方便一些,最后找了半天选择了Nikola,选择他书要有3个原因

  1. 调试方便,自带开发服务器,还可以通过插件实现实时检测文件变化。
  2. 方便部署到github page,他提供了一个自动化部署的命令,我看了下源码,就是帮你执行了一些命令,向我这样的自然要偷懒。
  3. 模板嵌套不错,我看了下他的默认模板,写的非常不错,我没看过别的,也不知道应该是什么水平,不过看着顺眼就好。

迁移过程

迁移文章

我之前django博客中,是用markdown来书写,所有文章都在数据库内,现在要都迁移出md文件,首先我去SAE中的数据库导出,其实可以到处成XML或这YAML但是我没这么麻烦,直接到处成SQL,然后导入了我本地的mysql,自己书写了个脚本来完成将文章变成文件的工作。简单晒下我的脚本吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# -*- coding: utf-8 -*-

import MySQLdb
import os

conn = MySQLdb.connect(host='127.0.0.1',user='qf',passwd='123456',charset='utf8' )
cur = conn.cursor()

conn.select_db('qf_core')

count = cur.execute('select title,slug,create_time,markdown from blog_post')

result=cur.fetchall()

template = """<!--
.. title: %s
.. id: %s
.. slug: %s
.. date: %s+08:00
.. tags:
.. link:
.. description:
.. type: text
-->

"""

for r in result:
title = r[0]
slug = r[1]
time = r[2]
markdown = r[3]
f = open('%s.md'%title,'wb')

f.write(unicode.encode(template%(title,slug,time.strftime('%Y-%m-%dT%H:%M:%S')),'utf-8'))
f.write(unicode.encode(markdown,'utf-8'))

这样我们就看到了一文件夹的文章,python做这种小工具真心简单。

集成多说评论

之前我所有的系统都是使用多说评论,无论是我的wordpress还是django,多说做的是真不错,但是这个框架必定是老外写的,其中集成了disqus,我参考disqus的实现将多说集成进去,找到源码中的comments_helper_disqus.tmpl,参考他我完成了comments_helper_duoshuo.tmpl源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
## -*- coding: utf-8 -*-
<%def name="comment_form(url, title, identifier)">
%if comment_system_id:
<!-- 多说评论框 start -->
<div class="ds-thread" data-thread-key="${identifier}" data-title="${title}" data-url="${url}"></div>
<!-- 多说评论框 end -->
<!-- 多说公共JS代码 start (一个网页只需插入一次) -->
<script type="text/javascript">
var duoshuoQuery = {short_name:"${comment_system_id}"};
(function() {
var ds = document.createElement('script');
ds.type = 'text/javascript';ds.async = true;
ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
ds.charset = 'UTF-8';
(document.getElementsByTagName('head')[0]
|| document.getElementsByTagName('body')[0]).appendChild(ds);
})();
</script>
<!-- 多说公共JS代码 end -->
%endif
</%def>

<%def name="comment_link(link, identifier)">
%if comment_system_id:
<a href="${link}#ds-thread">Comments</a>
%endif
</%def>


<%def name="comment_link_script()">
%if comment_system_id:
%endif
</%def>

部署

其实这个最好的部署环境是github page,但是大天朝你懂得,好在国内有很多良心企业,比如托总的gitcafe,稍微修改了下,我就将整个博客部署到上去

写在最后

博客是我技术的一个载体,真的记载了我从开始接触真正的技术到后来的练习的过程,我博客的变迁,也是我成熟的过程,我相信我的小博客不会就这么接触,也许还会有下一次迁移,倒是折腾吧!

AJAX是目前web开发少不了的环节,在很多时候不仅仅是get或者post数据,更重要的是要得到返回的数据,在我所开发的项目中大部分推崇的都是使用json作为返回格式,不仅在webapi上调用方便,在客户端上也有各种解析库!

作为一个web新手,逐渐使用AJAX频繁,也就自然而然的遇到了AJAX的跨域问题,这个时候我遇到了jsonp,仅仅多了一个字,不难看出他的本质还是json,但是他能够完成我们头疼的跨域问题

这篇文章就是用来解释这样一个问题,json和jsonp有什么区别,jsonp怎么调用,如果你是个web老手完全可以去冲咖啡了,刚刚学习,以此为记!没有什么人指点,可能很多都是我的个人见解。

阅读全文 »

django中提供便捷的分页服务,主要通过
Pagination来实现的,详细可以参考Django文档,通过简单设立每页显示的数量,来自动化获得分页object_list,先来看看调用方式,借用文档里面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)#新建分页,参数为所有的obj,和每页显示数量
>>> p.count#所有的obj的数量
4
>>> p.num_pages#分出来的页数
2
>>> p.page_range#页数的列表
[1, 2]
>>> page1 = p.page(1)#得到第一页
>>> page1
<Page 1 of 2>
>>> page1.object_list#第一页的obj列表
['john', 'paul']

>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()#是否有下一页
False
>>> page2.has_previous()#是否有前一页
True
>>> page2.has_other_pages()#是否有其他页
True
>>> page2.next_page_number()#下一页的地址,这里最好判断是否有,不然会异常
Traceback (most recent call last):
...
EmptyPage: That page contains no results

其实分页并不是一个复杂的流程,但是我跟喜欢使用官方提供的功能,省的自己浪费没有必要的时间,但是在使用的过程中,还是优点满足不了我们的用途,一般写导航的时候都会显示当前页前后3-4也的导航,向这样的:

![导航][2]

而我们仅仅能得到一个所有的列表,加之每次都要在模板里配置嫌多判断,前不久学习了一种好的写法,稍微改造了下,封装了下,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def my_pagination(request, queryset, display_amount=15, after_range_num = 5,bevor_range_num = 4):
#按参数分页
paginator = Paginator(queryset, display_amount)
try:
#得到request中的page参数
page =int(request.GET.get('page'))
except:
#默认为1
page = 1
try:
#尝试获得分页列表
objects = paginator.page(page)
#如果页数不存在
except EmptyPage:
#获得最后一页
objects = paginator.page(paginator.num_pages)
#如果不是一个整数
except:
#获得第一页
objects = paginator.page(1)
#根据参数配置导航显示范围
if page >= after_range_num:
page_range = paginator.page_range[page-after_range_num:page+bevor_range_num]
else:
page_range = paginator.page_range[0:page+bevor_range_num]
return objects,page_range

在模板中配套加入代码,这个模板使用bootstrap应该很好理解

分页出来的数据处理,像这样写

1
2
3
4
5
6
7
{% if objects.object_list %}
{% for p in objects.object_list %}
...
{% endfor %}
{% else %}
...
{% endif %}

然后在下面加入导航

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="pagination pagination-centered">
<ul>
{% if objects.has_previous %}
<li><a href="?page={{ objects.previous_page_number }}">前一页</a></li>
{% else %}
<li class="active"><a>前一页</a></li>
{% endif %}

{% for p in page_range %}
{% ifequal p objects.number %}
<li class="active"><a>{{p}}</a></li>
{% else %}
<li><a href="?page={{p}}" title="第{{p}}页">{{p}}</a><li>
{% endifequal %}
{% endfor %}

{% if objects.has_next %}
<li><a href="?page={{ objects.next_page_number }}">后一页</a></li>
{% else %}
<li class="active"> <a>后一页</a></li>
{% endif %}
</ul>
</div>

大家可能还关心view怎么写,给一个简单的示例吧:

1
2
3
4
def my_view(request):
all_objects = some_model.objects.all()
objects, page_range = my_pagination(request, all_objects)
return render_to_response('a_template.html',{'objects':objects,'page_range':page_range},context_instance=RequestContext(request))

简单来说下这样写的好处,一方面不用关心处理的逻辑,一方面不需要对模板进行特殊处理,将request传进去,连page参数都不用操心,相信这样的一个分页函数能帮到你!

django中的auth一般人都会用到不管是用他进入后台,还是自定义User model来实现用户管理,这都比必用的app,然后在使用中有一点很不方便,这两天研究了下,一般办法都是利用form解决的!但是苦于一直找不到想权限那样合适的控件!今天一个碰巧的情况解决了这些问题,不多说,重写groupadmin的form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from django import forms
from django.contrib import admin
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.admin import GroupAdmin
from django.contrib.auth.models import Group,User

class GroupAdminForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(queryset=User.objects.all(),
widget=FilteredSelectMultiple('Users', False),
required=False)
class Meta:
model = Group

def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
if instance is not None:
initial = kwargs.get('initial', {})
initial['users'] = instance.user_set.all()
kwargs['initial'] = initial
super(GroupAdminForm, self).__init__(*args, **kwargs)

def save(self, commit=True):
group = super(GroupAdminForm, self).save(commit=commit)

if commit:
group.user_set = self.cleaned_data['users']
else:
old_save_m2m = self.save_m2m
def new_save_m2m():
old_save_m2m()
group.user_set = self.cleaned_data['users']
self.save_m2m = new_save_m2m
return group

class MyGroupAdmin(GroupAdmin):
form = GroupAdminForm

admin.site.unregister(Group)
admin.site.register(Group, MyGroupAdmin)

初到北京,一切比想象中顺利,地铁40分上班,可以接受,折腾了一周,安顿下来,忙完比赛,也该简单写写感想了!

来北京本来就是一个艰难的决定,回想起来也没有想那么多,记得第一天上班老大很客气的教会我公司的基本细节,第一天把所有东西安顿好,手续办完,各种账号注册完就已经不早了。

老大说他是湖北人,挺亲切的!公司的氛围还不错,每天上下班,吃饭,休息,纯靠自觉,挺自由,感觉来北京的压力一下小了很多。

说起来,在北京第一次待这么久,说说对北京的感觉吧:

  1. 其实也没有被人说的那么夸张,那么不堪入目,大城市嘛,生活节奏快,人多,其实挺正常的,相信上海香港,也是一样繁忙!

  2. 人们常常担心的PM2.5的确很高,我嘛,天气不好不出门活动,不过这几天天气不错啊,也许是一直在下雨吧!

  3. 地铁。。。硬伤啊。。幸好,我每天不用挤高峰期,但是少数不幸了几次。。

还要在北京待几个月,也不知道会怎么样,自己照顾自己对我来说真的很轻松!但是希望爸爸的病快点好!别出什么意外了!

django中为大家提供便捷的图片上传功能,我的博客的图片类设计是这样的

1
2
3
4
class Image(models.Model):
UPLOAD_ROOT = '%Y/%m'
title = models.CharField(max_length=100, unique=True,verbose_name=u'名称')
image = models.ImageField(upload_to=UPLOAD_ROOT, verbose_name=u'图片')

挺简单的,但是这样传后,我也发现个问题,就是图片尺寸较大的,直接引用在页面中显示太大,而且速度也很慢,所以我想到生成缩略图,我的思路是再图片上传的时候进行缩略然后存储,有的人设计类的时候,设计原图和缩略同时存在的,原理也是类似,花了一个晚上,查了各种资料,还翻了下django源码,终于完成了。

首先看看怎么缩略图的生成,这里用的当然是PIL库了,缩略这样最基本的功能,实现起来不难,参考这篇文章然后将处理完的图片传递给django保存即可,这里保存过程找了很多方法,查看源码后找出一种比较美观简洁的方法。

代码如下,这里把image重命名为PImage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from django.db import models
from django.core.files.base import ContentFile
from PIL import Image as PImage
from cStringIO import StringIO

class Image(models.Model):
UPLOAD_ROOT = '%Y/%m'
title = models.CharField(max_length=100, unique=True, verbose_name=u'名称')
image = models.ImageField(upload_to=UPLOAD_ROOT, verbose_name=u'图片')

#自动缩略
def save(self, *args, **kwargs):
org_image = PImage.open(self.image)

if org_image.mode not in ('L', 'RGB'):
org_image = org_image.convert('RGB')

size=580
width, height = org_image.size
if width > size:
delta = width / size
height = int(height / delta)
org_image.thumbnail((size, height), PImage.ANTIALIAS)

#获取文件格式
split = self.image.name.rsplit('.',1)
format=split[1]
if format.upper()=='JPG':
format = 'JPEG'

# 将图片存入内存
temp_handle = StringIO()
org_image.save(temp_handle, format)
temp_handle.seek(0) # rewind the file

# 保存图像
self.image.save(self.image.name, ContentFile(temp_handle.getvalue()) , save=False)

super(Image, self).save(*args, **kwargs)

具体效果是这样的ubuntu

看到微信这么火,最近想尝试给自己的小站加个微信公众平台,不过结果估计也不会又多少人关注,纯当学习下django怎么调用别人的API把。

首先去微信公众平台的网站申请一个号,然后填写必要的信息,这么我就不罗列了,选择高级功能,打开开发者模式。然后会验证你的接口,这里我们再开始改造我们的django

新建app

这里我们执行

1
python2 manage.py startapp wechat

网站接入接口

微信公众平台验证接口,会发送GET请求到指定的URL,并带上四个参数,分别是signature微信加密签名,timestamp时间戳,nonce随机数,echostr随机字符串,通过检验signature来判断该请求是否来自微信服务器,这里会用到一个自己设参数token,相当与一个加密密钥,通过这个可以防止第三方伪造请求,如果判断成功就原样返回echostr那么就接入成功。

加密方式不复杂,文档里这样介绍的

加密/校验流程:

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

根据这个流程我们实现这样一个验证函数,通过python强大的标准库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def checkSignature(request):
signature=request.GET.get('signature',None)
timestamp=request.GET.get('timestamp',None)
nonce=request.GET.get('nonce',None)
echostr=request.GET.get('echostr',None)
#这里的token我放在setting,可以根据自己需求修改
token=WECHAT_TOKEN

tmplist=[token,timestamp,nonce]
tmplist.sort()
tmpstr="%s%s%s"%tuple(tmplist)
tmpstr=hashlib.sha1(tmpstr).hexdigest()
if tmpstr==signature:
return echostr
else:
return None

编写view

打开wechat中的views.py编写如下代码正确响应微信接入信号

1
2
3
4
5
6
7
8
9
10
11
12
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

import hashlib

@csrf_exempt
def index(request):
if request.method=='GET':
response=HttpResponse(checkSignature(request))
return response
else:
return HttpResponse('Hello World')

python就是这样,语句永远跟自然语言相近,基本语义按顺序读下来就好

配置url

我个人喜欢再app目录下单独管理每个app的url所以我再wechat目录下新建一个urls.py代码如下:

1
2
3
4
5
6
from django.conf.urls import patterns, include, url

urlpatterns=patterns('',
url(r'^$','wechat.views.index'),

)

然后在总urls里面添加

1
2
3
urlpatterns += patterns('',
url(r'^wechat/',include('wechat.urls')),
)

最后启用app并验证

再setting中启用wechat,尝试下在公众平台中验证下,地址就是你自己配置的URL,token填写自己设的token,应该很快可以验证通过,这样你就拥有了微信公众平台的开发权限了

一点想法

微信公众平台做的原来越好,看到很多优秀的微信应用,最近看36kr微信公众账号,感慨微信都支持自己添加菜单了,开放程度还是很高的,加上这种语音文字图像结合的交流方式,我感断言微信将会成为媲美微博的大平台,虽然现在自定义菜单还在内测相信要不了多久就能开放,下一步,准备简单添加几个功能,比如查看最新文章之类的!

编写代码的时候难免需要注释多行或者删除注视,这些在大部分IDE中都是一个快捷键的事,那么Vim中肯定也少不了,以前没怎么注意

批量注释

Ctrl+v进入块选模式,然后用光标选中你需要注释的行,然后用I在行首插入注释符比如//或者#,你会发现不对啊,也只有一行注释了啊,别急,然后esc你就会发现结果了

取消注视

同样的道理Ctrl+v选中行首的注释符,然后d就可以了

django.db.models是django中很重要的一层,处理着数据和对象的交换!在我看来,如果有SQL基础,学习这就很容易,因为基本上models就是将sql不同类型封装成了不同的类。

django入门后都知道models中有很多Field,其中很多Field为我们提供了便捷的功能,但是对于其中的各类Field掌握还不是很全,比如当时写博客问文章的Slug使用的是CharField后来还是小邪兽提醒我使用SlugField。今天通读了所有的Field就此也整理下

AutoField

其实质是一个InterField,随着记录的增长自动增加,但是这个Field不需要我们太留心,因为如果你的models中没有primary key的时候就会自动添加一个AutoField

BigIntegerField/SmallIntegerField

整型。。根据文档描述,BigIntegerField能表示-9223372036854775808到 9223372036854775807的数。

BooleanField / NullBooleanField

布尔型,表示true or false 如果你希望他能够空,那么你需要用NullBooleanField

CharField

字符串,没有什么好说的,比较常用,但是有一个必须赋值的参数max_length表示最大长度

CommaSeparatedIntegerField

逗号分开的整形?实质是CharField,不是很理解

DateField/DateTimeField/TimeField

日期和时间,类似python标准库中的datetime.date,其中有两个可选参数需要注意,auto_now保存时自动使用当前时间,一般作为最后修改时间,auto_now_add新建对象时自动使用当前时间,一般作为创建时间!

DecimalField

小数?英语不行,表示没看懂。

EmailField

Email,本质应该是CharField加个验证Email的正则

FileField / FilePathField

文件和文件路径,这块比较复杂,改天单独研究

FloatField

浮点数,这个一般会和DecimalField混淆,但是本质是不同的一个使用python的float类型,一个使用的是Decimal

ImageField

一个特殊的FileField,拥有两个额外的属性height_field,width_field

IPAddressField

ip地址,这个真是比较特殊,以后省去自己判断,其中注意protocol默认是both也就是都接受,也可以单独设成’IPv4’或者’IPv6’表示只接受IPV4或者IPV6

PositiveIntegerField/PositiveSmallIntegerField

非负整型

SlugField

这个我特别喜欢,用来表示缩略表示内容,一般用来生成URL

TextField

我个人认为这个和CharField类似,不同的仅仅是在form中这个需要TextArea,CharField仅仅需要TextInput

URLField

URL,一般存储URL是django1.5中新加入的