hookman 是基于github上的webhooks开发的一个用Python写的小程序
,基于TDD开发.
如果你想向项目贡献代码,请看hookman 开发blog

简单用法

  1. 安装 pip install hookman
  2. 配置github的webhooks,设置监听服务器ip和端口3610(默认)
  3. 使用
    3.1 进入github项目目录cd /my/github/projectdir
    3.2 运行 hookman --run -d
  4. 关闭
    3.1 hookman --stop

版本差异

v0.1.0

  1. 基本实现监听端口
  2. 添加 run,stop,daemon,pidfile,logfile,projectdir选项

v0.1.1

  • 修复了在前台shell模式下的bug

隔离测试是相对与于整合测试来说的,现代软件架构流行分层式、模块化,而隔离测试就是相当于在每层上进行测试,整合测试就是跨越多个层进行测试

introduction

举个简单例子来说,在django系列中,我们把表单提交分成两个层,一个form层,一个式model

form层相当于接近用户交互层,而model层与数据库联系更大, form层负责获取用户数据并验证,而model层根据form层数据将数据存入数据库。

隔离测试就是隔离form层向model层提交,而整合测试就是直接测试form层和model层。

判断一个测试是整合测试还是隔离测试就是看测试的边界,整合测试相当于我们更加熟悉,我们测试时通过伪造form提交,然后通过数据库获取存入数据来得到验证,而隔离测试就比较复杂,因为我们很难在一个耦合度高代码找到怎么隔离两个层的方法。

接下来我就介绍python里一个神器:mock

自己用过其他语言框架中的mock,但是python里面mock里面最神奇的的是里面的patch,就像一个超级补丁一样。

Example

接下来我用一个例子来介绍一下如何写隔离测试

首先我们在 lists.forms.py中一个表单model

class ListForm(forms.models.ModelForm):
    def save(self):
        pass

我们在lists.models.py有个model

class List(models.Model):
    def create_now():
        pass

现在来分析一下这个隔离测试,我们要测试ListFormsave方法

首先ListFormList两个类是耦合的,一个整合测试,我们只要调用save方法,然后查询数据库就可以完成这个测试,然而隔离测试不同,我们只能测试在调用save方法时,他“干”了什么。他可能调用了Listcreate_now方法,将得到表单数据传了过去。

意味着我们只能测试到save方法调用了List方法。

那这个隔离单元测试该怎么写?

接下来我们隆重介绍mock里面的最强补丁patch

根据TDD原则,我们先新建一个单元测试IsolutionFormTest

import unittest
from unittest.mock import patch, Mock      ##load super patch 
from lists.models import List
from lists.forms import ListForm


class IsolutionFormTest(unitest.TestCase):

       @patch('lists.forms.List.create_new')
       def test_save_creates_new_list(self, mock_list_create_new):
            form = ListForm(data={'text': "example text"})
            form.is_valid()    # get clean data
            form.save()

            mock_list_create_new.assert_called_once_with(
                text= "example text"
            )   # the major test

我来介绍一下这个patch,就像名字一样补丁,通过我们使用字符串将要替换的函数写出来,当test运行时,会自动将函数替换成一个mock对象,通过参数(上面的mock_list_create_new)赋给函数。

你可以这样想象,当form.save()调用时,在save函数里面,我们如果使用了lists.forms.List.create_new这个函数,这个函数就会被直接被补丁替换掉,你如果使用了lists.forms.List.create_new(text="xxx")就会变成mock_list_create_new(text="xxx"),当你调用了mock_list_create_new(text="xxx")时,mock_list_create_new这个mock对象就会记录下来。

这样我们就通过mock_list_create_new测试了函数是否执行了没有,因为隔离开form层和model层的就是通过两者之间的接口。我们只用测试接口是否执行了没有就可以了。

这样我们就完成了隔离单元测试,运行一下肯定失败,我们接下来就把save方法完善一下通过测试。

from django import form
form list.models import List

class ListForm(forms.models.ModelForm):
    def save(self):
        List.create_new(text=self.cleaned_data['text'])

ok测试通过了,我们就可以歇一口气了。

Conclusion

相对于整合测试,隔离测试运行速度更快,但是相对的隔离测试对接口要求非常严格,好的方面利用我们进行更好的代码设计,更好的分析代码的复杂程度,并且当接口变迁的时候,隔离测试能迅速发现变化而报警,然而隔离测试工作量比较大,而且没有整合测试那么好理解。在实际生产中对于复杂的接口我们尽量进行隔离测试,对于简单接口我们使用整合测试能根据减少程序的耦合性,而且能迅速发现集成问题。

ps:对patch感兴趣的童鞋可以自行google,patch好玩的地方还有很多,这里为了篇幅我只介绍了最核心的使用方法,大家可以自行探索patch更多好玩的东西。s

看了 << Python Web 开发 测试驱动方法>> 以后, 感觉自己找到自己的一些项目之所以不能够很好的 维护下去的原因.总结了一下自己的理解, 记录下来.

什么是 TDD

Test-Driven Development

TDD 是一种以测试为驱动开发的方法, 自己以前也听到过这个名词, 但是平常只是稍微写一点测试,并没有让测试领导开发节奏.

简单来说TDD是先写测试再写代码, 这于我们平时开发的时候有很大不同,比如我们写一个web页面, 我们一般是先写好代码,然后在浏览器上调试(相当于测试),然而测试驱动开发就不同了,我们一开始只写测试, 然后写代码让他通过我们的测试.

TDD的主题基本上很多人都知道,但是具体步骤和诀窍很多人不明就里.下面来详细介绍怎么让Test
引导你的开发.

怎么进行TDD

首先让我来看一看Test是什么, Test 分两种

  1. 功能测试
  2. 单元测试

功能测试

很多人知道单元测试却很少听到功能测试, 功能测试在开发过程中经常被人忽略,但是功能测试能很好的把握开发方向

功能测试就是从用户角度出发, 从用户的角度测试代码.

用web项目开发来打比方, 用户只能通过浏览器来浏览你的web, 所有的交互只能通过浏览器来实现,功能测试就是模拟用户进行浏览器上的操作,具体来说我们可用使用selenium来操控firefoxchrome,通过我们使用selenium提供的接口操作浏览器访问页面获取html, 来进行功能测试.

功能测试就是测试一个项目成品的功能, 在平常的项目开发中,这个测试往往是提到项目完成之后人工进行, 然而在TDD中这个却被提到了最前面,他成了一个风向标,所有的代码目的都是为了实现这个功能测试.

单元测试

单元测试这个很多人都很了解, python里面有unittest 这个单元测试框架.就是专门为单元测试而开发的.

单元测试历史悠久,很多人都写过单元测试,不过大部分人写单元测试都是在函数或者类完成之后写的.

在TDD中,单元测试进行最频繁的测试, 在功能测试完, 写每一个函数都提倡先写单元测试, 然后进行开发.

单元测试关注点与功能测试不同, 单元测试注重的是每一个函数执行的结果, 给定一个输入就一定要得到一个确定的输出,比功能测试他更关注底层代码,毕竟功能测试只关注用户最后得到结果,单元测试将你的代码函数形成一个单元,逐个运行,逐个测试.

前面介绍了一大堆概念,却没有落到实处, 功能测试很简单,我们评价一个项目,能很快的写出测试方法,但是对于单元测试来说,我到底该怎么进行,这同我平时的开发有什么不同,下面就详细的介绍单元测试的几个重要的要点.


留个坑慢慢填.

最近这几天在开发一个hmtl5的游戏, 但是对于js怎么使用面对对象来编程有点困惑,查了一些资料
整理如下.

js的this用法

  • 非对象属性函数(内部函数)

    var point = { 
     x : 0, 
     y : 0, 
    moveTo : function(x, y) { 
        // 内部函数
        var moveX = function(x) { 
    this.x = x;//this 绑定到了哪里?
        }; 
          // 内部函数
        var moveY = function(y) { 
            this.y = y;//this 绑定到了哪里?
         }; 
    
         moveX(x); 
       moveY(y); 
     } 
    }; 
    point.moveTo(1, 1); 
     point.x; //==>0 
     point.y; //==>0 
     x; //==>1 
    y; //==>1
    
  • 对象属性函数
var point = { 
        x : 0, 
        y : 0, 
        moveTo : function(x, y) { 
                this.x = this.x + x; 
                this.y = this.y + y; 
                             } 
         };

 point.x; // ==> 1
 point.y; // == >1 

 point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象
 point.x; // ==> 1
 point.y; // == >1 

其实this的用法还有很多,我为什么只列出上面两种是因为所有的this用法都可以归为这两类.

非对象属性就是这个函数不存在对象的内存空间里面的函数
对象属性就是说这个函数存在于对象内存空间的函数

这样说很绕, 抽象的来说, 把每个函数都看做是对象, 我们把这个对象point想象成一个上锁的柜子A, 每个变量名就是一把钥匙, 我们这个柜子A里面可用放很多钥匙,每个钥匙又对应着其他柜子,我们也可以放东西,但是这些东西只能是一些数字字符串什么的,这就对应着对象的这种继承性,

现在我们来看放在柜子A里面的钥匙,有一个钥匙可用开其他某一个的柜子B(相当于对象的属性的moveTo函数),当我们就在柜子B里面使用this时,这个this是什么呢.

这个this就要找一个柜子, 那为什么要找一个柜子(对象呢)

这就是js语言设计的松散, 当在非严格模式下, this 会被强制转换成一个对象, 对于例子一,因为内部函数 this 并没有给他赋值(你可以把他看做一个我们找不到他的钥匙的柜子), 所以this被强制转换成了全局的柜子(全局变量)

ps: 严格模式在函数或变量前加上 'use strict';

怎么解决这个问题了, 有两种方法, 不是没有赋值给this嘛, 我们可用call来给函数内部的this赋值,

moveX.call(this, x);   // 我们把当前函数的this赋给内部函数

但是这种方法不够优美, 我们还可以直接把 this用个变量that给内部函数用,

var that = this;
var moveY = function(y) { 
        that.y = y;//this 
 }; 

这样这个this 就不会被强制转换成全局变量了, 当然你可以开启严格模式, 这样this的话就会变成undefined, 你也不会因为你的代码问题而污染全部变量.

总结

js 的 this 是面对对象编程的一种体现, 但是js的this由于有点不严格,所以有时候会出现一些令人意向不到的结果,

引用

这段时间重新学习了web前端的技术栈,其实一开始加入社团时就是从前端开始入门的,等到了会仿写几个页面后就跳到了后端的坑,从html、css、js(只学了一点)又马不停蹄的转到.net平台、后来在老司机的带领下我们放弃了不开放的.net平台投入了python的怀抱

python的确很好,“内裤”很多,相对于.net 相对于封闭的生态圈, python对第三方的类库的依赖很大,pip 是程序的常客,看到一个好的类库就pip下来了。

学了不少程序语言,从静态语言到动态语言,这其中的转变刚开始的确让人很苦闷。

自己从c系的语言过来,刚接触C的时候对于“过程编程”有很深的体会,结果并不重要,重要的是过程,有时候为了写好一个完美的函数自己苦思冥想好几天,花在一个函数上的时间比项目的时间都多。

然而到了学习python, 自己把C系的学习习惯带到python , 在做项目的过程中,当自己写完一个函数的时候总在想怎么优化代码,怎么抽象化对象让事情简单,慢慢的走入一个误区,让我忘记了python的口号 life is short, I use python .

python是一门目的性很强的语言, 先让我实现功能,其他以后再说, 我以前使用的时候对过程强调的太多了, 一个函数我得测试测试很多遍,确定每个参数的作用还有影响, 花了太多时间, 适得其反,我的代码一点都不 pythonic

我在学习 python的过程中走了很多误区,总结起来有几点:

  • 太注重过程,不注重结果
  • 太注重功能丰富,不注重简单

在我看来, pythonic 就是用最简短清晰的代码最快的完成自己的目的

python和javascript

这个都是动态的脚本语言,javascript更倾向于脚本

两个家伙在我看来都是鸭式语言, 当然js更倾向函数式,而python更倾向于对象式.

python 是目的性强, javascrip 是表现力强

以前在后端的时候总觉得只要把自己那端的语言学好就行了, 任何事python都能用来解决, 无论是数据库还是服务器,就算是图像都能用python处理(使用PIL),虽然有着GIL,有这性能低下等等的问题 .
颇有一番”学好数理化走遍全天下都不怕”的念头,

然而在实际项目中慢慢发现, 计算我能用几百行python代码画出一朵花来,用js几句话就在canvas上弄出来了,而且很轻松就能换成其他的东西.

得益于node.js社区的火热, 现在javascript也能在后端大显身手了, 学习了一点node.js,感觉通过node.js对js的封装,让javascript变成了一门类python的语言, 原本js文件只能运行在浏览器里面,通过node.js将每个文件封装成模块,就像python将每个文件封装成package,通过模块与模块的合作,js也能像python那样简单的用几行代码就构成一个强壮的服务器(虽然说现在node.js有点不稳定,但是随着node社区的努力也能将node.js强壮起来)

总结

通过一个星期的重温js, 自己简单的涉猎了现在很火的Angular,Ionic.和Node.js,并简单的搭建 Electron + Cordova + Ionic + Angular 原来博客 的 Desktop端
(linux + windows + OS)的软件版,还有移动版(由于本人没有OS操作系统,只做了Android版),还花了半天时间搭建了这个基于hexo的博客,算是完成了全平台的搭建

接下来我会自己工作前的大学时光好好的培养自己解决问题的能力,希望能在工作前爱上并享受自己将要做的工作!

最近从python2转到python3,发现还是有一些不同,一些库改名字很好解决,但是这个import机制不了解原理是不好理解的.

python2是默认相对路径导入,python3是默认绝对路径导入

首先这个包的导入机制,就是你在一个module里面引用另一个module,python运行文件有两种方式,一种是直接以主文件运行(默认以这种方式运行,同下面一种有点区别),一种是以module形式运行,就是用python -m filename方式调用.

以module的方式运行

####### 什么是相对导入和绝对导入呢
相对导入是用一个.来声明的,相当于Unix上的选择当前文件夹.

假设你的文件目录为下面的

main
|    main.py
|     __init__py
|   momod+
|      | __init__.py
|      | pack.py
|       | flask
|      |    |  __init__.py
|       |    |  myflask.py
--------------------
python里面的module分三种,一种是build-in module(内建库),一种是第三方库,还有一种是你自己写的库(如上面的flask).

在python2里面,当你import 一个module时,搜索的顺序是 内建库,自己的库,第三方库.

####### 而在python3里面顺序为 内建库,第三方库,你自己的库.

我自己感觉python3的import的机制更为清晰,因为当你import一个库时,假如你写的库和第三方库重合时,你优先导入第三方库,如果你不适用声明相对导入的话,你无法正确的导入自己的库,而使用.来声明库来自自己的代码让代码的结构更加清晰了.所以如果你想让你的代码兼容py2和py3,你自己的库都要采用相对导入方法来导入.
比如在momod 里面的pack.py假如想引用flask的myflask.py要这样写

from .flask import myflask

########### 假如你在py2中写了 from flask import myflask(并且你安装了flask库),这个是可以成功运行但是在py3中就会报错,因为他会优先导入flask库假如你没有显示声明相对导入的话.


上面成立的前提是将pack.py以module方式运行,或者运行main.py在其中引入pack.py.接下来讲讲以主文件运行的不同.

当你直接使用

python pack.py

你假如在pack.py里面使用了这个

from .flask import myflask

引用了自己的myflaskmodule,在py2和py3中都会下面报这个错

SystemError: Parent module '' not loaded, cannot perform relative import

因为当你以主文件方式运行 pack.py ,python会吧pack.py重命名为__main__,所以用.相对路径也不会是当前文件.所以全部都只能用绝对引用.所以在主文件运行在python3里面有个问题,假如你自己的库与第三方库有重名.

python3默认绝对路径,自己的库不会优先于第三方库被扫描.有两个解决方法,把自己的库重命名,第二个方法就是把包含主文件的文件夹加上init.py,你可以在sys.path的路径里加上..或者具体上一个上一个文件夹的路径.怪绕口的,其实你只要python能找到你的上一个文件夹,就行.

说到这里顺便插一句对doctest和集成测试的理解.

由于我平时喜欢一边写代码一遍测试功能,图方便就使用doctest直接插在方法里面,在代码后面加上

if __name__ == '__main__':
        import doctest
        doctest.tesmod()

平时写小module的时候没有问题,在将python2转python3时候,出现问题,因为我要测试这个module时,会报上面的那个错,因为我要测试他的话必须将它作为主文件.查了资料知道,其实doctest虽然在当前页面代码测试,但是对于module的话,最好采用集成测试,一是module很多,假如一个一个运行很麻烦,二是有时候module必须多个一起测试,所以测试module时要用集成测试来取代doctest.

numpy简单来说就是python的C版数组实现,因为python原生列表虽然好使,但是生成大量数据时开销很大,而numpy是基于C的,生成大量数组非常简单,而且操作他们速度非常快.

由于numpy是基于C的,所以numpy是一种强类型的,当然numpy是可以判断数组里面是数据类型,但是我们可以显示声明他们,dtype是声明的参数,一般我们通过下面的方式简单声明一个narray

import numpy as np
arr = np.array([1, 2, 3], dtype=np.int32)

numpy还有一个强大的地方是多维数组,numpy对多维数组的支持很好.只要简单的使用嵌套序列就能被转化成多维数组.
比如
arr2 = np.array([[1, 2], [3, 4]])

numpy另一个强大的地方是矢量化,这对于科学计算来说非常有用

比如前面的arr,
我们可以简单使用

arr3 = arr * arr

得到另外一个序列 [1, 4, 9],数组间的运算应用到了元素级.

numpy之所以成为数据分析的基本数据结构,还在用取数据的灵活性

对于一维数组来说,python自身的列表就支持切片处理,numpy不仅支持切片处理,还支持列表取出,比如:

>>> num =  np.arange(10)
>>> num[[3, 1, 0]]
array([3, 1, 0])
在一维数组里面这个并没有什么优势,因为我们可以通过数据简单一个构造器[num[x] for x in [3, 1, 0]]构造出来.

当但在多维数组我们使用构造器非常繁琐了, numpy使用了很多技术使我们很方便的取出多维数组

我们先创建一个多维数组

arr = np.arange(32).reshape((8, 4))

生成的arr是

array([[ 0,  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]])

现在像一维数组一样的取出数据

In[13]: arr[[0, 3]]
Out[13]: 
array([[ 0,  1,  2,  3],
   [12, 13, 14, 15]])

我们取出了一个二维数组

试试用这个

In[14]: arr[arr > 8]
Out[14]: 
array([ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
   26, 27, 28, 29, 30, 31])

我们取出一个一维数组,这个arr > 8 是也是一个二维数组对应原来数组的每个位置都有一个布尔值代替,当numpy判断得到是一个array数组时会广播每个值来判断是否获取,这个可比你用for循环快了很多.

array([[False, False, False, False],
   [False, False, False, False],
   [False,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True]], dtype=bool)
numpy很善于处理不同的选择,当你直接给一个数组时,如上面的arr[[0, 3, 4]],它默认第二维为全部选择也就是想当与arr[[0, 3, 4], :],当你给确定的值时,他就会在第二维上取响应的值,比如
arr[[0, 3, 4], 1] 或 arr[[0, 3, 4], [1, 1, 1]]

从上面你可以看到,如果第二维你每个都想取第二个,你可以直接写一个整数就行,numpy会广播过去,假如想你分别再第一维的每个上分别对应取哪个你就可以用数组来分别选择.

有时候我们想在二维数组上面取出一个矩形块,直接使用 arr[[0, 3, 4], [1, 2, 3]]只能取出二维数组矩形块的对角线,我们这时候就可以先取出第一维的矩形列,然后再在取出的列中取出矩形行
arr[[0, 3, 4]][:, [1, 2, 3]]

当然我们还可以用二维数组来取出数据
arr[[[0], [3], [4]], [[1, 2, 3]]]

[[0], [3], [4]]代表第一维的1, 4, 5列, [[1, 2, 3]]代表第二维的2, 3, 4行.

numpy给我们提供一个函数将一维数组转换成二维数组我们可以简单使用
np.ix_([0, 3, 4], [1, 2, 3])

生成二维数组,这样我们使用

arr[np.ix_([0, 3, 4], [1, 2, 3])]

就可以取出矩形块了.

Pillow 是 PIL的对Python3支持的另外一个分支,当然他对Python2也兼容,由于PIL安装起来比较烦,而使用pip可以很轻松的安装Pillow,所以我选择Pillow使用,但是其核心还是PIL库的。


Python的图形处理库如PIL一直很强大,但是要想使用好它必须对图片有一定的知识储备。
使用起来很简单

from PIL import Image

引用Image包

im = Image.open('1.png')

打开图片,得到一个im对象,我们接下来就可以对这个对象进行操作(前提有这个1.png图片)

我们先看一下他的一些属性

>>> print im.format, im.size, im.mode
PNG (83, 81) RGB

第一个我们输出图片的格式,图片有很多种格式,常用的有jpg、png还有gif动图啊,PIL支持很多种格式,我们可以使用PIL轻松的将格式转换,im.save('1.jpg'),当然你可以选择格式假如你没选好后缀名的话,im.size就是图片大小,他返回的是一个元组第一个长度第二个是宽度,单位是像素。
现在就谈谈 这三个属性对应的关系吧
首先我们使用一张像素图来说吧

我们存贮图片的时候是将整个图像分成很多个相同的小方块,每个小方块我们称为像素,当然一张图片分的越小,像素越多,那么图片就越接近真实图片,上面的im.size属性就告诉我们,这张图片分成了,长为83px,宽为81px的图片,那么一共有83*81=6723个像素点,每个像素点里面存什么呢,这就是im.mode属性告诉我们的,贴一下属性有什么吧

  1. 1 (1-bit pixels, black and white, stored with one pixel per byte)
  2. L (8-bit pixels, black and white)
  3. P (8-bit pixels, mapped to any other mode using a color palette)
  4. RGB (3x8-bit pixels, true color)
  5. RGBA (4x8-bit pixels, true color with transparency mask)
  6. CMYK (4x8-bit pixels, color separation)
  7. YCbCr (3x8-bit pixels, color video format)
  8. I (32-bit signed integer pixels)
  9. F (32-bit floating point pixels)

像素存贮就是涉及到颜色的存贮,在早期的黑白游戏机,只有黑和白两种,那么每个像素点就只有1位颜色来存贮,1位只能存贮两种颜色,八位色就能存256种颜色,像八位我们能用256个油漆桶/256色调色板来形容,像上面我们使用的RGB是由三种三原色红绿蓝混合而成,我们知道大自然所有的颜色都可以用红绿蓝三种颜色调配出来,所以RGB又被称为真彩(true color),每种颜色我们都分成256种,所以我们一共有256256256=16777216种颜色可以调配,像素的其他模式我们不介绍太多,有兴趣的可以自己钻研。
那么我们知道每个像素占多少字节,又知道共有多少个像素,那我们是不是就可以直接计算出来图片大小,来验证一下

以第一张图片为例,共有8381=6723个像素点,用RGB模式,每个像素三个字节,共有67233=20667b=20kb,但是我这张图片只有11.6kb,误差太大了吧,这时候我们就要介绍一下上面那个im.format属性了,这张图片采用png格式,我们先尝试一下把他转成JPG格式吧

im.save('1.jpg')

我们再查看一下这个1.jpg的大小,只有2.24kb了,我们用PIL打开这张图片

>>> im2 = Image.open('1.jpg')
>>> print im2.format, im2.size, im2.mode 
JPEG (83, 81) RGB

图片大小没有改变,但是format变成了JPEG,而且文件大小变成原来的1/5,
JPEG和GIF和PNG是三种图片压缩技术,他们使用压缩算法把图片压缩成很小,当我们打开图片时,解密算法把他还原出来,所以我们算出来的大小与压缩后的大小是不一样的。
有了这些概念我们就能更好的使用PIL提供给我们的magic方法,下次在谈我对PIL的高级应用吧。


由于网站要实现图片和文字的混排的上传
所以在网上找了富文本编辑器的插件,发现CKEditor这款还不错的插件

我用的是4.5.1这个版本,的确很好用,只是引用了一个js文件就可以实现。


  1. 将网上下的CKEditor包解压放在根目录下
  2. 在页面上引用CKEditor的核心包ckeditor.js
<script src="ckeditor/ckeditor.js" type="text/javascript"></script>;
  1. 在页面添加一个输入框textarea

    <textarea name="individual" id="individual" runat="server"></textarea>
    <script type="text/javascript">
     CKEDITOR.replace('individual');
     </script>
    

只要通过上面上面几个步骤就能实现富文本编辑器,但是点开图片上传功能,发现只有上传url的功能,并不能本地上传图片,百度了一下发现由于安全性问题CKEditor没有上传功能,只有安上CKFinder才能实现上传功能,于是我在官网下了ckfinder__aspnet_2.5.0.1,同CKEditor一样引用JS文件(只要引用ckfinder.js)

<script src="ckfinder/ckfinder.js" type="text/javascript"></script>
  • 由于我是MVC的网站基于.net4.5,在官方给的包里面有一个asp.net网站实例放在_source文件夹里面,里面有一个基于.net2的示例网站,一运行就报找不到 System.Web.UI.Design这个命名空间的错,所以我把它从项目中排除,把项目中bin中debug文件夹下的CKFinder.dll复制出来,引用到我自己的项目中。

    接下来要配置CKEditor来让CKFinder引用进来,在CKEditor文件夹下config.js在CKEDITOR.editorConfig = function (config) {};方法中添加如下代码:

    config.filebrowserImageBrowseUrl = ‘ckfinder/ckfinder.html?Type=Images’;
    config.filebrowserFlashBrowseUrl = ‘ckfinder/ckfinder.html?Type=Flash’;
    config.filebrowserUploadUrl = 'ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Files';
    
    config.filebrowserImageUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images’;
    config.filebrowserFlashUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash’;
    config.filebrowserWindowWidth = ‘800’; //“浏览服务器”弹出框的size设置
    config.filebrowserWindowHeight = ‘500’;

注意在配置Url的时候要修改成相对于网站本地网站磁盘文件路径,比如说,你的网址是http://example.com.cn ,你把ckfinder文件夹放在Admin下的Editor文件夹,那么所有url要改成下面类似的格式

config.filebrowserImageBrowseUrl ='/Admin/Editor/ckfinder/ckfinder.html?Type=Images';

否则会报404错误,

最后一步是修改一个函数让所有人能看到服务器上传文件夹里面的文件,在ckfinder文件夹下面的cofig.ascx文件,找到 CheckAuthentication函数将返回值改成true

当然如果你想修改上传文件的地址,你可以在上面方法里面找到SetConfig()方法,找到BaseUrl,修改为你想上传的地址,

—–

PS:

我是在在VS里面进行调试的,由于VS的IIS在调试的时候不允许对磁盘文件的路由地址访问,就是CKFinder通过ckfinder.html这个html来实现上传图片的功能,但是这个在调试的时候VS无法访问这个文件,所以一直报404错误,可以修改IIS来允许IIS访问磁盘文件,步骤如下:

  1. 右键点击IIS Express,选择显示所有应用程序
  2. 找到运行网站的配置,进入applicationhost.config文件夹
  3. ctrl+f 寻找UrlRoutingModule
  4. 将preCodition设置为空字符

    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
    

如果你是用MVC进行表单传值的话,你必须在post方法上面添加 [ValidateInput(false)] 属性,如果不这样的话就会报下面的错

“/”应用程序中的服务器错误。
从客户端(content="<p>sdfsdafwewo shdfh...")中检测到有潜在危险的 Request.Form 值。


string

原来的很多函数都逐渐迁移到strunicode对象上去了,
不过有两个函数没有迁移出去


第一个是capwords

个人觉得没什么卵用
就是将英文单词首字母大写

比如

string.capwords('this are some words')

输出为 This Are Some Words


第二个我觉得挺有趣,他可以帮你把对应的文字换成你设定的

我们可以用它来设计出莫斯密码’=’.

"""create a table"""
table = string.maketrans('abc','123')
print 'abc123'.translate(table)

输出为123123成功把abc转成了123


string还有一个模板类型Template
这个类型同我们转义差不多(%),不过能够实现的
更加自主化
我们可以继承这个类来修改模板类的具体实现
而且这个模板类有一个


textwrap

由于sublime输出一个很长的字符串很卡,这个格式化字符串的类能够帮我们
解决很多问题

我一般去除整体缩进然后去填充字符串

dedented_text = textwrap.dedent('...a long word...')
print textwrap.fill(dedented_text, width=50)