<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://me.iblogc.com</id>
    <title>iblogc</title>
    <updated>2026-03-04T03:15:16.798Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://me.iblogc.com"/>
    <link rel="self" href="https://me.iblogc.com/atom.xml"/>
    <subtitle>Just do it~</subtitle>
    <logo>https://me.iblogc.com/images/avatar.png</logo>
    <icon>https://me.iblogc.com/favicon.ico</icon>
    <rights>All rights reserved 2026, iblogc</rights>
    <entry>
        <title type="html"><![CDATA[🌟 超值流量卡，享受无忧通信 🌟]]></title>
        <id>https://me.iblogc.com/post/liuliangka/</id>
        <link href="https://me.iblogc.com/post/liuliangka/">
        </link>
        <updated>2024-07-25T05:38:24.000Z</updated>
        <content type="html"><![CDATA[<p>在如今的数字化时代，流量卡已成为不可或缺的通信工具。我们的流量卡不仅价格便宜，还提供多重优惠，为用户带来全新的通信体验。</p>
<p><strong>💰 1. 价格亲民</strong><br>
我们深知用户对成本的敏感性，因此特别推出经济实惠的流量卡。无论是学生党还是上班族，都能轻松负担。</p>
<p><strong>📞 2. 免费通话时长</strong><br>
不仅仅是流量大，我们的流量卡还包含了免费通话时长。无论是与家人朋友畅聊，还是处理工作事务，都能享受通话自由。</p>
<p><strong>📶 3. 海量流量</strong><br>
无论是视频播放、在线游戏还是社交媒体浏览，我们的流量卡都能满足你的大流量需求。再也不用担心流量不足的问题，尽情享受互联网的乐趣。</p>
<p><strong>🌐 4. 全网覆盖</strong><br>
我们的流量卡覆盖中国电信、中国移动、中国联通和中国广电四大运营商，信号稳定，覆盖广泛。无论你身处城市还是乡村，都能畅享高速网络。</p>
<p><a href="https://hk.usim.vip/s/y4aPWqSp">购买入口1</a><br>
<a href="https://172.lot-ml.com/ProductEn/Index/6f415ba9f55dd55e">购买入口2</a></p>
<p>下单后+vx：vwo618，返 20-30 元<br>
<img src="https://me.iblogc.com/post-images/1721888459478.jpg" alt="" loading="lazy"></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[解决 NPM Nuxt 3 项目初始化报错 ERROR  Error: Failed to download template from registry]]></title>
        <id>https://me.iblogc.com/post/jie-jue-npm-nuxt-3-xiang-mu-chu-shi-hua-bao-cuo-error-error-failed-to-download-template-from-registry/</id>
        <link href="https://me.iblogc.com/post/jie-jue-npm-nuxt-3-xiang-mu-chu-shi-hua-bao-cuo-error-error-failed-to-download-template-from-registry/">
        </link>
        <updated>2024-06-07T17:48:14.000Z</updated>
        <content type="html"><![CDATA[<p>npx nuxi@latest init demo 初始化 nuxt 3 项目时报以下错误</p>
<pre><code class="language-shell">[01:29:58]  ERROR  Error: Failed to download template from registry: Failed to download https://raw.githubusercontent.com/nuxt/starter/templates/templates/v3.json: TypeError: fetch failed
</code></pre>
<h3 id="确认问题">确认问题</h3>
<p>执行下面命令确认问题：</p>
<pre><code class="language-shell">node -e &quot;require('https').get('https://raw.githubusercontent.com/nuxt/starter/templates/templates/v3.json')&quot;
</code></pre>
<h3 id="问题一及解决方案">问题一及解决方案</h3>
<p>如果报以下错，则是网络问题，可以使用改host、改DNS、开代理其中一种解决问题</p>
<pre><code class="language-shell">node:events:491
      throw er; // Unhandled 'error' event
      ^

Error: connect ECONNREFUSED 0.0.0.0:443
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1247:16)
Emitted 'error' event on ClientRequest instance at:
    at TLSSocket.socketErrorListener (node:_http_client:481:9)
    at TLSSocket.emit (node:events:513:28)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  errno: -61,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '0.0.0.0',
  port: 443
}
</code></pre>
<p>解决方案一：改host<br>
打开 https://sites.ipaddress.com/raw.githubusercontent.com/ 获取 raw.githubusercontent.com 的 ip，修改host</p>
<pre><code>185.199.108.133 raw.githuusercontent.com
185.199.109.133 raw.githuusercontent.com
185.199.110.133 raw.githuusercontent.com
185.199.111.133 raw.githuusercontent.com
</code></pre>
<p>解决方案二：改DNS<br>
切换为 Google DNS 8.8.8.8 8.8.4.4</p>
<p>解决方案三：开全局代理/shell临时代理（自行google）</p>
<h3 id="问题二及解决方案">问题二及解决方案</h3>
<p>如果报以下错，则是证书问题，可以使用改host、改DNS、开代理其中一种解决问题</p>
<pre><code class="language-shell">node:events:497
      throw er; // Unhandled 'error' event
      ^

Error: unable to verify the first certificate
    at TLSSocket.onConnectSecure (node:_tls_wrap:1674:34)
    at TLSSocket.emit (node:events:519:28)
    at TLSSocket._finishInit (node:_tls_wrap:1085:8)
    at ssl.onhandshakedone (node:_tls_wrap:871:12)
Emitted 'error' event on ClientRequest instance at:
    at TLSSocket.socketErrorListener (node:_http_client:500:9)
    at TLSSocket.emit (node:events:519:28)
    at emitErrorNT (node:internal/streams/destroy:169:8)
    at emitErrorCloseNT (node:internal/streams/destroy:128:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
}```

解决方案一（*我用此该当未解决问题*）：
```shell
npm config set strict-ssl false
</code></pre>
<p>解决方案二（我的解决方案）：<br>
设置环境/临时shell变量</p>
<p>Windows CMD</p>
<pre><code class="language-shell">set NODE_TLS_REJECT_UNAUTHORIZED=0
</code></pre>
<p>Windows PowerShell</p>
<pre><code class="language-shell">$env:NODE_TLS_REJECT_UNAUTHORIZED = &quot;0&quot;
</code></pre>
<p>其它系统自行google</p>
<p>这样之后再执行 npx nuxi@latest init demo 命令就成功了，只是会出现NODE_TLS_REJECT_UNAUTHORIZED 设置为 0 不安全的警告，不用管它</p>
<p>参考资料：</p>
<ul>
<li><a href="https://github.com/nuxt/cli/issues/159">https://github.com/nuxt/cli/issues/159</a></li>
<li><a href="https://stackoverflow.com/questions/20747817/error-unable-to-verify-leaf-signature-phonegap-installation">https://stackoverflow.com/questions/20747817/error-unable-to-verify-leaf-signature-phonegap-installation</a></li>
<li><a href="https://github.com/nuxt/framework/pull/7271">https://github.com/nuxt/framework/pull/7271</a></li>
</ul>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[七牛云文件免费删除脚本]]></title>
        <id>https://me.iblogc.com/post/qi-niu-yun-wen-jian-mian-fei-shan-chu-jiao-ben/</id>
        <link href="https://me.iblogc.com/post/qi-niu-yun-wen-jian-mian-fei-shan-chu-jiao-ben/">
        </link>
        <updated>2023-04-04T04:26:12.000Z</updated>
        <content type="html"><![CDATA[<p>在清理七牛云文件时发现，七牛云网址上删除文件夹操作竟然是在网页前面轮询一个一个删除的，但删除除了控制台网络请求里不断有请求，网页上没有任务反馈，也不知道删除多少什么时候删除完成，所以就写了一个脚本来模拟网页上的请求，这个删除请求不会计算在账户的DELETE请求数里，所以不会有单独计费，如果单次删除操作量大的，可以使用这个方式来节省费用（官网写的PUT/DELETE每月免费次数为10万）</p>
<pre><code class="language-python">import requests
import base64
import traceback

payload = {}
# 填入自己的 cookies
headers = {
    'authority':
        'portal.qiniu.com',
    'dnt':
        '1',
    'x-reqid':
        'abc',
    'Cookie':
        'xxx',
    'User-Agent':
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.62'
}

base_url = 'https://portal.qiniu.com'
# 要删除的文件所在的 bucket
bucket = 'hmp-public'
# 要删除的文件目录
prefix = 'sandbox3/channelSetMeal/img/'
query_url = '{}/api/kodov2/rsf/list?allversion=false&amp;bucket={}&amp;prefix={}&amp;limit=50'.format(base_url, bucket, prefix)
delete_url = '{}/api/proxy/rs/delete/'.format(base_url)

has_next = True
del_file_size_byte = 0
del_file_count = 0
except_msgs = []
while has_next:
    try:
        query_res = requests.request(&quot;GET&quot;, query_url, headers=headers, data=payload)
        if query_res.status_code != 200:
            break
        items = query_res.json()['items']
        has_next = True if len(items) else False
        if not has_next:
            break
        for item in items:
            del_file_size_byte += item['fsize']
            del_file_count += 1
            sid = 'hmp-public:' + item['key']
            # 对id进行base64编码
            id = base64.b64encode(sid.encode('utf-8')).decode('utf-8')
            del_res = requests.request(&quot;POST&quot;, delete_url + id, headers=headers, data=payload)
            print('删除', sid, '结果', del_res.status_code, '，第', del_file_count, '个，',
                  '%.2f' % (del_file_size_byte / 1024 / 1024), 'M')
    except Exception as e:
        # 获取异常堆栈信息
        except_msg = traceback.format_exc()
        except_msgs.append(except_msg)
        print(except_msg)
print('===删除', bucket, prefix, '下的文件', del_file_count, '个, 总计', '%.2f' % (del_file_size_byte / 1024 / 1024), 'M===')
print('异常信息', except_msgs)
</code></pre>
<p>单线程太慢，写了个多线程的，但自己用的时候注意控制下速率，操作太频繁会导致<code>/api/kodov2/rsf/list</code> 返回空items</p>
<pre><code class="language-python">import requests
import base64
import traceback
import threading
import time

# 请用网页端的登录信息更新headers
headers = {
    'authority':
        'portal.qiniu.com',
    'dnt':
        '1',
    'x-reqid':
        'abc',
    'Cookie':
        'xxx',
    'User-Agent':
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.62'
}

# 要删除的文件所在的 bucket
bucket = 'hmp-public'
# 要删除的文件目录
prefix = 'sandbox3/channelSetMeal/img/'

base_url = 'https://portal.qiniu.com'
query_url = '{}/api/kodov2/rsf/list?allversion=false&amp;bucket={}&amp;prefix={}&amp;limit=50'.format(base_url, bucket, prefix)
delete_url = '{}/api/proxy/rs/delete/'.format(base_url)

del_file_size_byte = 0
del_file_count = 0
except_msgs = []


def query():
    query_res = requests.request(&quot;GET&quot;, query_url, headers=headers)
    return query_res


def del_file(item):
    global del_file_size_byte
    global del_file_count
    del_file_size_byte += item['fsize']
    del_file_count += 1
    total_size = '%.2f' % (del_file_size_byte / 1024 / 1024)
    total_count = del_file_count
    sid = 'hmp-public:' + item['key']
    # 对sid进行base64编码
    id = base64.b64encode(sid.encode('utf-8')).decode('utf-8')
    del_res = requests.request(&quot;POST&quot;, delete_url + id, headers=headers)
    print('删除', sid, '结果', del_res.status_code, '，第', total_count, '个，', total_size, 'M')


def main():
    has_next = True
    while has_next:
        try:
            query_res = query()
            if query_res.status_code != 200:
                break
            items = query_res.json()['items']
            has_next = True if len(items) else False
            if not has_next:
                break
            threads = []
            for item in items:
                t = threading.Thread(target=del_file, args=(item,))
                threads.append(t)
                time.sleep(0.1)
                t.start()
            for t in threads:
                t.join()
        except Exception as e:
            # 获取异常堆栈信息
            except_msg = traceback.format_exc()
            except_msgs.append(except_msg)
            print(e)
            print(except_msg)
    print('===删除', bucket, prefix, '下的文件', del_file_count, '个, 总计', '%.2f' % (del_file_size_byte / 1024 / 1024), 'M===')
    print('异常信息', except_msgs)


if __name__ == '__main__':
    main()
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ 不间断空格 C2 A0 - Non-breaking space]]></title>
        <id>https://me.iblogc.com/post/bu-jian-duan-kong-ge-c2-a0-non-breaking-space/</id>
        <link href="https://me.iblogc.com/post/bu-jian-duan-kong-ge-c2-a0-non-breaking-space/">
        </link>
        <updated>2022-11-16T08:18:27.000Z</updated>
        <content type="html"><![CDATA[<figure data-type="image" tabindex="1"><img src="https://me.iblogc.com/post-images/1668589983391.png" alt="" loading="lazy"></figure>
<blockquote>
<p>背景：用户反馈了一个问题，短信里的链接点不开，排查后发现在部分手机上会偶现链接后面带空格了还会把后面内容当成链接内容的情况，因为我们放到短信里的都是我们自己的短链，所以不存在繁杂的请求路径的情况，所以就想一个服务端的解决方案，在接收到请求后把路径用空格分割下，只取第一段内容作为路径，就是把 <code>xxxx.com/1ERXTa 确认您……</code> 请求过来的路径参数 <code>1ERXTa 确认您……</code> 处理成 <code>1ERXTa</code>, 写好代码自己编了一个测试数据测试通过，用用户短信里的链接数据测试失败，后面通过浏览器地址栏查看发现，自己编的测试数据空格经过 url 编码后是 <code>%20</code>, 用户短信里的链接点击打开后空格经过 url 编码后是 <code>% C2% A0</code>，两个空格竟然不一样。</p>
</blockquote>
<p>经过一系列的测试，发现两个空格的确有区别</p>
<pre><code class="language-shell">Python 3.10.0 (tags/v3.10.0:b494f59, Oct  4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)] on win32
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; import urllib.parse
&gt;&gt;&gt;
&gt;&gt;&gt; s1 = ' '
&gt;&gt;&gt; s2 = ' '
&gt;&gt;&gt;
&gt;&gt;&gt; s1.encode (encoding=&quot;utf-8&quot;)
b'\xc2\xa0'
&gt;&gt;&gt; s2.encode (encoding=&quot;utf-8&quot;)
b' '
&gt;&gt;&gt;
&gt;&gt;&gt; urllib.parse.quote (s1)
'% C2% A0'
&gt;&gt;&gt; urllib.parse.quote (s2)
'%20'
</code></pre>
<figure data-type="image" tabindex="2"><img src="https://me.iblogc.com/post-images/1668586995279.png" alt="" loading="lazy"></figure>
<p>根据关键词 Google 了下，原来有一个叫 <a href="https://en.wikipedia.org/wiki/Non-breaking_space#cite_note-1">不间断空格</a> 的东西。它有两个作用，一是阻止两端的内容被自动换行分开，二是防止连续的空格字符被合并成单个空格。我也数据测试了下这两种现象，表现的确有差异。</p>
<p>阻止两端的内容被自动换行分开（在网页中可以用 <code>&amp;nbsp;</code> 来表示不间断空格）：<br>
下面这段文字是用 <code>&amp;nbsp;</code>（不间断空格）拼接<br>
这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 </p>
<p>下面这段文字里是用 <code> </code>（空格）字符拼接<br>
这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本 这 是 测 试 文 本</p>
<p>防止连续的空格字符被合并成单个空格：<br>
下面尖括号中间有 10 个 <code>&amp;nbsp;</code>（不间断空格）<br>
&lt;          &gt;<br>
下面尖括号中间有 10 个 <code> </code>（空格）<br>
&lt;          &gt;</p>
<p>更加详细的说明可以看：<a href="https://www.cnblogs.com/lingyejun/p/13056754.html">https://www.cnblogs.com/lingyejun/p/13056754.html</a></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[代码托管平台码云(Gitee)到 Gitea 迁移记]]></title>
        <id>https://me.iblogc.com/post/Gitee2Gitea/</id>
        <link href="https://me.iblogc.com/post/Gitee2Gitea/">
        </link>
        <updated>2020-03-01T11:42:34.000Z</updated>
        <summary type="html"><![CDATA[<p>团队的代码托管管理平台之前一直用Gitee的企业版本，但除了代码pull/push操作外，基本不用平台上其它功能，除了要新建一个仓库要打开下网页版，其它时间基本不会访问网页版本，所以经过半天的调研，从GitLab/Gogs/Gitea中选择了Gitea，把迁移过程记录如下。</p>
]]></summary>
        <content type="html"><![CDATA[<p>团队的代码托管管理平台之前一直用Gitee的企业版本，但除了代码pull/push操作外，基本不用平台上其它功能，除了要新建一个仓库要打开下网页版，其它时间基本不会访问网页版本，所以经过半天的调研，从GitLab/Gogs/Gitea中选择了Gitea，把迁移过程记录如下。</p>
<!--more-->
<h3 id="安装gitea">安装Gitea</h3>
<p>因为服务器上刚好装有docker，按照<a href="https://docs.gitea.io/zh-cn/install-with-docker/">官方文档</a>选择了最简单的docker安装。</p>
<pre><code class="language-shell">docker pull gitea/gitea:latest
sudo mkdir -p /data/gitea
docker run -d --name=gitea -p 10022:22 -p 10080:3000 -v /data/gitea:/data gitea/gitea:latest
// 重启gitea
docker restart gitea
</code></pre>
<p>安装完成后遇到了页面有三个静态文件（css/js）加载不成功，导致页面排版混乱，F12查看控制台报错net::ERR_CONTENT_LENGTH_MISMATCH，google之，找到这篇文章</p>
<p><a href="https://github.com/xhlwill/blog/issues/17">Nginx 做代理时浏览器报错 net::ERR_CONTENT_LENGTH_MISMATCH</a>，按照此方法解决。</p>
<h3 id="配置nginx">配置Nginx</h3>
<p>在服务器Nginx上配置反向代理</p>
<p>vi /etc/nginx/conf.d/gitea.conf</p>
<pre><code class="language-ini">upstream gitea {
    server 127.0.0.1:10080;
    keepalive 2000;
}
server {
    listen       80;
    server_name  git.i.example.com;
    client_max_body_size 1024M;

    location / {
        proxy_pass http://gitea/;
        proxy_set_header Host $host:$server_port;
    }
}
</code></pre>
<p>重新加载配置</p>
<pre><code class="language-shell">sudo nginx -s reload
</code></pre>
<h3 id="域名解析">域名解析</h3>
<p>git.i.example.com解析到当前服务器ip，并把服务器防火墙入方向的10022 tcp端口打开，以便使用ssh方式clone仓库时使用。</p>
<h3 id="gitea初始化">Gitea初始化</h3>
<p>打开http://.i.example.com，进入初始化界面（如果没进随便点注册或登录就会进），除了数据库根据需要配置，几个域名和网址要修改下，邮箱和其它选项按需配置。以后如果想修改配置，可以直接修改/data/gitea/gitea/conf/app.ini文件<a href="https://docs.gitea.io/zh-cn/config-cheat-sheet/">配置说明</a>，修改完成后重启下gitea即可生效。</p>
<h3 id="仓库迁移">仓库迁移</h3>
<p>因为我迁移的是团队项目，所以先通过Gitea提供的API把所有仓库以镜像方式（镜像方式同步过来仓库对成员为只读，并且可以设置间隔时间，默认8小时，定时从原始地址Gitee同步最新代码）同步过来**[操作1]<strong>，然后为每个项目配置好协作者/团队/权限等设置，在这期间，团队成员还是往Gitee上提交代码，待全部设置完成后取消告知团队成员不要往Gitee提交代码，并调用Giea api把所有仓库从Gitee上同步一下最新代码</strong>[操作2]<strong>，然后每个仓库从镜像仓库转为普通仓库，并让团队的所有在自己仓库根目录执行修改本地仓库Git远程仓库地址替换操作</strong>[操作3]**</p>
<p><strong>[操作1]</strong>：登录Gitea后，界面右上角有一个加号，点开了后有一个迁移外部仓库的功能，只要填入外部仓库URL，授权验证信息等信息就可以一键把外部仓库的所有代码（包括所有branch和commit）迁移到Gitea，如果要迁移的仓库比较多，可以使用Gitea提供的Api来操作。对应此迁移操作的api是</p>
<pre><code>POST /repos/migrate?access_token=&lt;your gitea admin access token&gt;

Request body
{
    description: MigrateRepoForm form for migrating repository
    auth_password: string
    auth_username: string
    clone_addr*: string
    description: string
    issues: boolean
    labels: boolean
    milestones: boolean
    mirror: boolean
    private: boolean
    pull_requests: boolean
    releases: boolean
    repo_name*: string
    uid*: integer($int64)
    wiki: boolean
}
</code></pre>
<p><em><strong>注：</strong></em></p>
<ol>
<li>
<p>access_token 请在有管理员权限的账号的设置&gt;应用中创建；</p>
</li>
<li>
<p>Request body 中的uid即管理后台&gt;账户管理/组织管理中的ID列值；</p>
</li>
</ol>
<p>找了Gitee没找到可以获取账户下所有仓库信息的API，所以只好手写了一个Gitee仓库地址的文件，类似</p>
<p>vi gitee-url.txt</p>
<pre><code>https://gitee.com/example/project_a.git
https://gitee.com/example/project_b.git
</code></pre>
<p>使用shell脚本逐行读取url，并调用Gitea api迁移仓库。</p>
<pre><code class="language-shell">#!/bin/bash

for line in $(&lt;gitee-url.txt);
do
		# Windows注释下面这行
    line=$(echo $line | sed -e 's/\r//g');
    tmp=${line#https://gitee.com/xxx/};
    project_name=${tmp%.git};
    curl -X POST &quot;http://git.i.example.com/api/v1/repos/migrate?access_token=&lt;your gitea admin access token&gt;&quot; -H &quot;accept: application/json&quot; -H &quot;Content-Type: application/json&quot; -d &quot;{ \&quot;auth_password\&quot;: \&quot;NDY2&amp;F*K!hL75y*z\&quot;, \&quot;auth_username\&quot;: \&quot;korvin101@gmail.com\&quot;, \&quot;clone_addr\&quot;: \&quot;$line\&quot;, \&quot;issues\&quot;: true, \&quot;labels\&quot;: true, \&quot;milestones\&quot;: true, \&quot;mirror\&quot;: true, \&quot;private\&quot;: true, \&quot;pull_requests\&quot;: true, \&quot;releases\&quot;: true, \&quot;repo_name\&quot;: \&quot;$project_name\&quot;, \&quot;uid\&quot;: 2, \&quot;wiki\&quot;: true}&quot;;
done
</code></pre>
<p><strong>[操作2]</strong>：从Gitee上同步最新代码</p>
<pre><code class="language-shell">for line in $(&lt;gitee-url.txt);
do
    line=$(echo $line | sed -e 's/\r//g');
    tmp=${line#https://gitee.com/xxx/};
    project_name=${tmp%.git};
    curl -X POST &quot;http://git.i.example.com/api/v1/repos/{owner}/$project_name/mirror-sync?access_token=&lt;your gitea admin access token&gt;&quot; -H &quot;accept: application/json&quot;
done
</code></pre>
<p>***注：***owner为项目拥有者用户名/组织名</p>
<p><strong>[操作3]</strong>：原本地仓库Git远程仓库地址替换</p>
<pre><code class="language-shell">// http地址
// 原代码仓库http地址：https://gitee.com/example/project_a.git
// 新代码仓库http地址：http://git.i.example.com/JIANSU/project_a.git
// https://gitee.com/example &gt; http://git.i.example.com/JIANSU
// 本地仓库使用此命令替换，可在包含所有项目的外层文件夹路径下执行批量替换
// Windows删除'.bak'
sed -i '.bak' 's/https:\/\/gitee\.com\/example/http:\/\/git\.i\.example.com\/JIANSU/g' */.git/config

// ssh地址
// 原代码仓库ssh地址：git@gitee.com:example/project_a.git
// 新代码仓库地址：ssh://git@git.i.example.com:10022/JIANSU/project_a.git
// git@gitee.com:example &gt; ssh://git@git.i.example.com:10022/JIANSU
// 本地仓库使用此命令替换，可在包含所有项目的外层文件夹路径下执行批量替换
// Windows删除'.bak'
sed -i '.bak' 's/git@gitee\.com:example/ssh:\/\/git@git\.i\.example\.com:10022\/JIANSU/g' */.git/config
</code></pre>
<ol>
<li>
<p>如果之前是用http地址进行克隆的仓库的话，现在就是在进行pull和push操作时，把账户密码换成Gitea的就可以了；</p>
</li>
<li>
<p>如果以前是用ssh克隆的仓库的话，现在在Gitea的设置&gt;SSH / GPG 密钥里添加一下公钥就可以进行git pull/git push等操作了；</p>
</li>
</ol>
<h3 id="仓库备份">仓库备份</h3>
<p>Gitea有自己的备份与恢复功能<a href="https://docs.gitea.io/zh-cn/backup-and-restore/#%E5%A4%87%E4%BB%BD%E4%B8%8E%E6%81%A2%E5%A4%8D">备份与恢复</a>，这个备份比较全面，数据/代码/日志都可以备份，正是因为这样，如果仓库比较多这个备份的文件肯定会有点大，而且每次都是全量备份，所以频率肯定不能太高，而我只是想对仓库代码做一个高频率备份，所以写了一个Python3脚本调用Gitea api和 Git命令来进行所有仓库的所有分支代码备份，因为这个备份基于Git机制，所以虽然频率高，但备份始终只有一份。脚本如下：</p>
<p>backup.py</p>
<blockquote>
<p>如果使用python2运行，分支名里有中文的话，请自行处理字符编码问题。</p>
</blockquote>
<p>** python</p>
<pre><code class="language-python">#!/usr/bin/python3
import os
import platform
import requests


current_dir = os.path.abspath(os.path.dirname(__file__))
access_token = &quot;&lt;your access token&gt;&quot;
repos_url = 'http://git.i.example.com/api/v1/repos/search?access_token={}&amp;page={}&amp;limit={}'
branches_url = 'http://git.i.example.com/api/v1/repos/{}/branches?access_token={}'
repo_key_url = 'http://git.i.example.com/api/v1/repos/{}/{}/keys?access_token={}'


def repos():
    page = 1
    limit = 50
    has_next = True
    while has_next:
        r = requests.get(repos_url.format(access_token, page, limit))
        for repo in r.json()['data']:
            yield repo
        page += 1
        has_next = len(r.json()['data']) == limit


&quot;&quot;&quot;拉取项目所有分支代码到本地&quot;&quot;&quot;


def sync_repo():
    repo_index = 0
    for repo in repos():
        repo_index += 1
        # 克隆仓库
        os.chdir(current_dir)
        print('克隆第 {} 个仓库 {} '.format(repo_index, repo['name']))
        os.system(&quot;git clone {}&quot;.format(repo['ssh_url']))
        os.chdir(os.path.join(current_dir, repo['name']))
        # 更新仓库
        print('同步 {} 仓库所有分支'.format(repo['name']))
        os.system('git fetch --all')
        # if platform.system() == 'Windows':
        # Windows
        branches = requests.get(branches_url.format(
            repo['full_name'], access_token)).json()
        for branch in branches:
            branch_name = branch['name']
            os.system('git branch --track {} origin/{}'.format(branch_name, branch_name))
            # 用reset而不用pull是因为如果分支被强推了pull下来会有合并冲突，用rest就不会有冲突问题
            os.system('git checkout {} &amp;&amp; git reset --hard origin/{}'.format(branch_name, branch_name))
        # else:
        #     # Linux/macOS
        #     # git branch -r | grep -v '\-&gt;' | while read remote; do git branch --track ${remote#origin/} $remote; done &amp;&amp; git fetch --all &amp;&amp; git pull --all
        #     # os.system(&quot;git branch -r | grep -v '\-&gt;' | while read remote; do git branch --track ${remote#origin/} $remote; done &amp;&amp; git fetch --all &amp;&amp; git pull --all&quot;)
        #     # # 用reset而不用pull是因为如果分支被强推了pull下来会有合并冲突，用rest就不会有冲突问题
        #     os.system(&quot;git branch -r | grep -v '\-&gt;' | while read remote; do git branch --track ${remote#origin/} $remote; git checkout ${remote#origin/}; git reset --hard $remote; done&quot;)


&quot;&quot;&quot;设置项目部署公钥&quot;&quot;&quot;


def set_pub_key():
    repo_index = 0
    body = {
        &quot;key&quot;: &quot;ssh-rsa aabbcc&quot;,
        &quot;read_only&quot;: True,
        &quot;title&quot;: &quot;SandBox&quot;
    }
    for repo in repos():
        repo_index += 1
        print('==={}. {}==='.format(repo_index, repo['name']))
        r = requests.post(repo_key_url.format(
            repo['owner']['username'], repo['name'], access_token), data=body)
        print(r.json())


if __name__ == '__main__':
    sync_repo()
    # set_pub_key()
</code></pre>
<p>可以把脚本放在本地，使用cron(Linux/macOS)/计划任务(Windows)定时运行<code>python backup.py</code></p>
<p><em><a href="https://blog.csdn.net/flydragon0815/article/details/46006473">Windows计划任务运行cmd命令时，可使用非当前登录用户运行，这样就不会弹出小黑窗。</a></em></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[WebFlux 中 mongo 操作-Transaction]]></title>
        <id>https://me.iblogc.com/post/WebFlux中mongo操作-Transaction/</id>
        <link href="https://me.iblogc.com/post/WebFlux中mongo操作-Transaction/">
        </link>
        <updated>2020-01-17T07:16:46.000Z</updated>
        <summary type="html"><![CDATA[<br>]]></summary>
        <content type="html"><![CDATA[<br>
<!--more-->
<pre><code class="language-java">@PostMapping(&quot;/test&quot;)
public Mono testA(@RequestParam boolean exception) {
  return embedService.saveAC(new ADocument(&quot;张三&quot;), new CDocument(&quot;李四&quot;), exception);
}

@Override
public Mono&lt;Boolean&gt; saveAC(ADocument aDocument, CDocument cDocument, boolean exception) {
  return reactiveMongoTemplate.inTransaction()
    //所有文档的持久化操作都只能在单独一个execute函数中汇总实现
    .execute(action -&gt; action.insert(aDocument)
             .flatMap(a -&gt; {
               cDocument.setName(a.getName() + &quot;copy&quot;);
               return action.insert(cDocument)
                 .map(d -&gt; {
                   if (exception) {
                     //测试跨文档的异常回滚
                     throw Exceptions.propagate(new RuntimeException(&quot;模拟异常的出现&quot;));
                   }
                   return d;
                 });
             })
            )
    //如果里面是个mono，则用next取出第一个元素就是里面的mono
    .next()
    .map(list -&gt; {
      //需要注意，在execute之外的函数中产生的异常，不会触发事务的回滚。
      //                    if (exception) {
      //                        throw Exceptions.propagate(new RuntimeException(&quot;模拟异常的出现&quot;));
      //                    }
      return Boolean.TRUE;
    });
}
</code></pre>
<p>flux的数据库操作，在有事务的前提下不能用flatMap，要用事务不能用flatMap要用concatMap保持有序</p>
<pre><code class="language-java">@PostMapping(&quot;/test&quot;)
public Mono testA(@RequestParam boolean exception) {
  return embedService.saveAC(new ADocument(&quot;张三&quot;), new CDocument(&quot;李四&quot;), exception);
}

@Override
public Mono&lt;Boolean&gt; saveAC(ADocument aDocument, CDocument cDocument, boolean exception) {
  return reactiveMongoTemplate.inTransaction()
    //所有文档的持久化操作都只能在单独一个execute函数中汇总实现
    .execute(action -&gt; Flux.fromIterable(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;)
             //如果是个flux此处要用concatMap保持有序不能用flatMap
             .concatMap(i -&gt; action.insert(aDocument)
                        .flatMap(a -&gt; {
                          cDocument.setName(a.getName() + &quot;copy&quot;);
                          return action.insert(cDocument)
                            .map(d -&gt; {
                              if (exception) {
                                //测试跨文档的异常回滚
                                throw Exceptions.propagate(new RuntimeException(&quot;模拟异常的出现&quot;));
                              }
                              return d;
                            });
                        }));
            )
    //如果里面返回的就是一个flux则不需要使用next
    //.next()
    .map(list -&gt; {
      //需要注意，在execute之外的函数中产生的异常，不会触发事务的回滚。
      //                    if (exception) {
      //                        throw Exceptions.propagate(new RuntimeException(&quot;模拟异常的出现&quot;));
      //                    }
      return Boolean.TRUE;
    });
}
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[WebFlux 中 mongo 操作-Aggregation]]></title>
        <id>https://me.iblogc.com/post/WebFlux中mongo操作-Aggregation/</id>
        <link href="https://me.iblogc.com/post/WebFlux中mongo操作-Aggregation/">
        </link>
        <updated>2020-01-17T07:12:53.000Z</updated>
        <summary type="html"><![CDATA[<br>]]></summary>
        <content type="html"><![CDATA[<br>
<!--more-->
<p>switch</p>
<pre><code class="language-java">ConditionalOperators.Switch.CaseOperator cond = ConditionalOperators.Switch.CaseOperator.when(
                BooleanOperators.And.and(
                        ComparisonOperators.Eq.valueOf(&quot;channelBillStatus1&quot;).equalToValue(&quot;已结算&quot;),
                        ComparisonOperators.Eq.valueOf(&quot;channelBillStatus2&quot;).equalToValue(&quot;已结算&quot;)
                )
        ).then(&quot;已结清&quot;);

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.project(&quot;channelBillStatus1&quot;, &quot;channelBillStatus2&quot;)
                        .and(ConditionalOperators.switchCases(cond).defaultTo(&quot;未结清&quot;)).as(&quot;channelBillStatus&quot;)
        );
        
        reactiveMongoTemplate.aggregate(aggregation, PlatformBillItem.class, PlatformBillBo.class);
</code></pre>
<p>lookup及id类型转换</p>
<pre><code class="language-java">//把_id转成String并赋值给id
        Aggregation.project(&quot;internalId&quot;, &quot;name&quot;, &quot;isAvailable&quot;, &quot;isCanAdd&quot;, &quot;fitGender&quot;, &quot;fitAge&quot;, &quot;fitMaritalStatus&quot;, &quot;price&quot;, &quot;sortNo&quot;, &quot;createdAt&quot;)
          			//如果需要把String转Object使用ConvertOperators.ToObjectId.toObjectId()
                .and(ConvertOperators.ToString.toString(&quot;$_id&quot;)).as(&quot;id&quot;);
        //用当前表的id值去匹配chn_section表的sectionId字段值，并把结果存入chnSections数组
        Aggregation.lookup(&quot;chn_section&quot;, &quot;id&quot;, &quot;sectionId&quot;, &quot;chnSections&quot;);
        //如有需要，把chnSections数组拆出来，chnSections数组有几个元素，当前这条数据就会被拆成多少条，chnSections值会变成元素值而不再是原来的数组
        //如果chnSections数组无值，默认会丢弃这条数据，如果要保留设置preserveNullAndEmptyArrays=true
        Aggregation.unwind(&quot;chnSection&quot;, true);
        //只输出这些字段
        Aggregation.project(&quot;internalId&quot;, &quot;name&quot;, &quot;isAvailable&quot;, &quot;isCanAdd&quot;, &quot;fitGender&quot;, &quot;fitAge&quot;, &quot;fitMaritalStatus&quot;, &quot;price&quot;, &quot;sortNo&quot;, &quot;createdAt&quot;, &quot;chnSections&quot;);
        reactiveMongoTemplate.aggregate(aggregation, PlatformBillItem.class, PlatformBillBo.class);
</code></pre>
<p>如果lookup时，如果要对匹配的数据进行筛选（参考链接：https://stackoverflow.com/questions/51107626/spring-data-mongodb-lookup-with-pipeline-aggregation）</p>
<pre><code class="language-java">//原始mongo
//{
//   $lookup:
//     {
//       from: &lt;collection to join&gt;,
//       let: { &lt;var_1&gt;: &lt;expression&gt;, …, &lt;var_n&gt;: &lt;expression&gt; },
//       pipeline: [ &lt;pipeline to execute on the collection to join&gt; ],
//       as: &lt;output array field&gt;
//     }
//}
//自定义一个AggregationOperation类
public class CustomProjectAggregationOperation implements AggregationOperation {
    private String jsonOperation;

    public CustomProjectAggregationOperation(String jsonOperation) {
        this.jsonOperation = jsonOperation;
    }

    @Override
    public Document toDocument(AggregationOperationContext aggregationOperationContext) {
        return aggregationOperationContext.getMappedObject(Document.parse(jsonOperation));
    }
}

private static String getJsonOperation() {
        return &quot;{&quot; +
                &quot;    $lookup: &quot; +
                &quot;    {&quot; +
                &quot;        from: 'chn_set_meal',&quot; +
                &quot;        let: {&quot; +
                &quot;            id: { $toString: '$_id' }&quot; +
                &quot;        },&quot; +
                &quot;        pipeline: [&quot; +
                &quot;            {&quot; +
                &quot;                $match: &quot; +
                &quot;                {&quot; +
                &quot;                    $expr: &quot; +
                &quot;                    {&quot; +
                &quot;                        $and: &quot; +
                &quot;                        [&quot; +
                &quot;                            {&quot; +
                &quot;                                $eq: ['$setMealId', '$$id']&quot; +
                &quot;                            },&quot; +
                &quot;                            {&quot; +
                &quot;                                $eq: ['$cooperationState', '合作中']&quot; +
                &quot;                            }&quot; +
                &quot;                        ]&quot; +
                &quot;                    }&quot; +
                &quot;                }&quot; +
                &quot;            },&quot; +
                &quot;            {&quot; +
                &quot;                $project: {&quot; +
                &quot;                    channelId: 1,&quot; +
                &quot;                    channelName: 1&quot; +
                &quot;                    cooperationState: 1&quot; +
                &quot;                }&quot; +
                &quot;            }&quot; +
                &quot;        ],&quot; +
                &quot;        as: 'channels'&quot; +
                &quot;    }&quot; +
                &quot;}}&quot;;
    }

AggregationOperation aggregationOperation = new CustomProjectAggregationOperation(getJsonOperation());
        return reactiveMongoTemplate.aggregate(Aggregation.newAggregation(aggregationOperation), SetMeal.class, SetMealListBo.class);
</code></pre>
<p>group</p>
<pre><code class="language-java">//背景：查询交易表，订单和交易一对多
Aggregation.group(&quot;orderNo&quot;)
  //单一组的金额汇总
  .sum(&quot;amount&quot;).as(&quot;totalAmount&quot;)
  //组的最后一个订单号
  .last(&quot;orderNo&quot;).as(&quot;orderNo&quot;)
  //组里数据条数
  .count().as(&quot;tradeCount&quot;)
  //把一组数据里每条数据的状态放到一个statuses数组里
  .addToSet(&quot;status&quot;).as(&quot;statuses&quot;)
  //把一组数据里的一些字段信息重新组装成一个对象放到billItems的对象数组里
  .push(new BasicDBObject(&quot;tradeContent&quot;, &quot;$tradeContent&quot;)
        .append(&quot;tradeNo&quot;, &quot;$tradeNo&quot;)
        .append(&quot;amount&quot;, &quot;$amount&quot;)
       ).as(&quot;billItems&quot;);
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[自用软件推荐]]></title>
        <id>https://me.iblogc.com/post/awesome-software/</id>
        <link href="https://me.iblogc.com/post/awesome-software/">
        </link>
        <updated>2019-10-12T08:40:17.000Z</updated>
        <summary type="html"><![CDATA[<p>日用软件/效率提升/开发工具/Chorme 插件扩展推荐</p>
]]></summary>
        <content type="html"><![CDATA[<p>日用软件/效率提升/开发工具/Chorme 插件扩展推荐</p>
<!--more-->
<h5 id="日常">日常</h5>
<ol>
<li>
<p><a href="https://www.alfredapp.com/"><strong>Alfred</strong></a>(macOS)</p>
<p>Mac 上的效率启动神器</p>
</li>
<li>
<p><a href="https://u.tools/"><strong>uTools</strong></a>(macOS/Windows/Linux)</p>
<blockquote>
<p>你的生产力工具集。</p>
<p>uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件，打造你得心应手的工具集合。</p>
<p>当你熟悉它后，能够为你节约大量时间，让你可以更加专注地改变世界。</p>
</blockquote>
<p>除了插件中心提供的工具，随手就可以用自己熟悉的语言写一个简单的小工具放上面，感觉比 Alfred 还好用，哈哈</p>
</li>
<li>
<p><a href="http://www.yingdev.com/projects/wgestures"><strong>WGestures</strong></a>(Windows)</p>
<p>系统鼠标手势</p>
</li>
<li>
<p><a href="https://zh.snipaste.com/"><strong>Snipaste</strong></a>(macOS/Windows)</p>
<p>截图/贴图</p>
</li>
<li>
<p><a href="https://zh.xnipapp.com/"><strong>Xnip</strong></a>(macOS）</p>
<p>截图应用</p>
</li>
<li>
<p><a href="https://better365.yswebportal.cc/ishot.html"><strong>iShot</strong></a>(macOS)</p>
<p>截图/长截图/贴图/录屏</p>
</li>
<li>
<p><a href="https://www.voidtools.com/"><strong>Everything</strong></a>(Windows)</p>
<p>Windows 本地搜索，快如闪电，用过就回不去</p>
</li>
<li>
<p><s><a href="http://www.irolan.com/"><strong>Rolan</strong></a>(Windows)</s></p>
<p><s>Windows 上的快速启动器</s></p>
</li>
<li>
<p><a href="http://rime.im/"><strong>Rime</strong></a>(macOS/Windows/Linux)</p>
<p>输入法</p>
</li>
<li>
<p><a href="https://symless.com/synergy/"><strong>Synergy</strong></a>(macOS/Windows/Linux)</p>
<p>多台设备共用键鼠</p>
</li>
<li>
<p><s><strong>[Shadowsocks/v2rayNG/v2ray/V2rayU]</strong>(macOS/Windows/Linux/Android）</s></p>
<p><s>科学上网</s></p>
</li>
<li>
<p><a href="https://freemacsoft.net/appcleaner/"><strong>AppCleaner</strong></a>(macOS）</p>
<p>应用卸载器</p>
</li>
<li>
<p><a href="https://www.typora.io/"><strong>Typora</strong></a>(macOS/Windows/Linux）</p>
<p>markdown 笔记写作应用，配合坚果云或 Dropbox 同步，爽</p>
</li>
<li>
<p><a href="https://www.sumatrapdfreader.org/"><strong>SumatraPDF</strong></a>(Windows）</p>
<p>PDF 阅读器</p>
</li>
<li>
<p><s><a href="http://www.eagleget.com/"><strong>EagleGet</strong></a>(Windows）</s></p>
<p><s>下载器</s></p>
</li>
<li>
<p><s><a href="https://www.freedownloadmanager.org/zh/"><strong>Free Download Manager</strong></a>(macOS/Windows）</s></p>
<p><s>下载器</s></p>
</li>
<li>
<p><a href="https://www.bandisoft.com/bandizip/cn/"><strong>Bandizip</strong></a>(macOS/Windows）</p>
<p>压缩/解压软件</p>
</li>
<li>
<p><a href="https://ezip.awehunt.com/"><strong>MacZip</strong></a>(macOS）</p>
<p>压缩/解压软件</p>
</li>
<li>
<p><s><a href="https://github.com/hakimel/reveal.js"><strong>reveal.js</strong></a>(Web）</s></p>
<p><s>程序员的 PPT 工具</s></p>
</li>
<li>
<p><a href="https://paper.meiyuan.in/"><strong>pap.er</strong></a>(macOS）</p>
<p>壁纸应用</p>
</li>
<li>
<p><a href="https://trankynam.com/atext/"><strong>aText</strong></a>(macOS/Windows）</p>
<p>文字输入效率提升工具</p>
</li>
<li>
<p><a href="https://www.mowglii.com/itsycal/"><strong>Itsycal</strong></a>(macOS)</p>
<p>macOS 菜单栏上自定义显示日历与时间</p>
</li>
<li>
<p><a href="https://www.qireader.com/"><strong>Qi Reader</strong></a>(Web)</p>
<p>简洁好用的在线 RSS 阅读器，可全平台使用，支付 PWA</p>
</li>
</ol>
<h5 id="开发">开发</h5>
<ol>
<li>
<p><a href="https://github.com/cmderdev/cmder"><strong>Cmder</strong></a>(Windows)</p>
<p>Windows 默认命令行替代品</p>
</li>
<li>
<p><a href="https://www.apifox.cn/"><strong>Apifox</strong></a>(macOS/Windows/Linux)</p>
<p>跨平台的 REST 客户端</p>
</li>
<li>
<p><s><a href="https://www.getpostman.com/"><strong>Postman</strong></a>(macOS/Windows/Linux/<s>Chrome App</s>)</s></p>
<p>跨平台的接口调试工具，有 mocks 服务和接口文档生成功能</p>
</li>
<li>
<p><a href="https://iterm2.com/"><strong>iTerm2</strong></a>(macOS)</p>
<p>终端应用</p>
</li>
<li>
<p><a href="http://www.telerik.com/fiddler"><strong>Fiddler</strong></a>(macOS/Windows/Linux)</p>
<p>抓包工具</p>
</li>
<li>
<p><a href="https://www.charlesproxy.com/"><strong>Charles</strong></a>(macOS/Windows/Linux)</p>
<p>抓包工具</p>
</li>
<li>
<p><a href="https://pypi.org/project/mitmproxy/"><strong>mitmproxy</strong></a>(macOS/Windows/Linux)</p>
<p>可编程抓包</p>
</li>
<li>
<p><a href="https://github.com/fatedier/frp"><strong>frp</strong></a>(macOS/Windows/Linux)<br>
内网穿透</p>
</li>
<li>
<p><a href="https://github.com/kingToolbox/WindTerm"><strong>WindTerm</strong></a>/</p>
<p>全能终端神器</p>
</li>
<li>
<p><s><a href="https://www.royalapps.com/ts/mac/features"><strong>Royal TSX</strong></a></s></p>
<p><s>远程管理工具</s></p>
</li>
</ol>
<h4 id="chromeedge">Chrome/Edge</h4>
<h5 id="扩展应用">扩展/应用</h5>
<ol>
<li>
<p><a href="https://chrome.google.com/webstore/detail/adblock-plus-free-ad-bloc/cfhdojbkjhnklbpkdaibdccddilifddb"><strong>Adblock Plus</strong></a></p>
<p>广告拦截器，谁用谁知道</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/checker-plus-for-gmail/oeopbcgkkoapgobdbedcemjljbihmemj"><strong>Checker Plus for Gmail™</strong></a></p>
<p>Gmail/Inbox 插件，不用打开网页处理邮件</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh"><strong>Dark Reader</strong></a></p>
<p>黑色主题，适用于任何网站。关爱眼睛，就使用 Dark Reader 进行夜间和日间浏览</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/extension-manager/gjldcdngmdknpinoemndlidpcabkggco"><strong>扩展管理器（Extension Manager）</strong></a></p>
<p>扩展管理工具，可以对扩展进行分组，并进行批量快速的启用、禁用</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/wappalyzer/gppongmhjkpfnbhagpmjfkannfbllamg"><strong>Wappalyzer</strong></a></p>
<p>探测当前网页正在使用的开源软件或者 js 类库</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/ghostery-%E2%80%93-privacy-ad-blo/mlomiejdfkolichcflejclcbmpeaniij"><strong>Ghostery</strong></a></p>
<p>了解谁在跟踪您的网页浏览操作，并可禁用跟踪行为。</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo"><strong>Tampermonkey</strong></a></p>
<p>给网站添加自定义脚本</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne"><strong>Stylus</strong></a></p>
<p>给网站添加自定义样式表</p>
</li>
<li>
<p><s><a href="https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb"><strong>Web Server for Chrome</strong></a></s></p>
<p><s>快速搭建本地 Web 服务器（当然，你也可以<code>python -m http.server</code>)</s></p>
</li>
<li>
<p><s><a href="https://chrome.google.com/webstore/detail/octotree-github-code-tree/bkhaagjahfmjljalopjnoealnfndnagc"><strong>Octotree</strong></a></s></p>
<p><s>在浏览器左侧树形展示 Github 代码。</s></p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif"><strong>Proxy SwitchyOmega</strong></a></p>
<p>你懂的</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/v2ex-plus/daeclijmnojoemooblcbfeeceopnkolo"><strong>v2ex plus</strong></a></p>
<p>优雅便捷的 V2EX 扩展</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/web-scraper-free-web-scra/jnhgnonknehpejjnehehllkliplmbmhn"><strong>Web Scraper</strong></a></p>
<p>图形化创建爬虫，爬取网站数据</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/google-keep-chrome-extens/lpcaedmchfhocbbapmcbpinfpgnhiddi"><strong>Google Keep</strong></a></p>
<p>记事和清单</p>
</li>
<li>
<p><a href="https://flomoapp.com/"><strong>flomo · 浮墨笔记</strong></a></p>
<p>灵感记录</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/simpread-reader-view/ijllcpnolfcooahcekpamkbidhejabll"><strong>简悦</strong></a></p>
<p>阅读模式 + 标注 + 稍后读，自己最常用的还是网页转阅读模式/网页转 Markdown 同步到坚果云，或是导出成 PDF。</p>
</li>
<li>
<p><a href="https://chrome.google.com/webstore/detail/floccus-bookmarks-sync/fnaicdffflnofjppbagibeoednhnbjhg"><strong>floccus bookmarks sync</strong></a></p>
<p>浏览器<strong>内置</strong>书签<strong>同步</strong>工具，目前支持 Firefox、Chrome、Edge 和其它能装 Chrome 插件的浏览器，可配合坚果云使用。</p>
</li>
</ol>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git后悔药]]></title>
        <id>https://me.iblogc.com/post/git-reset/</id>
        <link href="https://me.iblogc.com/post/git-reset/">
        </link>
        <updated>2019-07-25T10:33:09.000Z</updated>
        <summary type="html"><![CDATA[<p>使用git提交代码过程中有时会手抖提交错误代码，这时就需要用到git的后悔药reset操作。</p>
]]></summary>
        <content type="html"><![CDATA[<p>使用git提交代码过程中有时会手抖提交错误代码，这时就需要用到git的后悔药reset操作。</p>
<!--more-->
<figure data-type="image" tabindex="1"><img src="https://me.iblogc.com/post-images/1676611108079.png" alt="" loading="lazy"></figure>
<h3 id="差异diff">差异（diff）</h3>
<p>工作区vs暂存区: <code>git diff</code></p>
<p>暂存区vs本地仓库: <code>git diff —cached</code></p>
<p>本地仓库vs远程仓库: <code>git diff &lt;分支名&gt; origin/&lt;分支名&gt;</code></p>
<h3 id="撤消reset">撤消（reset）</h3>
<p>撤消工作区修改: <code>git reset —hard</code></p>
<p>撤消(1)<code>git add</code>: <code>git reset &amp;&amp; git checkout .</code>或<code>git reset —-hard</code>(会还原所有修改)</p>
<p>撤消(2) <code>git commit</code>: <code>git reset --hard origin/master</code>(使用远端的master分支恢复到本地)</p>
<p>撤消(3) <code>git push</code>: <code>git reset --hard HEAD^ &amp;&amp; git push -f</code>(先在本地回到上一个版本，然后强推到远端)</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git常用命令别名设置]]></title>
        <id>https://me.iblogc.com/post/git-alias/</id>
        <link href="https://me.iblogc.com/post/git-alias/">
        </link>
        <updated>2019-06-28T09:17:06.000Z</updated>
        <summary type="html"><![CDATA[<p>如果平时使用git使用git命令多于GUI工具，则设置一些常用命令的别名有且于效率提升，以下是我平时使用较多的一些命令的别名设置</p>
]]></summary>
        <content type="html"><![CDATA[<p>如果平时使用git使用git命令多于GUI工具，则设置一些常用命令的别名有且于效率提升，以下是我平时使用较多的一些命令的别名设置</p>
<!--more-->
<p>Git别名设置</p>
<pre><code class="language-bash">git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.sw switch
git config --global alias.cp cherry-pick
git config --global alias.unstage 'reset HEAD'
# 可用git pull -r代替
git config --global alias.fr '!f() { git fetch &amp;&amp; git rebase $@; }; f'; 
# git提交日志
git config --global alias.lg &quot;log --color --graph --pretty=format:'%Cred%h%Creset - %Cgreen(%cd)%C(yellow)%d%Creset %s %C(blue)[%an/%cn]%Creset' --date=format:'%Y-%m-%d %H:%M:%S %z' --abbrev-commit&quot;

# 加两条我的初始配置
git config --global pull.rebase true
git config --global rebase.autoStash true
</code></pre>
<p>删除别名</p>
<pre><code class="language-bash">git config --global --unset alias.xxx
</code></pre>
<p>以下两个命令设置git alias和zsh alias都失败，暂没找到方法可以设置别名</p>
<pre><code class="language-bash"># 查看仓库提交者排名前 5
git log --pretty='%aN' | sort | uniq -c | sort -k1 -n -r | head -n 5
</code></pre>
<pre><code class="language-bash"># 统计每个人增删行数
git log --format='%aN' | sort -u | while read name; do echo -en &quot;$name\t&quot;; git log --author=&quot;$name&quot; --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf &quot;added lines: %s, removed lines: %s, total lines: %s\n&quot;, add, subs, loc }' -; done
</code></pre>
<p>git lg命令效果图<br>
<img src="https://me.iblogc.com/post-images/git-lg-pre-img.png" alt="git lg命令效果图" loading="lazy"></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Android真机调试常用命令]]></title>
        <id>https://me.iblogc.com/post/android真机调试常用命令/</id>
        <link href="https://me.iblogc.com/post/android真机调试常用命令/">
        </link>
        <updated>2019-04-28T12:39:32.000Z</updated>
        <summary type="html"><![CDATA[<p>使用USB连接Android真机调试时，使用无线连接调试会方便很多，并使用电脑端用adb命令实现截图和录屏，方便调试和问题反馈。</p>
]]></summary>
        <content type="html"><![CDATA[<p>使用USB连接Android真机调试时，使用无线连接调试会方便很多，并使用电脑端用adb命令实现截图和录屏，方便调试和问题反馈。</p>
<!--more-->
<h2 id="无线调试">无线调试</h2>
<pre><code># 前提条件：手机和电脑处理同一网段
# 第一次手机先使用USB连接电脑执行以下命令让手机上的某一端口处于监听状态
adb tcpip &lt;port&gt;

# 在手机上查看ip地址或使用以下命令查看ip
adb shell ifconfig 
# 连接手机（在同一个环境下，一般手机/电脑不重启就会一直连接着）
adb connect &lt;ip&gt; :&lt;port&gt;
# 查看连接的设备
adb devices
</code></pre>
<h2 id="截图">截图</h2>
<pre><code># 截图并保存到手机sd卡的下
adb shell screencap -p /sdcard/screenshot.png
</code></pre>
<p>便捷脚本（截图并自动复制到电脑剪切板/保存到电脑本地）</p>
<blockquote>
<p>因脚本里调用了linux/macOS的命令，所以只适用于macOS系统，windows请自行修改脚本。</p>
</blockquote>
<p><code>vi shot.sh</code></p>
<pre><code>#!/bin/bash
# Android截图，定位和预览默认关闭，请取消注释

dd=`date +%Y-%m-%d-%H-%M-%S`
pwd=`pwd`
adb shell screencap -p /sdcard/screenshot.png
adb pull /sdcard/screenshot.png
adb shell rm /sdcard/screenshot.png
mv screenshot.png $dd.png
echo &quot;截图已保存为当前目录下的&quot;$dd.png
# 修改图片尺寸，长或宽最大不超过960，等比缩放
echo &quot;压缩图片...&quot;
sips -Z 960 $pwd/$dd.png
# 定位到文件
open ./$dd.png -R
# 打开预览
open -a Preview $dd.png
# 复制到剪切板
osascript -e 'on run args' -e 'set the clipboard to POSIX file (first item of args)' -e end $pwd/$dd.png
echo &quot;截图已复制到剪切板&quot;
</code></pre>
<p>授予执行权限</p>
<pre><code>chmod a+x shot.sh
</code></pre>
<p>使用方法</p>
<pre><code>./shot.sh
</code></pre>
<p>⌘+v试试</p>
<p><em>可把命令添加alias别名</em></p>
<h2 id="录屏">录屏</h2>
<pre><code># 执行录屏并保存到手机sd卡目录下（默认时长180s）
# 可配置参数
# --time-limit: 录制时长，单位秒
# --size: 分辨率，如1280*720，不指定默认使用手机的分辨率
# --bit-rate: 视频的比特率，如6Mbps为6000000
# --verbose: 命令行显示log
adb shell screenrecord /sdcard/demo.mp4
</code></pre>
<p>便捷脚本（录屏并自动复制到电脑剪切板/保存到电脑本地）</p>
<blockquote>
<p>因脚本里调用了linux/macOS的命令，所以只适用于macOS系统，windows请自行修改脚本。</p>
</blockquote>
<p><code>vi record.sh</code></p>
<pre><code>#!/bin/bash
# Android录屏
dd=`date +%Y-%m-%d-%H-%M-%S`&quot;-$1s&quot;
pwd=`pwd`
adb shell screenrecord --time-limit $1 /sdcard/screenrecord.mp4
adb pull /sdcard/screenrecord.mp4
adb shell rm /sdcard/screenrecord.mp4
mv screenrecord.mp4 $dd.mp4
echo &quot;$1秒视频已保存为当前目录下的&quot;$dd.mp4
# 定位到文件
open ./$dd.mp4 -R
# 复制到剪切板
osascript -e 'on run args' -e 'set the clipboard to POSIX file (first item of args)' -e end $pwd/$dd.mp4
echo &quot;$1秒视频已复制到剪切板&quot;
</code></pre>
<p>授予执行权限</p>
<pre><code>chmod a+x record.sh
</code></pre>
<p>使用方法</p>
<pre><code># 3为录制秒数，可修改
./record.sh 3
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[JS笔记]]></title>
        <id>https://me.iblogc.com/post/js笔记/</id>
        <link href="https://me.iblogc.com/post/js笔记/">
        </link>
        <updated>2019-04-24T08:35:13.000Z</updated>
        <summary type="html"><![CDATA[<p>js中要用变量作为key的话使用方括号括住<br>
例：<code>this.searchKeyword</code></p>
<pre><code class="language-javascript">this.$http({
    url: this.searchUrl,
    method: this.remoteRequestMethod,
    params: Object.assign({}, this.searchParams, this.pager),
    data: Object.assign({ [this.searchKeyword]: query }, this.searchBody, this.pager)
    })
</code></pre>
]]></summary>
        <content type="html"><![CDATA[<p>js中要用变量作为key的话使用方括号括住<br>
例：<code>this.searchKeyword</code></p>
<pre><code class="language-javascript">this.$http({
    url: this.searchUrl,
    method: this.remoteRequestMethod,
    params: Object.assign({}, this.searchParams, this.pager),
    data: Object.assign({ [this.searchKeyword]: query }, this.searchBody, this.pager)
    })
</code></pre>
<!--more-->
<p>全文完🙈</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[SpringWebFlux使用笔记]]></title>
        <id>https://me.iblogc.com/post/SpringWebFlux/</id>
        <link href="https://me.iblogc.com/post/SpringWebFlux/">
        </link>
        <updated>2019-02-28T05:32:59.000Z</updated>
        <summary type="html"><![CDATA[<p>记录使用SpringWebFlux的一些笔记。</p>
]]></summary>
        <content type="html"><![CDATA[<p>记录使用SpringWebFlux的一些笔记。</p>
<!--more-->
<h2 id="groupby">groupBy</h2>
<p>对flux进行分组。</p>
<pre><code class="language-java">channelOnlineCityBoFlux.sort((s1, s2) -&gt; Objects.requireNonNull(s1.getInitial()).compareTo(s2.getInitial()))
  .groupBy(city -&gt; city.getInitial().substring(0, 1).toUpperCase())
  .sort((s1, s2) -&gt; Objects.requireNonNull(s1.key()).compareTo(s2.key()))
  .flatMap(gf -&gt; gf.collectList()
          .map(cityList -&gt; {
              ChannelOnlineCityGroupByPinYinBo cityGroupByPinYinBo = new ChannelOnlineCityGroupByPinYinBo();
              cityGroupByPinYinBo.setLetter(gf.key());
              cityGroupByPinYinBo.setCities(cityList);
              return cityGroupByPinYinBo;
          }));
</code></pre>
<h2 id="handle">handle</h2>
<p>handle作用相当于是filter和map的组合。</p>
<iframe
  src="https://carbon.now.sh/embed/?bg=rgba(171%2C184%2C195%2C100)&t=dracula&wt=none&l=text%2Fx-java&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=48px&ph=32px&ln=false&fm=Hack&fs=13px&lh=133%25&si=false&code=public%2520static%2520String%2520alphabet(int%2520letterNumber)%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520if%2520(letterNumber%2520%253C%25201%2520%257C%257C%2520letterNumber%2520%253E%252026)%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520return%2520null%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%257D%250A%2520%2520%2520%2520%2520%2520%2520%2520int%2520letterIndexAscii%2520%253D%2520'A'%2520%252B%2520letterNumber%2520-%25201%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520return%2520%2522%2522%2520%252B%2520(char)%2520letterIndexAscii%253B%250A%2520%2520%2520%2520%257D%250A%250A%250Apublic%2520static%2520void%2520main(String%255B%255D%2520args)%2520%257B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%252F%252F%2520%25E5%2586%2599%25E6%25B3%25951%250A%2520%2520%2520%2520%2520%2520%2520%2520Flux%253CString%253E%2520alphabet%2520%253D%2520Flux.just(-1%252C%252030%252C%252013%252C%25209%252C%252020)%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520.handle((i%252C%2520sink)%2520-%253E%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520String%2520letter%2520%253D%2520alphabet(i)%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520if%2520(letter%2520!%253D%2520null)%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520sink.next(letter)%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257D)%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520alphabet.map(a%2520-%253E%2520a.toLowerCase())%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520.subscribe(System.out%253A%253Aprintln)%253B%250A%250A%2520%2520%2520%2520%2520%2520%2520%2520%252F%252F%2520%25E5%2586%2599%25E6%25B3%25952%250A%2520%2520%2520%2520%2520%2520%2520%2520Flux.just(-1%252C%252030%252C%252013%252C%25209%252C%252020)%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520.handle((i%252C%2520sink)%2520-%253E%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520String%2520letter%2520%253D%2520alphabet(i)%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520if%2520(letter%2520!%253D%2520null)%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520sink.next(letter)%253B%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%257D)%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%252F%252F%2520%25E8%25BF%2599%25E9%2587%258C%25E9%259C%2580%25E8%25A6%2581%25E5%2581%259A%25E4%25B8%2580%25E6%25AC%25A1%25E5%25BC%25BA%25E5%2588%25B6%25E7%25B1%25BB%25E5%259E%258B%25E8%25BD%25AC%25E6%258D%25A2%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520.map(a%2520-%253E%2520(String)%2520a)%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520.map(sa%2520-%253E%2520sa.toLowerCase())%250A%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520%2520.subscribe(System.out%253A%253Aprintln)%253B%250A%250A%2520%2520%2520%2520%257D&es=2x&wm=false"
  style="transform:scale(1); width:600px; height:800px; border:0; overflow:hidden;"
  sandbox="allow-scripts allow-same-origin">
</iframe>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Hello Gridea]]></title>
        <id>https://me.iblogc.com/post/hello-gridea/</id>
        <link href="https://me.iblogc.com/post/hello-gridea/">
        </link>
        <updated>2018-12-11T16:00:00.000Z</updated>
        <summary type="html"><![CDATA[<p>👏  欢迎使用 <strong>Gridea</strong> ！<br>
✍️  <strong>Gridea</strong> 一个静态博客写作客户端。你可以用它来记录你的生活、心情、知识、笔记、创意... ...</p>
]]></summary>
        <content type="html"><![CDATA[<p>👏  欢迎使用 <strong>Gridea</strong> ！<br>
✍️  <strong>Gridea</strong> 一个静态博客写作客户端。你可以用它来记录你的生活、心情、知识、笔记、创意... ...</p>
<!-- more -->
<p><a href="https://github.com/getgridea/gridea">Github</a><br>
<a href="https://gridea.dev/">Gridea 主页</a><br>
<a href="https://fehey.com/">示例网站</a></p>
<h2 id="特性">特性👇</h2>
<p>📝  你可以使用最酷的 <strong>Markdown</strong> 语法，进行快速创作</p>
<p>🌉  你可以给文章配上精美的封面图和在文章任意位置插入图片</p>
<p>🏷️  你可以对文章进行标签分组</p>
<p>📋  你可以自定义菜单，甚至可以创建外部链接菜单</p>
<p>💻  你可以在 <strong>Windows</strong>，<strong>MacOS</strong> 或 <strong>Linux</strong> 设备上使用此客户端</p>
<p>🌎  你可以使用 <strong>𝖦𝗂𝗍𝗁𝗎𝖻 𝖯𝖺𝗀𝖾𝗌</strong> 或 <strong>Coding Pages</strong> 向世界展示，未来将支持更多平台</p>
<p>💬  你可以进行简单的配置，接入 <a href="https://github.com/gitalk/gitalk">Gitalk</a> 或 <a href="https://github.com/SukkaW/DisqusJS">DisqusJS</a> 评论系统</p>
<p>🇬🇧  你可以使用<strong>中文简体</strong>或<strong>英语</strong></p>
<p>🌁  你可以任意使用应用内默认主题或任意第三方主题，强大的主题自定义能力</p>
<p>🖥  你可以自定义源文件夹，利用 OneDrive、百度网盘、iCloud、Dropbox 等进行多设备同步</p>
<p>🌱 当然 <strong>Gridea</strong> 还很年轻，有很多不足，但请相信，它会不停向前 🏃</p>
<p>未来，它一定会成为你离不开的伙伴</p>
<p>尽情发挥你的才华吧！</p>
<p>😘 Enjoy~</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 alembic 进行数据库版本管理]]></title>
        <id>https://me.iblogc.com/post/使用alembic进行数据库版本管理/</id>
        <link href="https://me.iblogc.com/post/使用alembic进行数据库版本管理/">
        </link>
        <updated>2018-09-13T10:53:14.000Z</updated>
        <summary type="html"><![CDATA[<p>转自：https://www.cnblogs.com/blackmatrix/p/6236573.html，做了部分修改</p>
<h2 id="前言">前言</h2>
<p>随着项目业务需求的不断变更，数据库的表结构修改难以避免，此时就需要对数据库的修改加以记录和控制，便于项目的版本管理和随意的升级和降级。</p>
<p>Alembic 就可以很好的解决这个问题。Alembic 是 SQLAlchemy 作者开发的 Python 数据库版本管理工具。</p>
]]></summary>
        <content type="html"><![CDATA[<p>转自：https://www.cnblogs.com/blackmatrix/p/6236573.html，做了部分修改</p>
<h2 id="前言">前言</h2>
<p>随着项目业务需求的不断变更，数据库的表结构修改难以避免，此时就需要对数据库的修改加以记录和控制，便于项目的版本管理和随意的升级和降级。</p>
<p>Alembic 就可以很好的解决这个问题。Alembic 是 SQLAlchemy 作者开发的 Python 数据库版本管理工具。</p>
<!--more-->
<h2 id="安装">安装</h2>
<pre><code class="language-bash">pip install alembic
</code></pre>
<p>通过 pip 命令安装，如果使用虚拟环境，记得激活虚拟环境后再执行 pip 命令</p>
<p>同时需要安装的还有 SQLAlchemy 和 PyMysql</p>
<pre><code class="language-bash">pip install sqlalchemy
pip install pymysql
</code></pre>
<h2 id="初始化">初始化</h2>
<p>在使用 alembic 之前，需要进行初始化操作。</p>
<pre><code class="language-bash">alembic init &lt;YOUR_ALEMBIC_DIR&gt;
</code></pre>
<p>YOUR_ALEMBIC_DIR，可以取一个符合项目名称规范的目录名，如</p>
<pre><code class="language-bash">alembic init alembic
</code></pre>
<p><strong>此时需要注意，如果之前是在虚拟环境中安装的 alembic，需要激活虚拟环境后，在执行上述命令。</strong></p>
<p><strong>同时，建议 cd 到项目根目录再执行初始化操作，因为 YOUR_ALEMBIC_DIR 会在当前目录下创建。</strong></p>
<p>显示类似结果即初始化成功。</p>
<pre><code class="language-bash">Creating directory D:\Project\py_sqlalchemy_demo\alembic ... done
Creating directory D:\Project\py_sqlalchemy_demo\alembic\versions ... done
Generating D:\Project\py_sqlalchemy_demo\alembic.ini ... done
Generating D:\Project\py_sqlalchemy_demo\alembic\env.py ... done
Generating D:\Project\py_sqlalchemy_demo\alembic\README ... done
Generating D:\Project\py_sqlalchemy_demo\alembic\script.py.mako ... done
Please edit configuration/connection/logging settings in 'D:\\Project\\py_sqlalchemy_demo\\alembic.ini' befor
e proceeding.
</code></pre>
<p>初始化成功后，会在执行初始化命令的目录下，生成一个 alembic.ini 的配置文件，及一个 alembic 目录，目录名就是之前设置的 YOUR_ALEMBIC_DIR。</p>
<h2 id="修改配置文件">修改配置文件</h2>
<p>接下来对 alembic.ini 的信息进行修改。</p>
<p>主要修改的是配置文件中的数据库连接部分。</p>
<pre><code class="language-python">sqlalchemy.url = driver://user:pass@localhost:port/dbname
</code></pre>
<p>将配置文件中，此部分替换成对应的数据库连接，这个数据库连接的写法是与 SQLAlchemy 创建 engine 时是一样的。</p>
<p>如我在 demo 中使用的是 SQLAlchemy 与 PyMysql，那数据库连接就是类似如下</p>
<pre><code class="language-python">mysql+pymysql://demo_user:demo123456@127.0.0.1:3306/demo_db
</code></pre>
<h2 id="修改-envpy">修改 env.py</h2>
<p>除修改配置文件外，还需要对 YOUR_ALEMBIC_DIR 目录下的 env.py 文件进行修改。</p>
<p>在 env.py 中，将 target_metadata 设置成项目的 model，使 alembic 能获取到项目中 model 定义的信息。</p>
<p>将原先的</p>
<pre><code class="language-python">target_metadata = None
</code></pre>
<p>修改成项目中的 model</p>
<pre><code class="language-python">import os
import sys

sys.path.append(dirname(dirname(abspath(__file__))))
from app import db
target_metadata = db.metadata
</code></pre>
<h2 id="创建新版本">创建新版本</h2>
<p>用 alembic revision -m + 注释 创建数据库版本</p>
<pre><code class="language-bash">alembic revision --autogenerate -m &quot;init db&quot;
</code></pre>
<p>运行后，类似如下结果，即创建版本成功</p>
<pre><code class="language-bash">INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected removed table 'user'
Generating D:\Project\py_sqlalchemy_demo\alembic\versions\7b55b3d83158_create_tables.py ... done
</code></pre>
<p>每次修改过 SQLAlchemy 的 model，执行此命令即可创建对应的版本。</p>
<p>执行成功后，会在项目根目录下的 alembic/versions / 下生成的对应版本的 py 文件。命令规则是版本号 + 注释。(这个命名规则是在配置文件中定义的)</p>
<p>在每次创建新版本后，需要执行将数据库升级到新版本的命令，才能继续更新版本。</p>
<h2 id="变更数据库">变更数据库</h2>
<p>在每次创建新版本后，需要执行将数据库升级到新版本的命令，才能继续更新版本</p>
<p><strong>将数据库升级到最新版本</strong></p>
<pre><code class="language-bash">alembic upgrade head
</code></pre>
<p>运行结果类似</p>
<pre><code class="language-bash">(venv_win) D:\Project\py_sqlalchemy_demo&gt;alembic upgrade head
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade 7b55b3d83158 -&gt; b034414f04cd, create tables02
</code></pre>
<p>其中，命令中的 head 和 base 特指最新版本和最初版本。当需要对数据库进行升级时，使用 upgrade，降级使用 downgrade。</p>
<p><strong>将数据库降级到最初版本</strong></p>
<pre><code class="language-bash">alembic downgrade base
</code></pre>
<p><strong>将数据库降级到执行版本</strong>，使用 alembic downgrade + 版本号，不包含注释部分</p>
<pre><code class="language-bash">alembic downgrade &lt;version&gt;
</code></pre>
<p>如</p>
<pre><code class="language-bash">alembic downgrade 7b55b3d83158
</code></pre>
<p>运行结果</p>
<pre><code class="language-bash">INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running downgrade b034414f04cd -&gt; 7b55b3d83158, create tables02
</code></pre>
<p>升级也是同样的道理，alembic upgrade + 版本号</p>
<h2 id="离线更新生成-sql-脚本">离线更新（生成 sql 脚本）</h2>
<p>在某些不适合在线更新的情况，可以采用生成 sql 脚本的形式，进行离线更新：</p>
<pre><code class="language-bash">alembic upgrade &lt;version&gt; --sql &gt; migration.sql
</code></pre>
<p>如：</p>
<pre><code class="language-bash">alembic upgrade ae1027a6acf --sql &gt; migration.sql
</code></pre>
<p>从特定起始版本生成 sql 脚本：</p>
<pre><code class="language-bash">alembic upgrade &lt;vsersion&gt;:&lt;vsersion&gt; --sql &gt; migration.sql
</code></pre>
<p>如：</p>
<pre><code class="language-bash">alembic upgrade 1975ea83b712:ae1027a6acf --sql &gt; migration.sql
</code></pre>
<p>如果是数据库降级操作，把 upgrade 替换为 downgrade。</p>
<h2 id="查询当前数据库版本号">查询当前数据库版本号</h2>
<p>在对数据库进行升级或降级后，会在当前操作的数据库中新增一个表；alembic_version。</p>
<p>表中的 version_num 字段记录了当前的数据库版本号。</p>
<h2 id="清除所有版本">清除所有版本</h2>
<p>如果需要清除所有的版本，将 versions 删除掉，同时删除数据库的 alembic_version 表。</p>
<h2 id="参考资料">参考资料</h2>
<p><a href="http://alembic.zzzcomputing.com/en/latest/tutorial.html">http://alembic.zzzcomputing.com/en/latest/tutorial.html</a></p>
<p><a href="http://www.codeweblog.com/%25E5%25B8%25B8%25E8%25A7%2581%25E7%259A%2584sqlalchemy%25E5%2588%2597%25E7%25B1%25BB%25E5%259E%258B-%25E9%2585%258D%25E7%25BD%25AE%25E9%2580%2589%25E9%25A1%25B9%25E5%2592%258C%25E5%2585%25B3%25E7%25B3%25BB%25E9%2580%2589%25E9%25A1%25B9/">http://www.codeweblog.com/%E5%B8%B8%E8%A7%81%E7%9A%84sqlalchemy%E5%88%97%E7%B1%BB%E5%9E%8B-%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9%E5%92%8C%E5%85%B3%E7%B3%BB%E9%80%89%E9%A1%B9/</a></p>
<p><a href="http://blog.csdn.net/wenxuansoft/article/details/50242957">http://blog.csdn.net/wenxuansoft/article/details/50242957</a></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django REST framework 单元测试「Unit Testing」]]></title>
        <id>https://me.iblogc.com/post/django-rest-framework接口单元测试/</id>
        <link href="https://me.iblogc.com/post/django-rest-framework接口单元测试/">
        </link>
        <updated>2017-09-05T15:23:41.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
## `settings`
`Django`运行单元测试时，会以`settings`里的数据库配置里的`NAME`新建一个以`test_`开关的临时数据库，并在测试结束后删除，默认的测试数据库会以当前的`migrations`文件来创建数据表并进行迁移，但如果`migrations`文件很多，每次运行时间将很久，所以可以跳过迁移，直接以当前`Model`结果来创建表以提升测试效率，如果想进一步加快测试时创建数据库的速度，可以使用`SQLite`数据库引擎，当使用`SQLite`数据库引擎时，测试将默认使用内存数据库。
```python
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
if TESTING:
    # 当使用SQLite数据库引擎时，测试将默认使用内存数据库
    DATABASES['default'] = {
        'ENGINE': 'django.db.backends.sqlite3',
    }
    # 单元测试时, 跳过migrate, 极 的提升测试运 效率
    # 具体可以查看
    # https://simpleisbetterthancomplex.com/tips/2016/08/19/django-tip-12-disabl ing-migrations-to-speed-up-unit-tests.html
    # https://stackoverflow.com/questions/36487961/django-unit-testing-taking-a- very-long-time-to-create-test-database
<pre><code>class DisableMigrations(object):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return &quot;notmigrations&quot;


MIGRATION_MODULES = DisableMigrations()
</code></pre>
<pre><code>
## 示例代码
```python
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals

from rest_framework import status
from rest_framework.test import APITestCase

from apps.account.models import User
from apps.account.tests.test_utils import TestCaseUtils

__author__ = 'jeff'


class UserAPITests(APITestCase, TestCaseUtils):
    # 初始数据加载，可使用manage.py dumpdata [app_label app_label app_label.Model]生成
    # xml/yaml/json格式的数据
    # 一般放在每个应用的fixtures目录下, 只需要填写json文件名即可，django会自动查找
    # 此测试类运行结束后，会自动从数据库里销毁这份数据
    # fixtures = ['user.json']

    def setUp(self):
        # 在类里每个测试方法执行前会运行
        # 在此方法执行前，django会运行以下操作
        # 1. 重置数据库，数据库恢复到执行migrate后的状态
        # 2. 加载fixtures数据
        # 所以每个测试方法里对数据库的操作都是独立的，不会相互影响
        kwargs = dict(mobile_phone='15999999999', password='111111')
        self.user = User.app_user_objects.create(**kwargs)

    def tearDown(self):
        # 在类里每个方法结束执行后会运行
        pass

    @classmethod
    def setUpClass(cls):
        # 在类初始化时执行，必须调用super
        super(UserAPITests, cls).setUpClass()
        cls.token = ''

    @classmethod
    def tearDownClass(cls):
        # 在整个测试类运行结束时执行，必须调用super
        super(UserAPITests, cls).tearDownClass()

    def test_app_user_login_success(self):
        &quot;&quot;&quot;APP用户登录接口成功情况&quot;&quot;&quot;
        # path使用硬编码，不要使用reverse反解析url，以便在修改url之后能及时发现接口地址变化，并通知接口使用人员
        path = '/api/api-token-auth/'
        data = {'mobile_phone': '15999999999', 'password': '111111'}
        response = self.client.post(path, data)
        # response.data是字典对象
        # response.content是json字符串对象
        self.assertEquals(response.status_code,
                          status.HTTP_200_OK,
                          '登录接口返回状态码错误: 错误信息: {}'.format(response.content))
        self.assertIn('token', response.data, '登录成功后无token返回')

    def test_app_user_login_with_error_pwd(self):
        path = '/api/api-token-auth/'
        data = {'mobile_phone': '15999999999', 'password': '123456'}
        response = self.client.post(path, data)
        self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertJSONEqual('{&quot;errors&quot;:[&quot;用户名或密码错误。&quot;]}', response.content)

    def test_get_app_user_profile_success(self):
        &quot;&quot;&quot;成功获取app用户个人信息接口&quot;&quot;&quot;
        path = '/api/account/user/profile/'
        headers = self.get_headers(user=self.user)
        response = self.client.get(path, **headers)
        # 校验一些关键数据即可
        # 如果是创建新数据，不仅要校验返回的状态码和数据，
        # 还需要到使用Django ORM去数据库查询数据是否创建成功
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(6, len(response.data))
        self.assertIn('url', response.data)
        self.assertIn('mobile_phone', response.data)
        self.assertIn('avatar', response.data)
        self.assertIn('company_name', response.data)
        self.assertIn('username', response.data)
        self.assertIn('is_inviter', response.data)

    def test_get_app_user_profile_without_token(self):
        &quot;&quot;&quot;不传token请求获取用户信息接口&quot;&quot;&quot;
        path = '/api/account/user/profile/'
        response = self.client.get(path)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
</code></pre>
<h2 id="断言">断言</h2>
<pre><code class="language-python"># 来自unittest.case.TestCase
assertFalse(expr, msg=None)
assertTrue(expr, msg=None)
assertEqual(first, second, msg=None)
assertNotEqual(first, second, msg=None)
assertAlmostEqual(first, second, places=None, msg=None, delta=None)
assertNotAlmostEqual(first, second, places=None, msg=None, delta=None)
assertSequenceEqual(seq1, seq2, msg=None, seq_type=None)
assertListEqual(list1, list2, msg=None)
assertTupleEqual(tuple1, tuple2, msg=None)
assertSetEqual(set1, set2, msg=None)
assertIn(member, container, msg=None)
assertNotIn(member, container, msg=None)
assertIs(expr1, expr2, msg=None)
assertIsNot(expr1, expr2, msg=None)
assertDictEqual(d1, d2, msg=None)
assertDictContainsSubset(expected, actual, msg=None)
assertItemsEqual(expected_seq, actual_seq, msg=None)
assertMultiLineEqual(first, second, msg=None)
assertLess(a, b, msg=None)
assertLessEqual(a, b, msg=None)
assertGreater(a, b, msg=None)
assertGreaterEqual(a, b, msg=None)
assertIsNone(obj, msg=None)
assertIsInstance(obj, cls, msg=None)
assertNotIsInstance(obj, cls, msg=None)
assertRaisesRegexp(expected_exception, expected_regexp,
                           callable_obj=None, *args, **kwargs)
assertRegexpMatches(text, expected_regexp, msg=None)
assertNotRegexpMatches(text, unexpected_regexp, msg=None)
</code></pre>
<h2 id="测试接口地址">测试接口地址</h2>
<p>测试接口地址建议使用硬编码，不要使用<code>reverse</code>反解析url，原因是接口地址尽量避免改变，如果必须修改，需要以很明显的方式来提醒开发人员以便开发人员通知接口使用人员。</p>
<h2 id="测试数据准备">测试数据准备</h2>
<p>有如下两种方法准备测试数据</p>
<ol>
<li>简单的数据可以在<code>setUp()</code>里来创建；</li>
<li>复杂数据可以使用fixtures来写，并在赋值给测试类的<code>fixtures</code>属性；<br>
fixtures数据示例</li>
</ol>
<pre><code class="language-json">[
  {
    &quot;model&quot;: &quot;myapp.person&quot;,
    &quot;pk&quot;: 1,
    &quot;fields&quot;: {
      &quot;first_name&quot;: &quot;John&quot;,
      &quot;last_name&quot;: &quot;Lennon&quot;
    }
  },
  {
    &quot;model&quot;: &quot;myapp.person&quot;,
    &quot;pk&quot;: 2,
    &quot;fields&quot;: {
      &quot;first_name&quot;: &quot;Paul&quot;,
      &quot;last_name&quot;: &quot;McCartney&quot;
    }
  }
]
</code></pre>
<h2 id="测试覆盖率coverage">测试覆盖率（coverage）</h2>
<p>在<code>Pycharm</code>里可以通用右键项目，选择<code>Run 'Test:' with Coverage</code>来查看测试的覆盖率。也可以通过其它第三方包查看测试覆盖率，具体请自己查询。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[在 macOS 上更改 Jenkins 的默认用户，解决权限问题]]></title>
        <id>https://me.iblogc.com/post/在macos上更改jenkins默认用户解决权限问题/</id>
        <link href="https://me.iblogc.com/post/在macos上更改jenkins默认用户解决权限问题/">
        </link>
        <updated>2017-08-24T06:29:14.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
在MacOS上使用`dmg`安装包安装完Jenkins之后，发了Jenkins自动在系统里新建了一个名为`jenkins`的用户。默认的，Jenkins程序里的自动化构建操作都是以这个用户身份来进行的，所以有时会出现一些权限问题，解决方法就是修改Jenkins配置文件，把Jenkins运行的默认账户改成平时用的账户。
<pre><code class="language-shell">#停止Jenkins
sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist

# 修改Group和User
# &lt;用户名&gt;填写你的MacOS用户名，不知道的可以在命令行使用whoami查看，不需要尖括号
$ sudo vim +1 +/daemon +’s/daemon/staff/’ +/daemon +’s/daemon/&lt;用户名&gt; +wq org.jenkins-ci.plist

# 可能相应文件夹的权限
sudo chown -R &lt;用户名&gt;:staff /Users/Shared/Jenkins/
sudo chown -R &lt;用户名&gt;:staff /var/log/jenkins/

# 启动Jenkins
sudo launchctl load /Library/LaunchDaemons/org.jenkins-ci.plist
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[内网穿透工具 frp 客户端自定义子域名访问配置]]></title>
        <id>https://me.iblogc.com/post/内网穿透工具frp客户端自定义子域名访问配置/</id>
        <link href="https://me.iblogc.com/post/内网穿透工具frp客户端自定义子域名访问配置/">
        </link>
        <updated>2017-08-16T08:36:56.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
## 前提
<p>A: 公网电脑<br>
B: 内网电脑</p>
<h2 id="下载">下载</h2>
<p>从[releases](<a href="https://github.com/fatedier/frp/releases">Releases · fatedier/frp · GitHub</a>)下载系统对应的压缩包，Mac可使用<code>darwin amd64</code>的包，在公网电脑和本地电脑各放一份。</p>
<h2 id="配置">配置</h2>
<p>公网电脑上<code>frps.ini</code></p>
<pre><code>[common]
# 用于接收 frpc 连接的端口
bind_port = 7000
# 通过此端口访问http服务
vhost_http_port = 8080
# 日志文件输出位置
log_file = ./frps.log
# 日志等级
log_level = info
# 域名
subdomain_host = example.com
# frp管理后台端口
dashboard_port = 7500
# frp管理后台用户名
dashboard_user = admin
# frp管理后台密码
dashboard_pwd = admin
</code></pre>
<p>本地电脑上<code>frpc.ini</code></p>
<pre><code>[common]
# 公网电脑IP
server_addr = 111.111.111.111
# frp连接的端口
server_port = 7000

[web]
type = http
# 本地http服务端口
local_port = 8080
# 子域名前缀, 子域名前缀里不要使用下划线&quot;_&quot;，不然可能会出现莫名其妙的400错误可以用&quot;-&quot;代替。
subdomain = iblogc
</code></pre>
<p>配置域名<code>example.com</code>的A记录的泛解析<br>
<code>*.example.com</code>指向公网电脑IP<code>111.111.111.111</code></p>
<h2 id="运行">运行</h2>
<ol>
<li>在内网电脑B上<code>8080</code>端口运行<code>http</code>服务</li>
<li>在公网电脑上运行（Windows电脑上运行请去掉<code>./</code>）</li>
</ol>
<pre><code>./frps -c ./frps.ini
</code></pre>
<ol start="3">
<li>在本地电脑上运行（Windows电脑上运行请去掉<code>./</code>）</li>
</ol>
<pre><code>./frpc -c ./frpc.ini
</code></pre>
<h2 id="成功">成功</h2>
<p>在任何一台能联网的机器上访问 <code>http://iblogc.example.com:8080</code> 即可访问内网电脑B上的http服务。<br>
在任务一台能联网的机器上访问<code>example.com:7500</code>即可访问frp的管理后台。</p>
<h2 id="frps服务端与nginx可共用80端口">frps服务端与nginx可共用80端口</h2>
<pre><code>server {
       listen 80;
       server_name *.example.com;
       location / {
           proxy_pass http://127.0.0.1:8080;
           proxy_redirect http://$host/ http://$http_host/;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header Host $host;
       }
}
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[MacOS 远程连接 Windows]]></title>
        <id>https://me.iblogc.com/post/mac远程连接windows/</id>
        <link href="https://me.iblogc.com/post/mac远程连接windows/">
        </link>
        <updated>2017-08-08T01:40:28.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
MacOS连接Windows时，除了在Windows上打开远程桌面功能外，还需要修改Windows的组策略才能成功连接。
<ol>
<li>开始-运行-gpedit.msc，进入组策略编辑器。</li>
<li>在左侧边栏中展开，计算机配置-管理模板-Windows组件-远程桌面服务-远程桌面会话主机-安全，修改以下两项。
<ul>
<li>远程（RDP）连接要求使用指定的安全层，改为启用，安全层选择RDP。</li>
<li>要求使用网络级别的身份验证对远程连接的用户进行身份验证，改为禁用。</li>
</ul>
</li>
<li>关闭组策略编辑器，重试远程，如果不行重启Windows再重试远程即可。</li>
</ol>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django 表单自定义验证]]></title>
        <id>https://me.iblogc.com/post/django-biao-dan-zi-ding-yi-yan-zheng/</id>
        <link href="https://me.iblogc.com/post/django-biao-dan-zi-ding-yi-yan-zheng/">
        </link>
        <updated>2017-07-23T10:29:11.000Z</updated>
        <content type="html"><![CDATA[<h3 id="model"><code>Model</code></h3>
<p>第一种</p>
<pre><code class="language-python">def validate_begins(value):
    if not value.startswith(u'new'):
        raise ValidationError(u'Must start with new')
 
 
class Post(models.Model):
    title = models.CharField('标题', max_length=100,
                             validators=[validate_begins])
    body = models.CharField('内容', max_length=1000)
</code></pre>
<p>第二种</p>
<pre><code class="language-python">class Post(models.Model):
    title = models.CharField('标题', max_length=100,
                             validators=[validate_begins])
    body = models.CharField('内容', max_length=1000)
 
    def clean_fields(self, exclude=None):
        # 验证模型的所有字段
        if exclude:
            # 禁用掉自定义的字段验证
            exclude.append('body')
        super(Post, self).clean_fields(exclude)
        if not self.title.startswith(u'new'):
            raise ValidationError({'title':u'Must start with new'})
</code></pre>
<p>第三种</p>
<pre><code class="language-python">class Post(models.Model):
    title = models.CharField('标题', max_length=100,
                             validators=[validate_begins])
    body = models.CharField('内容', max_length=1000)
 
    def clean(self):
        super(Post, self).clean()
        if not self.title.startswith(u'new'):
            raise ValidationError({'title':u'Must start with new'})
</code></pre>
<h3 id="form"><code>Form</code></h3>
<p>第四种</p>
<pre><code class="language-python">def validate_begins(value):
    if not value.startswith(u'new'):
        raise ValidationError(u'Must start with new')

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'body']
    def __init__(self, *args, **kwargs):
        super(PostForm, self).__init__(*args, **kwargs)
        self.fields[&quot;title&quot;].validators.append(validate_begins)
</code></pre>
<p>第五种</p>
<pre><code class="language-python">class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'body']
 
    def clean_title(self):
        title = self.cleaned['title']
        if not title.startswith(u'new'):
            raise ValidationError(u'Must start with new')
        return title
</code></pre>
<p>第六种</p>
<pre><code class="language-python">class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'body']
     
    # django1.7之前此方法必须返回一个cleaned_data字典，但现在可选
    def clean(self):
        cleaned_data = super(PostForm, self).clean()
        if not cleaned_data['title'].startswith('new'):
            raise forms.ValidationError({'title': u'Must start with new'})
</code></pre>
<p>第七种</p>
<pre><code class="language-python">def validate_begins(value):
    if not value.startswith(u'new'):
        raise ValidationError(u'Must start with new')

class PostForm(forms.ModelForm):
    author =  forms.CharField('标题', max_length=100,
                             validators=[validate_begins])
    class Meta:
        model = Post
        fields = ['title', 'body']
</code></pre>
<p>第八种</p>
<pre><code class="language-python">from django import forms
from django.core.validators import validate_email
 
class MultiEmailField(forms.Field):
    def to_python(self, value):
        &quot;Normalize data to a list of strings.&quot;
 
        # Return an empty list if no input was given.
        if not value:
            return []
        return value.split(',')
 
    def validate(self, value):
        &quot;Check if value consists only of valid emails.&quot;
 
        # Use the parent's handling of required fields, etc.
        super(MultiEmailField, self).validate(value)
 
        for email in value:
            validate_email(email)
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django Rest framework 入门笔记及跳坑记录]]></title>
        <id>https://me.iblogc.com/post/django-rest-framework入门笔记及跳坑记录/</id>
        <link href="https://me.iblogc.com/post/django-rest-framework入门笔记及跳坑记录/">
        </link>
        <updated>2016-12-17T07:03:10.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
## 更新记录
2016-01-26 初稿
<h2 id="序列化时嵌套显示外键关联字段">序列化时嵌套显示外键关联字段</h2>
<ul>
<li>
<p>自动<br>
使用<code>depth</code>参数指定外键深度</p>
</li>
<li>
<p>手动指定<br>
使用外键对应<code>model</code>的小写为属性，外键对应的<code>model</code>序列化程序为值<br>
以下例子在<code>HospitalPic</code>序列化结果里嵌套显示<code>Hospital</code><br>
models.py</p>
<pre><code class="language-python">from django.db import models

class Hospital(models.Model):
    name = models.CharField()

class HospitalPic(models.Model):
    hospital = models.ForeignKey(Hospital)
</code></pre>
</li>
</ul>
<p>serializers.py</p>
<pre><code class="language-python">from rest_framework import serializers

class HospitalSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Hospital 
        fields = '__all__'
</code></pre>
<p>class HospitalPicSerializer(serializers.HyperlinkedModelSerializer):<br>
hospital = HospitalSerializer()</p>
<pre><code>  class Meta:
      model = HospitalPic
      fields = '__all__'
</code></pre>
<pre><code>***反向关系嵌套***
在`Hospital`序列化结果里嵌套显示`HospitalPic`
serializers.py
```python
from rest_framework import serializers

class HospitalPicSerializer(serializers.HyperlinkedModelSerializer): 
    class Meta:
        model = HospitalPic
        fields = '__all__'


class HospitalSerializer(serializers.HyperlinkedModelSerializer):
    hospitalpic_set = HospitalPicSerializer(many=Ture)
    class Meta:
        model = Hospital
        fields = '__all__'
</code></pre>
<h2 id="在序列化对象里添加关联表的字段内容">在序列化对象里添加关联表的字段内容</h2>
<p>定义一个<code>serializer Field</code>，并添加参数<code>source</code>指向外键对对应的字段（<code>source</code>值其实是从当前序列化的实例的属性）</p>
<pre><code class="language-python">my_address= serializers.ReadOnlyField(source='address.full_address')
</code></pre>
<h2 id="在序列化对象里添加自定义内容">在序列化对象里添加自定义内容</h2>
<pre><code class="language-python">from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers
 
class UserSerializer(serializers.ModelSerializer):
    days_since_joined = serializers.SerializerMethodField()
 
    class Meta:
        model = User
 
    def get_days_since_joined(self, obj):
        return (now() - obj.date_joined).days
</code></pre>
<h2 id="使用viewset并不有设置queryset而是重写了get_queryset时需要在router里增加base_name参数base_name为router为viewset注册url时自动添加的name前缀如果未设置则从viewset的queryset里取使用viewset自动生成的url-name为base_name-list-base_name-detail-等">使用<code>ViewSet</code>，并不有设置<code>queryset</code>，而是重写了<code>get_queryset</code>时，需要在<code>router</code>里增加<code>base_name</code>参数（<code>base_name</code>为<code>router</code>为<code>ViewSet</code>注册url时自动添加的name前缀，如果未设置则从<code>ViewSet</code>的<code>queryset</code>里取，使用<code>ViewSet</code>自动生成的url name为&lt;base_name&gt;-list &lt;base_name&gt;-detail 等）</h2>
<p>views.py</p>
<pre><code class="language-python">class ContactViewSet(viewsets.ModelViewSet):
    serializer_class = ContactSerializer
    permission_classes = (permissions.IsAuthenticated,)
 
    def get_queryset(self):
        return self.request.user.contact_set.all()
</code></pre>
<p>urls.py</p>
<pre><code class="language-python">router.register(r'contact', ContactViewSet, base_name='contact')
</code></pre>
<p>未设置<code>base_name</code>会报下面错误</p>
<pre><code>'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.
</code></pre>
<h2 id="给api接口的url添加了命名空间namespace">给api接口的url添加了命名空间<code>namespace</code></h2>
<p>urls.py</p>
<pre><code class="language-python">url(r'^api/', include(router.urls, namespace='api')),
</code></pre>
<p>需要对<code>HyperlinkedRelatedField</code>字段的参数进行修改<br>
serializers.py</p>
<pre><code class="language-python">class HospitalPicSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = HospitalPic
        fields = '__all__'
        extra_kwargs = {
            'url': {'view_name': 'api:hospitalpic-detail'},
            'hospital': {'view_name': 'api:hospital-detail'}
        }
</code></pre>
<p>不然会出现以下错误</p>
<pre><code class="language-python">Could not resolve URL for hyperlinked relationship using view name &quot;user-detail&quot;. You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
</code></pre>
<p>不过话说我们全api的url加<code>namespace</code>一般是为了版本控制，所以有一种简单的方法,只要在settings.py添加基于<code>namespace</code>的版本控制，这样就不需要修改<code>HyperlinkedRelatedField</code>字段的<code>view_name</code>了<br>
urls.py</p>
<pre><code class="language-python">url(r'^api/v1/', include(router.urls, namespace='v1')),
url(r'^api/v2/', include(router.urls, namespace='v2')),
</code></pre>
<p>settings.py</p>
<pre><code class="language-python">REST_FRAMEWORK = {
    ……
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
    ……
}
</code></pre>
<h2 id="要drf的错误提示为中文需要设置">要drf的错误提示为中文，需要设置</h2>
<pre><code class="language-python">LANGUAGE_CODE = 'zh-CN'
</code></pre>
<p>如果设置为</p>
<pre><code class="language-python">LANGUAGE_CODE = 'zh-Hans'
</code></pre>
<p>虽然django默认表单错误会输出中文，但drf还是输出英文</p>
<h2 id="django的validators可以直接在drf中使用不需要做任何修改">django的<code>validators</code>可以直接在drf中使用，不需要做任何修改</h2>
<h2 id="当字段里的属性editablefalse时modelserializer里该字段会抛弃model里显式和隐式unique的所有validators">当字段里的属性<code>editable=False</code>时，<code>ModelSerializer</code>里该字段会抛弃<code>model</code>里显式和隐式（unique）的所有<code>validators</code></h2>
<h2 id="serializer里write_only写在field里和写在extra_kwargs里是有区别的"><code>Serializer</code>里<code>write_only</code>写在<code>field</code>里和写在<code>extra_kwargs</code>里是有区别的，</h2>
<pre><code class="language-python">class UserRegisterSerializer(serializers.ModelSerializer):
    &quot;&quot;&quot;用户注册Serializer&quot;&quot;&quot;
 
    code = serializers.CharField(min_length=4, max_length=6, label=_('验证码'),
                                 help_text=_('验证码'), write_only=True)
    re_password = serializers.CharField(label=_('重复密码'), help_text=_('重复密码'),
                                        validators=validators.password_validators(),
                                        write_only=True)
 
    class Meta:
        model = User
        fields = ('mobile_phone', 'code', 'password', 're_password')
        extra_kwargs = {'password':
                            {'write_only': True}
                        }
 
    def validate(self, attrs):
        &quot;&quot;&quot;
        Check that the start is before the stop.
        &quot;&quot;&quot;
        if attrs['password'] != attrs['re_password']:
            raise serializers.ValidationError(_('密码不一致'))
 
        # 校验验证码
        verify_result = Sms(attrs['mobile_phone']).verify_sms_code(
            attrs.pop('code'))
        if not verify_result:
            error = verify_result.get('error')
            raise ParseError(error)
        return attrs
 
    def create(self, validated_data):
        user = User(
            username=validated_data['mobile_phone'],
            mobile_phone=validated_data['mobile_phone'],
        )
        user.set_password(validated_data['password'])
        user.save()
        return user
</code></pre>
<p>因为<code>create()</code>这个方法return了一个<code>user</code>实例，<code>User</code>里没有的字段<code>code</code>和<code>re_password</code>需要将<code>write_only </code>写在<code>field</code>参数里，不然会报以下错误</p>
<pre><code>AttributeError: Got AttributeError when attempting to get a value for field `code` on serializer `UserRegisterSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'code'.
</code></pre>
<h2 id="如果使用django-rest-swagger报以下错误">如果使用<code>django-rest-swagger</code>报以下错误</h2>
<pre><code>Can't read from server. It may not have the appropriate access-control-origin settings.
</code></pre>
<p>注释掉设置里的</p>
<pre><code class="language-python">    # 'base_path': '127.0.0.1:8000/docs',
</code></pre>
<h2 id="serializerdata和serializervalidated_data"><code>serializer.data</code>和<code>serializer.validated_data</code></h2>
<p>在<code>serializer</code>只使用<code>data</code>参数实例化的时：</p>
<ul>
<li><code>serializer.data</code>是原始数据（字符串），<code>serializer.validated_data</code>是进行数据验证并转换成对应数据类型的数据。</li>
<li>两者者必须在<code>serializer</code>调用<code>is_valid</code>方法后才能调用<br>
在<code>serializer</code>只使用<code>instance</code>参数实例化时：</li>
<li>只有<code>serializer.data</code>没有<code>serializer.validated_data</code>，并且<code>serializer.data</code>里的数据也是字符串；</li>
<li>没有方法<code>is_valid</code>；</li>
<li>即<code>is_valid</code>和<code>validated_data</code>只在有data参数实例化时才可调用；</li>
</ul>
<h2 id="在serializer里获取原始请求信息">在<code>serializer</code>里获取原始请求信息</h2>
<p>默认的，上下文信息会被传递到<code>serializer</code>里，所以在<code>serializer</code>可以直接使用<code>self.context['request']</code>来获取请求信息。（在要继承自<code>viewsets.GenericViewSet</code>的类里使用的<code>serializer</code>才能取到，如果是继承<code>APIView</code>的，自己传入即可<code>serializer = self.serializer_class(data=request.data, context={'request': request})</code>）</p>
<h2 id="自定义serializer字段">自定义<code>serializer</code>字段</h2>
<p>自定义字段继承<code>serializers.Field</code>，<code>to_representation</code>方法处理出来的数据用来序列化显示，<code>to_internal_value</code>处理接收到的数据，<code>get_attribute</code>方法指定这个字段访问的实例属性，<code>get_value</code>方法指定</p>
<pre><code class="language-python">class QiNiuField(serializers.Field):
    def get_attribute(self, instance):
        # （序列化时）从模型实例中取一个值给这个字段处理,也可以使用`source`参数指定
        return instance.key
    
    def get_value(self, dictionary):
        # （反序列化时）从传入数据中提取一个值给这个字段处理
        return super(QiNiuField, self).get_value(dictionary)

    def to_representation(self, value):
        # （序列化时）处理出来的数据用来序列化显示
        return value.url

    def to_internal_value(self, data):
        # （反序列化时）处理接收到的数据
        return data['key']
</code></pre>
<h2 id="嵌套序列化传参问题">嵌套序列化，传参问题</h2>
<p>官方文档中有这么一个例子<a href="http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects">Dealing with nested objects</a><br>
如果是以<code>Content-Type:application/json</code>形式传数据格式传数据，直接嵌套传就可以了<code>{'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}</code>，但如果是以,<br>
但是如果以<code>Content-Type:form-data</code>或<code>Content-Type:x-www-form-urlencoded</code>上传，则上传<code>user</code>信息进不是嵌套，而是就<code>.</code>连接了，<code>&quot;user.email&quot;:&quot;foobar&quot;</code>.</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django Rest framework 使用问题及解决方法]]></title>
        <id>https://me.iblogc.com/post/django-rest-framework使用问题及解决方法/</id>
        <link href="https://me.iblogc.com/post/django-rest-framework使用问题及解决方法/">
        </link>
        <updated>2016-12-17T06:58:04.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
<h2 id="更新记录">更新记录</h2>
<p>2016-01-29 初稿</p>
<h2 id="问题1">问题1</h2>
<p><code>ViewSet</code>没有写<code>serializer_class</code>属性，而是重写了<code>get_serializer_class()</code>方法，出现</p>
<pre><code>Cannot use OrderingFilter on a view which does not have either a 'serializer_class' or 'ordering_fields' attribute.
</code></pre>
<p>原因：因为启用了<code>rest_framework.filters.OrderingFilter</code>而没有设置<code>ordering_fields</code><br>
解决方法：<code>ViewSet</code>里加<code>ordering_fields</code>属性，可是禁用<code>rest_framework.filters.OrderingFilter</code></p>
<h2 id="问题2">问题2</h2>
<p><code>ViewSet</code>没有写<code>queryset</code>属性，而是重写了<code>get_queryset()</code>方法，出现</p>
<pre><code>'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.
</code></pre>
<p>解决方法：需要在<code>urls.py</code>里给<code>ViewSet</code>注册<code>Router</code>时添加<code>base_name</code>（<code>base_name</code>为<code>router</code>为<code>ViewSet</code>注册url时自动添加的name前缀，如果未设置则从<code>ViewSet</code>的<code>queryset</code>里取，使用<code>ViewSet</code>自动生成的url name为&lt;base_name&gt;-list &lt;base_name&gt;-detail 等）<br>
urls.py</p>
<pre><code>router.register(r'users', UserViewSet, base_name='user')
</code></pre>
<h2 id="问题3">问题3</h2>
<p>给url设置了<code>namespace</code><br>
urls.py</p>
<pre><code class="language-python">url(r'^api/', include(router.urls, namespace='api')),
</code></pre>
<p>访问部分接口出现</p>
<pre><code>Could not resolve URL for hyperlinked relationship using view name &quot;user-detail&quot;. You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.
</code></pre>
<p>解决方法1：给所有的<code>serializer</code>里包含的外键字段手动设置<code>view_name</code>值（注意，继承<code>HyperlinkedModelSerializer </code>，会隐式添加一个<code>HyperlinkedRelatedField</code>字段<code>url</code>，而所有的外键都会变成<code>HyperlinkedRelatedField</code>字段，所以需要对两种类型字段手动设置<code>view_name</code>值）<br>
serializers.py</p>
<pre><code class="language-python">class ContactSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Contact
        fields = '__all__'
        extra_kwargs = {
            'url': {'view_name': 'api:contact-detail'},
            'user':{'view_name':'api:user-detail'}
        }  
</code></pre>
<p>解决方法2：启动drf基于<code>NameSpace</code>的版本控制<br>
settings.py</p>
<pre><code class="language-python">REST_FRAMEWORK = {
    ……
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
    ……
}
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django Rest framework 里的 API 请求频率控制]]></title>
        <id>https://me.iblogc.com/post/django-rest-framework里的api请求频率控制/</id>
        <link href="https://me.iblogc.com/post/django-rest-framework里的api请求频率控制/">
        </link>
        <updated>2016-12-17T06:48:19.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
<h2 id="更新记录">更新记录</h2>
<p>2016-08-25 初稿</p>
<p><code>Django Rest framework</code>有自带的频率控制配置</p>
<h2 id="全局设置">全局设置</h2>
<pre><code class="language-python">REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        # 开启匿名用户接口请求频率限制
        'rest_framework.throttling.AnonRateThrottle',
        # 开启授权用户接口请求频率限制
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        # 频率限制有second, minute, hour, day
        # 匿名用户请求频率
        'anon': '100/day',
        # 授权用户请求频率
        'user': '1000/day'
    }
}
</code></pre>
<h2 id="类视图单独配置">类视图单独配置</h2>
<pre><code class="language-python">from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView
 
class ExampleView(APIView):
    throttle_classes = (UserRateThrottle,)
 
    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)
</code></pre>
<h2 id="方法视图配置">方法视图配置</h2>
<pre><code class="language-python">@api_view(['GET'])
@throttle_classes([UserRateThrottle])
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)
</code></pre>
<h2 id="自定义">自定义</h2>
<p>方法一：</p>
<pre><code class="language-python">class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'
 
class SustainedRateThrottle(UserRateThrottle):
    scope = 'sustained'
...and the following settings.
</code></pre>
<p><code>settings.py</code></p>
<pre><code class="language-python">REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'example.throttles.BurstRateThrottle',
        'example.throttles.SustainedRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'burst': '60/min',
        'sustained': '1000/day'
    }
}
</code></pre>
<p>然后在视图里设置<code>throttle_classes</code>即可。</p>
<p>方法二：<br>
<code>settings.py</code></p>
<pre><code class="language-python">REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.ScopedRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'contacts': '1000/day',
        'uploads': '20/day'
    }
}
</code></pre>
<p>然后在类视图中设置<code>throttle_scope </code></p>
<pre><code class="language-python">class ContactListView(APIView):
    throttle_scope = 'contacts'
    ...
 
class ContactDetailView(APIView):
    throttle_scope = 'contacts'
    ...
 
class UploadView(APIView):
    throttle_scope = 'uploads'
    ...
</code></pre>
<p><strong>1. 匿名用户频率如果设置大于授权用户频率，则以授权用户频率为准。</strong><br>
<strong>2. 频率限制是针对单个接口的频率，而不是所有接口的频率。</strong></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[在 CentOS7 上用 MySQL+Nginx+Gunicorn+Supervisor 部署 Django]]></title>
        <id>https://me.iblogc.com/post/在centos7使用mysql-nginx-gunicorn+supervisor部署django/</id>
        <link href="https://me.iblogc.com/post/在centos7使用mysql-nginx-gunicorn+supervisor部署django/">
        </link>
        <updated>2016-12-08T15:19:59.000Z</updated>
        <summary type="html"><![CDATA[<p>本文记录下在CentOS下部署Django项目的步骤。</p>
]]></summary>
        <content type="html"><![CDATA[<p>本文记录下在CentOS下部署Django项目的步骤。</p>
<!--more-->
<h2 id="mysql">MySQL</h2>
<h3 id="安装mysql和mysql-devel">安装mysql和mysql-devel</h3>
<pre><code>yum install mysql
yum install mysql-devel
</code></pre>
<h3 id="安装mysql-server">安装mysql-server</h3>
<pre><code>wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
rpm -ivh mysql-community-release-el7-5.noarch.rpm
yum install mysql-community-server
</code></pre>
<h3 id="重启mysql服务">重启mysql服务</h3>
<pre><code>service mysqld restart
</code></pre>
<h3 id="设置root密码">设置root密码</h3>
<p>初次安装mysql需要设置root密码</p>
<pre><code>mysql -uroot
set password for 'root'@'localhost' =password('password');
</code></pre>
<h3 id="配置mysql">配置mysql</h3>
<p>在<code>/etc/my.cnf</code>文件中[mysql]和[mysql]中添加以下内容</p>
<pre><code>[mysql]
default-character-set=utf8

[mysqld]
character-set-server=utf8
</code></pre>
<p>字符编码保持和<code>/usr/share/mysql/charsets/Index.xml</code>中的一致。</p>
<h3 id="远程连接设置">远程连接设置</h3>
<p>把在所有数据库的所有表的所有权限赋值给位于所有IP地址的root用户。</p>
<pre><code>mysql&gt; grant all privileges on *.* to root@'%'identified by 'password';
</code></pre>
<p>如果是新用户而不是root，则要先新建用户</p>
<pre><code>mysql&gt;create user 'username'@'%' identified by 'password';
</code></pre>
<p>此时就可以进行远程连接了。</p>
<h2 id="virtualenv">Virtualenv</h2>
<p>安装epel扩展源</p>
<pre><code>yum install epel-release
</code></pre>
<p>安装pip</p>
<pre><code>yum install python-pip
</code></pre>
<p>安装virtualenv和virtualenvwrapper</p>
<pre><code>pip install virtualenv virtualenvwrapper
</code></pre>
<p>编辑<code>~/.bashrc</code>文件，结尾添加以下内容</p>
<pre><code>export WORKON_HOME=~/.virtualenvs
source /usr/bin/virtualenvwrapper.sh
</code></pre>
<p>然后执行以下命令使配置生效</p>
<pre><code>source ~/.bashrc
</code></pre>
<p>创建env</p>
<pre><code>mkvirtualenv explame
</code></pre>
<p>使用pip安装项目需要的包</p>
<h2 id="wsgi">WSGI</h2>
<p>在项目目录下新建<code>nginx_wsgi.py</code>文件</p>
<pre><code>touch nginx_wsgi.py
</code></pre>
<p>添加如下内容</p>
<pre><code>import sys
import site
import os
 
# site-packages
site.addsitedir('/home/nginxuser/.virtualenvs/example/lib/python2.7/site-packages')
# Add the  project  directory
# sys.path.append('/home/nginxuser/nginxuser')
PROJECT_DIR = '/home/nginxuser/projects/example'
sys.path.insert(0, PROJECT_DIR)
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings.prod'
# Activate your virtual env
activate_env = os.path.expanduser(&quot;/home/nginxuser/.virtualenvs/example/bin/activate_this.py&quot;)
execfile(activate_env, dict(__file__=activate_env))
 
# after activite env
from django.core.wsgi import get_wsgi_application
 
application = get_wsgi_application()
</code></pre>
<h2 id="nginx">Nginx</h2>
<h3 id="安装">安装</h3>
<pre><code>yum install nginx
</code></pre>
<h3 id="检查配置是否有错">检查配置是否有错</h3>
<pre><code>nginx -t -c /etc/nginx/nginx.conf
</code></pre>
<h3 id="启动nginx">启动nginx</h3>
<pre><code>service nginx start
</code></pre>
<h3 id="设置开机自启">设置开机自启</h3>
<pre><code>systemctl enable nginx
</code></pre>
<h3 id="创建用户">创建用户</h3>
<pre><code>useradd nginxuser
passwd nginxuser
</code></pre>
<h3 id="修改nginx主配置">修改nginx主配置</h3>
<pre><code>vim /etc/nginx/nginx.conf
</code></pre>
<p>非注释首行</p>
<pre><code>user nginx
</code></pre>
<p>改为</p>
<pre><code>user nginxuser
</code></pre>
<p>不然可能会出现网站静态文件访问报403问题。</p>
<h3 id="新建网站运行配置">新建网站运行配置</h3>
<pre><code>vim /etc/nginx/conf.d/example.conf
</code></pre>
<pre><code>server {                                                               
    listen      80;                                                    
    server_name example.com;                            
    charset     utf-8;                                                 
    client_max_body_size 75M;                                          
    access_log /home/nginxuser/projects/example/nginxlogs/access.log;
    error_log /home/nginxuser/projects/example/nginxlogs/error.log;          
 
    location /static {                                                 
        alias /home/nginxuser/projects/explame/static;                
    }                                                                  
 
    location / {                                                       
        proxy_pass http://127.0.0.1:8000;                              
        proxy_set_header Host $host;                                   
        proxy_set_header X-Real-IP $remote_addr;                       
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   
    }                                                                  
}                                                                       jk
</code></pre>
<h2 id="gunicorn">Gunicorn</h2>
<h3 id="安装-2">安装</h3>
<pre><code>pip install gunicorn
</code></pre>
<p>项目根目录下添加gunicorn运行配置文件gunicorn.conf.py</p>
<pre><code>import multiprocessing
bind = &quot;127.0.0.1:8000&quot;
workers = 2
errorlog = &quot;/home/nginxuser/example/gunicorn.error.log&quot;
#loglevel = &quot;debug&quot;
proc_name = &quot;gunicorn_example&quot;
</code></pre>
<h3 id="启动">启动</h3>
<pre><code>sudo gunicorn example.nginx_wsgi:application -c /home/nginxuser/projects/example/gunicorn.conf.py
</code></pre>
<p>后台运行</p>
<pre><code>sudo nohup gunicorn example.nginx_wsgi:application -c /home/nginxuser/projects/example/gunicorn.conf.py&amp;
</code></pre>
<p>如果运行报错先使用以下命令检查下nginx配置是否有错</p>
<pre><code>nginx -t -c /etc/nginx/nginx.conf
</code></pre>
<h2 id="supervisor">Supervisor</h2>
<h3 id="安装-3">安装</h3>
<pre><code>pip install supervisor
</code></pre>
<h3 id="创建管理进程配置文件">创建管理进程配置文件</h3>
<pre><code>vim /etc/supervisord.d/example.ini
</code></pre>
<p>（需要注意：用 supervisord 管理时，gunicorn 的 daemon 选项需要设置为 False）</p>
<pre><code>[program:example]
directory = /home/nginxuser/projects/example ; 程序的启动目录
command = gunicorn example.nginx_wsgi:application -c /home/nginxuser/projects/example/gunicorn.conf.py  ; 启动命令，可以看出与手动在命令行启动的命令是一样的
autostart = true     ; 在 supervisord 启动的时候也自动启动
startsecs = 5        ; 启动 5 秒后没有异常退出，就当作已经正常启动了
autorestart = true   ; 程序异常退出后自动重启
startretries = 3     ; 启动失败自动重试次数，默认是 3
user = nginx         ; 用哪个用户启动
redirect_stderr = true  ; 把 stderr 重定向到 stdout，默认 false
stdout_logfile_maxbytes = 20MB  ; stdout 日志文件大小，默认 50MB
stdout_logfile_backups = 20     ; stdout 日志文件备份数
; stdout 日志文件，需要注意当指定目录不存在时无法正常启动，所以需要手动创建目录（supervisord 会自动创建日志文件）
stdout_logfile = /data/logs/usercenter_stdout.log

; 可以通过 environment 来添加需要的环境变量，一种常见的用法是修改 PYTHONPATH
; environment=PYTHONPATH=$PYTHONPATH:/path/to/somewhere
</code></pre>
<p><strong>冒号后面要有空格</strong></p>
<h3 id="启动-2">启动</h3>
<p>使用<code>-c</code>指定配置文件。</p>
<pre><code>supervisord -c /etc/supervisord.conf
</code></pre>
<p>如果启动时遇到以下报错信息</p>
<pre><code>Error: Another program is already listening on a port that one of our HTTP servers is configured to use. Shut this program down first before starting supervisord.
For help, use /use/bin/supervisord -h
</code></pre>
<p>可以使用以下命令解决</p>
<pre><code>sudo unlink /var/run/supervisor/supervisor.sock
</code></pre>
<h3 id="命令行客户端工具supervisorctl">命令行客户端工具supervisorctl</h3>
<p>启动时需要使用和<code>supervisorctl</code>使用一样的配置文件。</p>
<pre><code>supervisorctl -c /etc/supervisord.conf
</code></pre>
<p>启动后进入<code>supervisorctl</code>的shell，在此shell里可以执行以下命令</p>
<pre><code>status # 查看程序状态
start example # 启动example程序
stop example # 关闭example程序
restart example # 重启example程序
reread # 读取有更新（增加）的配置文件，不会启动新添加的程序
update # 重启配置文件修改过的程序
</code></pre>
<p>也可以不进shell执行以上命令</p>
<pre><code>supervisorctl status # 查看程序状态
supervisorctl start example # 启动example程序
supervisorctl stop example # 关闭example程序
supervisorctl restart example # 重启example程序
supervisorctl reread # 读取有更新（增加）的配置文件，不会启动新添加的程序
supervisorctl update # 重启配置文件修改过的程序
</code></pre>
<h3 id="开启web管理界面">开启web管理界面</h3>
<p>如果要开启web管理界面，打开<code>/etc/supervisord.conf</code>把下面几行取消注释即可</p>
<pre><code>:[inet_http_server]         ; inet (TCP) server disabled by default
:port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
:username=user              ; (default is no username (open server))
:password=123               ; (default is no password (open server))
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[杭电记忆---（献给母校-杭州电子工业学院）]]></title>
        <id>https://me.iblogc.com/post/hang-dian-ji-yi/</id>
        <link href="https://me.iblogc.com/post/hang-dian-ji-yi/">
        </link>
        <updated>2016-10-12T06:10:24.000Z</updated>
        <content type="html"><![CDATA[<blockquote>
<p>本文系转载 <a href="https://www.cnblogs.com/qiujianben/articles/104251.html">原文地址</a><br>
背景：我是 2012 届毕业的，大学前两学期（2008-2009/2009-2010）是在文一校区度过的，在网上偶然间看到这遍文章很有感触，就想把这篇回忆永久保存。当然我就读时文一校区叫：杭州电子科技大学信息工程学院。</p>
</blockquote>
<p>“文一路多熟悉啊，我的大学就在那里，旁边还有好多大学，不过现在搬了，有机会要回去看看”<br>
———郑钧<br>
以我的学长郑钧这段话开始我对杭电特别是文一路校区一些琐碎的回忆，权当给后来者更或说是自己留些关于老校区的声色影像吧！<br>
杭州，西湖区，文一路，杭州电子工业学院。<br>
文一路相当繁华，特别是对学生来说，有数不尽的小店。吃的比较出名的有：知味观——杭电校外食堂；太一——以廉价出名，是男人拼酒的首选；一品——冬天最爱，骨头砂锅相当不错；此外，教工路上雅兔对面的衢州三头够辣，财经东方分部门口的山东升风水饺很不错，上过快报推荐的。买衣服的话，以我爱我家为界，西边都是专卖店，东边则都是个性小店，大都挂外贸服饰的招牌。其他店家，东方对面有个藏银，据说东西都是西藏过来的，是否真实就不得而知了；杭师门口的游戏人间总卖一些新奇玩意，仔细淘的话，有不少好东东；再过去快乐小站，是个水吧，也是我们的一大据点，东西一般，但正对杭师院大门，坐靠窗位置，可阅美女无数；网吧，教工路上风影看电影的多，雅兔则是游戏天堂，杭电人相当的多；书店则首推江郎，很多学生自己去那里买教材的，可以打折的。<br>
杭电两侧各有一个大超市，翠苑的物美感觉东西多些，而且楼上就是翠苑电影大世界，外围全是座位，可以在物美或者 kfc 买好吃的，在楼上和 mm 坐一个下午，风景不错；东边的华润是今年新开的，去的不多，总感觉东西偏少，而且学生顾客没有物美多；说到超市，其实最喜欢的还是黄龙好又多，很多人宁可多跑路，也要去好又多的，虽然只有一层，但感觉就比较好，而且很多人喜欢那里的电炉烤鸡。小超市，最爱是可的，以前是在杭电诊所楼下的，经常有人半夜跑出去买东西吃，后来文一路整顿，搬了，附近就没什么好的地方了，现在的店都是后来开的。<br>
学校四周，比较有印象的还有：京典——洗头比理发好，和 11 路站的阿俊、杭师对面的千丝万缕算是这一带比较出名的；一家小店，在工程门口，现在叫不上名字了，家常菜做的不错，关键是价廉量足，非典过后那段时间每天吃饭都要排队的；天马——以前是网吧老大，后来没落，主要经营歌厅和陶吧；撞击酒吧——学校外教最喜欢去的地方，经常有这方面的活动；文二夜市——杭电人逛的最多的地方，淘碟、点卡、明星壁画还有其他小玩意；文二电影院——平时几乎都是情侣专场，只有有球赛的日子才有上座率，00 欧锦赛，02 世界杯，大家很多时候在这里度过。<br>
公交车，最熟悉的自然是 11 路，到保俶路站转 328 去下沙，到武林广场站去银泰，终点站城站回家，到现在我到杭州也总是要坐坐 11 路；58 路，是个环线，可以到杭大、湖墅电子市场、武林广场等，我在杭大学法语时天天坐这班；155 是到延安路的，25 到吴山，816 到舟山东路，等等。其他就记不大清了，不过有一点特别，杭电门口的站叫省委党校，而文一和教工路口站才叫电子学院。</p>
<p>外面绕了一大圈，回到杭电。学校大门最大的特点就是那棵老树，估计也是有点年头的，正说明了杭电校园的特点：绿化好；校名写的很小，不注意还发现不了，经常有女生整个寝室来拍照片的；门口门卫都很年轻，离校前在草坪聚会时，找他们吹过瓶子；旁边是邮递房，新生时信很多，越大就越懒了，记得我们的邮箱是 233；进校后的大草坪，是杭电一大聚会地点，迎新晚会、班级聚会、老乡聚会很多在这里，右边有几棵树，形成一个隐秘的小空间，情侣很多，记得吃完散伙饭那天我们霸占了草坪，赶走了 n 多情侣，呵呵。<br>
正对校门是实验楼，也是杭电最宏伟的建筑，好象是 9 层还是 10 层吧。每两个分院共用一层，各分院的办公室、实验室还有机房。我们财经是和管理一起在四层，以前三个分院的时候我们是一起的，叫二分院（工商管理），辩论超强，出过不少牛人。现在的财经是杭电第一大院，以人数称雄，体育也很强。实验楼有个很大的好处，夏天很凉快，所以不少学生晚上睡这里的，理由当然很多，有的是做实验，有的是看门，不一而足。这里的另一特色是电梯超慢，人狂多，经常等电梯还没走路快。二楼是文理分院，可能是大家除了本分院外最常去的地方，因为英语和数学教研室在这里，所以大英、高数、线代这些公共基础课的重修补考费就是这里缴的，心痛啊。里面有个老师是杭电第一名捕，常监考数学，捕人无数，我亲眼看到过的就不下二十来人。</p>
<p>实验楼东侧，是图书馆，藏书好像还可以吧，算不上多，也不会太少，就是旧了点。借书很有意思，正门很少人走的，都从旁边楼梯直接上三楼侧门，脱鞋进去后，里面就是夏天也是感觉冷的。借书是有限制的，我记得是文艺类一次只能借两本吧，超期要罚的，我弄丢了《欧美音乐赏析》，超期加赔花了 100 多。一楼自由阅览室是考研基地，位置永远紧俏的，经常晚上和早上晨跑时就有人抢了。在这里你看到的是杭电最努力最用功的一批人，至于成效如何，我们不得而知。而杭电最精英的人，此刻要么在实验室里埋头研究，要么就在寝室里砍 cs、星际，再或者就是觥筹交措、呼朋唤友中，总之，各有各的精彩。二楼和四楼记得都有期刊阅览室，这里是写论文的好地方，所以大四学生居多。<br>
图书馆后是花圃，还有一条越来越干净的西溪河，这里也是情侣的天堂，美中不足的是这里有住校工，而且有养狗。河拐出去的地方是东门，是后来新开的，这里管的比较松，24 小时可以进出。东门进来是杭电招待所，比较一般，好像是 150 标间，非典时外地回来学生在这里隔离，只要交每天 10 块钱伙食费就可以待一周，吃的不错，还有冠军杯看，羡慕。学校的宴席大都在这里摆，我吃过几次，味道一般。对面是以前的研究生楼，房间不大，两人一间吧，带厨房，感觉太暗，有些潮。一栋新楼是教师宿舍，学校老师基本上是住翠苑和文二的，这里住的大都是单身的还有外教，我们外教还养过小狗，非常可爱。楼侧面的墙是练习网球的好地方。<br>
这一段绿化特别的好，绿树丛中，还有个小院，不大容易发现，这里是 6 号楼，最早是诊所兼教师宿舍，后来住成教，再后来人越来越多了，就住的是千届学生，我记得是 2000 工管的，回字型的房子，男女生分住两头，我们曾经猜测过是否有人可以趁管理员不注意溜到对面去，无从考究。这里管理员好像对外来的不大友善，很多人就在院子里直接叫人，不过容易暴露身份。反正我是很不喜欢这个楼，从来只打电话的。<br>
再往里走，是两栋教学楼，一曰东教，一曰新东教，这也是杭电一大笑话，造新东教时学校还征集过楼名，记得还有不少人投了，结果最后命名新东教，举校倾倒。先说东教，杭电老建筑，也算得上雕梁画栋了，以前杭电的明信片上有图。木地板、木桌、铁椅，大而空，感觉就是两个字：阴冷，据说闹鬼，以前有人上吊过的，晚上很少人自习的。这里每层都有大教室，所以很多哲学这种公共课在这里上的，很多辩论赛也是在这里搞的，我在这里作过很多次的 dj。杭电的大教室有个共同点，就是后面的高一级，所以在两段高低上的位置是考试最佳，你视力好的话前面一览无余。顺便说一句，东教对面是杭商的女生宿舍，有时候能和她们聊上几句。</p>
<p>说起新东教，话题可就多了。这里最早是杭电花圃，有一条长廊，据说是爬满葡萄藤的，是杭电最出名的情人长廊，一到了晚上，非常……，演绎了很多杭电浪漫经典。好像说有些坏小子会偷偷跑去捣乱，呵呵，杭电毕竟还是光棍多啊。很可惜我们 99 届进校后，花圃就拆了盖楼，不过到现在我还记得那时我们的美女辅导员带我们去看长廊，讲过去的往事。变成新东教后，学校还很搞笑的在旁边原地建了一个亭子，种上了竹子什么的，可是亭子正对教室，恐怕没有人愿意在众目睽睽下卿卿我我吧。新东的教室，4 和 6 是大教室，很大的那种，好像所有分院都是在这里开年级会议的，我们 99 财经都是在 306。这里还是考试集中的地方，凡是两个班以上的大课，大都在这里上和考试，四级也是在这里，六级则在新教。新东最好的一点是阳光充足，特别是 4 和 6 的大教室，坐在后排都沐浴在和煦的阳光中，所以，嘿嘿，这里也是要抢的，好像有很多次前面五六排几乎没人。新东一个特点是逃课很难，想偷偷溜走不被发现，非常难，不过高手还是很多，羡慕！在这里上过的课中印象最深的是大二时候的美术欣赏，上的人很多，而且以男生居多，知道原因了吧，因为传说中教这门的 miss l 是个美女，所以很多可以说是完全没有艺术细胞的人也挤来凑热闹。你如果要问，传说是否属实，那就到杭电上美术欣赏吧，不过她挺凶的。新东四楼比较偏僻，上课自习的人不多，也是个小小的禁区吧，因为经常一个教室里就两个人，*#•￥ （以下省略 100 字），顶上好像是个天台，以前情侣很多的，不过出过一次事后就人少了，去年吧，有人从上面摔下来死了，原因众说不一。<br>
新东前面，是杭电一个好地方，是一大块草坪，完全沐浴在阳光中，冬天在这里聊天嗑瓜子打牌的很多，看报纸睡觉的也很多，我经常和同学在这里背书，要考试了。旁边则是中心花园，杭电喜欢用方位来命名地点。这个花园两部分，靠行政楼是草坪，也是冬天背书的好地方，不过我们一直奢望有天能在上面踢场球，这里最有特色是有棵孤零零的小树，什么树我忘了，可能是柳树一类吧，就这么一棵，非常滑稽，这边也是拍照好地方。靠实验楼一边比较乱，有亭子假山，这里是我们背书打牌的地方；有小林子，情侣的地盘；有一排石桌，这里是早上起来背英语人的。这个花园里好像很多乱七八糟的植物，名字都怪怪的，记得我们英语听说课，有次老外就要我们写中心花园，晕，那些名字看了就头痛哦。<br>
花园旁一条路兼是停车场，好车不多，都是老师的吧。学生，我知道的不多，只是听说自动化有个家伙开别克赛欧的。<br>
路的尽头是工字楼，我以前文章提过，是我们的老巢，住了三年。结果大家都住出感情来了，好处很多：位置优越，四通八达，吃饭打水踢球上课出门都很近；独门独院，没有人和声音骚扰；有自己独立的停车棚和晒衣场，不用和别人挤；房间小些，但只住六人，舒服多了；没有公共电扇，可以通宵供电…… 所以大四搬走后，经常夜里和凌晨会听到有人高叫：“打回工字楼，还我工字楼”可见怀念之深，后来这里成了研究生楼，郁闷！工字楼晚是要锁门的，以前还可以翻窗，后来封了，就只能叫门了，看夜的是个老头子，反应比较慢，有次我们一个兄弟喝高了，等久了，叫把玻璃给砸了，生猛啊，结果后来入不成党，可惜啊！杭电规矩是男生不得入女生楼，女生进男生宿舍呢，则要登记，还隔会就打传呼来催，工字楼好的是后门比较好进，所以常常有人在门口掩护，女生悄悄溜进来。不过有几个家伙和管理员混熟了，女朋友可以随意进出的，所以有时会遇上尴尬，大家都知道男生在寝室穿着是怎么的咯！我们班住在二楼的最东头，也是最热闹的地段，晚上熄灯后好多人聚在门口，打双扣的，看小说的，聊天的，吃东西的，比熄灯前还热闹，经常还有人会提议去 “调戏” 某人，于是，惨叫声起，一出悲剧在上演。<br>
从我们工南窗口望出去，是新教，这是在新东教之前东西教之后盖的，故曰新教楼。不过也很有点旧了，前年考试前，我们在寝室里复习时，就是轰的一声，四楼的楼梯断了，幸好无人受伤。新教是老生最爱，98 和 99 的喜欢在这里自习，千届则大都在新东教，还有些勇敢的则在东西教。新教单数是大教室，逃课最好的战场，双数是小教室，大部分小课在这里上。这里给我印象最深的是徐旭初老师的课，可能很多杭电人都应该知道的，他也算是杭电最受欢迎的老师吧，据说是出身于高官家庭。本人很有些才华，颇有些读书人的风骨。他的讲座在杭电也是很受欢迎的，我还曾经请他到学校电台作过几次节目，聊的还是很投机的。此外，出身社科部的几位如钱升老师、郑建功老师等在学校也是比较受欢迎的，或严谨，或风趣，个人认为是为杭电读书人之望。还有一位老夫子，是曾经教我们英语的，张唯新老师，一看就是那种老底子的人，每次看到他我就会想到上海的老克腊，虽然和他打的交道不多，但心里还是很敬佩他的。<br>
新教两侧就是前面提到过的行政楼和西教楼。行政楼和我们学生联系不大，我只是因为广播台的关系常去宣传部，也没什么特别的地方，顶楼会议室常有些招聘会什么的，学校派出所也在这里，打证明什么的要到这里。西教故事和东教差不多。过去一点还有学校的印刷厂，服务态度不好。<br>
再过去就是南门了，也就是学校的后门，好像很多人把学校后门外叫堕落街，可能也是有点吧。南门出去，有几栋居民楼，好像有些房子出租的，不过很紧俏，很难租到；临街门口有几个小摊，卖煎饼的，卖水果的，卖烧烤的，还有糯米团子，课间很多人出来解决早饭问题。往外走，是杭商东院，以前是中专，杭电没抢过杭商，结果让他们把学校伸到教工路东面来了，课间时候很多人进出，有不少美女。靠着杭商的门，一排依次是杭商招待所，房间很小，好像也是 150；速食店，开始主卖珍珠奶茶，后来卖炒粉、炒饭什么的，生意极其火暴，这里做的最好的是卤肉饭，5 元一份，浇上汤非常好吃，光我们班就平均每天有六七个人吃的，常年如此；一家超市，门口卖台湾香肠和肉饼，味道还可以；网吧，换了好几个老板，最后开网吧才稳定下来；街口是烧烤店，生意也很好，吃鱿鱼的人很多，卖的甘蔗不错。再出去就是文二路了，短短一段街上好几家中专大专学校，所以相应的店也很多，不过和文一路不同，这里大都是小店。<br>
再回到学校，南门口是跃进楼，成教的地盘，也是校学生会驻地，这里印象不深，很少有这里的课。靠着跃进楼是篮球场，自然是灌蓝高手们的天堂了，据我所知，杭电好像只有一个人能扣篮，是个体育老师。篮球场有三块，八个篮筐，杭电篮球自 98 届之后，就很少高手了，98 届打篮球很有几个出名人物的，不知道下沙那边水平如何？我只知道财经 01 届有几个特招生，所以女篮还打进过 cuba。旁边是排球场，学校打排球风气还是比较盛的，好像 97 时候水平不错，到了 99 就不行了，上次在杭电举行的浙江省大运会女排比赛，我们就打的很烂。说到排球，我们班排球很强，几次校内比赛，都成绩很好，也拿过冠军的。不过排球场和篮球场都经常被我抢来踢球的，永远怀念那些小场地比赛，衣服脱下一摆门，几个人一凑就开始了，还经常喜欢下雨天冒雨踢，真的特别痛快，那些岁月……<br>
再西边，就是足球场了。说实话，场地很烂，基本上没什么草，可每天都挤满了人，有时候抢场地还会吵起来甚至打架。通常整个场地里同时可以进行 10 几场比赛，纵横交错，合理利用每一寸土地，大概杭电的男生工程制图都学的很好吧，划分的很有效率。这里，是我们的 easy band，凝聚着我们太多的回忆，进球后的疯狂庆祝，赢球时的痛快高歌，丢球时的无奈沮丧，输掉比赛时的郁闷，甚至难过落泪，我们常为一个美妙的进球欢呼雀跃，紧紧拥抱在一起，也会因为一个低级的失误互相指责，总会有人站出来激励大家向前，所有一切都记录在这块小小的球场上，破旧的球门里，和我们每一个人的心里。不管你平时活跃或沉默，无论你球踢的好坏与否，足球给我们提供了一个舞台，宣泄心情的舞台，它实实在在给我们的生活带来了快乐。前段时间回学校去看看时，我特意拍了好多球场的照片，虽然看来有些单调，但何尝不是我们梦中的恋之风景呢！</p>
<p>球场的南边，是体育馆，建成后学校很多活动都在这里举行，女排比赛，冰红茶的校园歌手大赛，很多学术报告等，对了，健美课也是在这里上的，据说好像很好玩。西边是 8 号楼，00 届男生和 01 届成教先后住过，最早时候是租给外面的，后来学校人员紧张才收回的，这里一大好处是白天可以在窗口看不少比赛，坏处是晚上球场尽是情侣，对单身汉是极大的打击。北边，是原来的杭电俱乐部，早就出租给了证券公司做营业大厅，学校只有在大型活动时才租回来用，我印象里有两次，一次是我们入学时迎新大会，还有一次是杭电另一牛人——马云（可是上过 newsweekly 的主啊）回校作报告时，坐的很满，也很热情，不过报告完了提的问题实在有些逊，那天我是在第一排，是负责录音的，现在马云报告的磁带还在杭电广播台，只是不知搬迁后尚还在否？说起来，还可以自我吹捧一些，杭电我们所知两大风云人物郑钧是国贸的学长，而马 ceo 是国贸的老师，不知杭电下一个风云人物还会出在国贸吗？愿上苍保佑郑钧马云的后辈们，玩且笑之，拭目以待！<br>
在球场东南角，是浴室、理发室、水房。水房门口总是 n 多水瓶子，“勤劳的处女们——f4 美作语” 总是会打很多水，开始男生总在猜测哪里需要那么多水，据说有 n 多版本，后来传出来是因为女生不喜欢在浴室洗澡，呵呵，扯远了。她们（他们）为了防止被懒人顺手牵羊，于是形成了独特的热水瓶文化，瓶子上用记号笔写了许多让人忍俊不禁的话，来告诫偷水贼们，有的直接：住手，小偷；有的善良：水，你拿走，瓶子，请留下，谢谢；还有的恶毒，写满了诅咒。这大概也算是环境造就文化吧。理发室从没去过，只知道剪出来都差不多，不过理发室有个小孩子非常可爱，发型常变，世界杯时候留的就是 R9 的阿福头，呵呵，很想看他什么时候留上贝壳当年的莫西干 :-）浴室，应该每个经历过大学的人都知道，水龙头总是稀缺产品，都有过几个人共用一个的时候，相应的，就有人提早在开门前排队来抢得位置，也有人专门挑关门前半小时去洗，结果常常在本该拥挤的中间时间会很空，然后循环，颇有些敌进我退，敌退我进的游击战道理。而在这时候，人生一大乐事就不是什么金榜题名，洞房花烛，而是浴室逢战友了，碰到熟人，就意味着可以挤上一个水龙头了，哪管这个人也许才认识一天。浴室水很充足，这一点比在家里洗还舒服，但却要时刻忍受冷热交替的煎熬，时不时会听到一声惨叫，不是被烫着了就是冻着了。浴室最大的痛苦还不在于此，空气很不流通，经常会洗到一半觉得气闷，忍不住要晕倒，不过也有狠角色，我们寝室的毛比就经常一个澡能洗上两个小时，美其名曰效益最大化。<br>
离浴室最近的就是杭电的核心所在，1 号楼。之所以说是核心，当然不是指地理位置，这里僻居西北一隅，核心在于此乃女生楼所在，理工科学校的人该都知道学校里女生的珍贵程度，看看每天 1 号楼前络绎不绝的男生就有数了。1 号楼好像是 6 层吧，97 届在 2 层，99 届在 1 层、3 层和 5 层，其他不详。朝北寝室的特点是一年四季永远挂着的窗帘，还有每张床上的床罩，双重保护，好像也是挡不住对面 3 号楼如狼似虎的男生们，百多号望远镜天天盯着，总会有漏网之鱼的，于是…… 就在男生中传播。我也有个望远镜，当然地理位置始终不好，所以前三年不是外借就是闲置，直到大四搬到 4 号楼，才可以拿来看对面工程的模特美女们，可惜抢的人太多，哎，这班兄弟！记得以前我们杭电论坛 hder 上的一个兄弟拿数码相机从窗口拍对面一号楼的 mm 们然后发到网上，供饿狼们流口水，当然照片很健康，只是走在路上的场景而已，我还记得有一张是我们认为的杭电校花吴 mm 的，然后对她的讨论是论坛上最热的帖子，好像回复有几百条。呵呵，说起来，这位 mm 我们很多人曾经也是动过心的，不过据说她和男朋友是青梅竹马的，最后也就是曾经想想而已（其实最主要的原因是我和她们班一个 mm 谈过恋爱，所以…… 槽糕，暴露自己的本来面目了:-））。望远镜好像女生也有，只不过男生看女生，对方是躲，而女生看男生，是悉听尊便，男生总是要大方些的嘛。女生终究还是含蓄些的，不至于明目张胆的去偷窥，不过私底下的议论总是少不了的，我曾经听过一个女孩子录的她们寝室卧谈的内容，呵呵，很是有些…… 涉及隐私，我不该多说，女生们心里自然有数的。因为男生禁止入内，所以对大部分男生来说，他们大学里很大的一个遗憾，就是没进过女生楼。很不幸的，我进去过了，其实看门的大妈也是有人情味的，找好理由就成功了一半。我那时是放暑假时候，我 mm 和大妈很熟，跟她说我要帮她搬电视机，所以我就得到特许，抱着电视机上了二楼，当然时间不久，呵呵，那时候单纯，就是有不该看的也没有去看，其实也没什么特别的，只不过女生楼三个字特别而已。1 号楼，最热的当然是一楼口的传达室，这里的大妈也是杭电最忙碌的人们。楼前小小一块地方，不知留下了多少人的多少回忆，见面，接吻，吵架，哭泣，一幕幕似是而非的场景天天在上演，有的始终是两个人的游戏，有的年年岁岁花相近，岁岁年年人不同，或男主角，或女主角，变幻不停，爱情的故事永远不会终结。熄灯后，1 号楼前的小亭子里，总是会有人在，小两口恋恋不舍相伴到天明也是常有的事，然而我始终记得的是半夜独自哭泣的女孩，是为她的爱情，还是那曾经年轻的岁月。——思往事，惜流芳，易成伤，拟歌先敛，欲笑还颦，最断人肠。<br>
说到女生楼，自然离不开浪漫的爱情。记得以前在 hder，常有人问起杭电的浪漫经典，我所知道的不多，那时就发了个帖子——情已逝，杭电的风花雪月。提到有两件比较近的。一是我确实知道的，97 国贸的一个学姐，好像是国贸的系花哦，和男朋友闹别扭了，那位 gg 买了 999 朵玫瑰（那时侯可是 99 年），把整个寝室都堆满了；还有一件好像是有些久了，毕业的时候，好多的学长通宵坐在 1 号楼下，弹起吉他，为心中的那个她放声高歌，楼上的 jj 们也点歌、合唱，好多暗恋了四年的人们在那么多人前大声的说出了心中憋了四年的话，那一夜，杭电无眠。还有一件我无法证实，好像是有位 gg 用玫瑰在 1 号楼前摆出了 i love you，那位 jj 在全楼女生的鼓励下勇敢的冲出了 1 号楼，天下有情人终……。<br>
抛开风月，回到杭电。1 号楼对面是 3 号楼，以望远镜出名，而且好像卫生也是最差的，3 号楼的兄弟不要砍我。这里还有学校的西门，常年不开。1 号楼隔壁就是 2 号楼，这楼最出名的是有位自动化的 98 级兄弟从五楼跳下来，不过运气很差，挂住了树枝受了伤，后来如何就不得而知了。在 3 号楼隔壁是 4 号楼，我在那里住了一年，还是很有感情的。两楼中间是小超市，东西不多，我们常来买些瓜子、花生、锅巴、牛肉什么的，有时候有茶叶蛋，这里的肉粽不要买，因为要自己剥的，很粘手。超市门口有电话亭，好像是打 ic 的吧，大一时要排队，手机多了后就很空了。回过来讲 4 号楼，1 楼是宿管中心所在，我们闹保险丝的事情就是在这里，楼里的管理员陈老师很有意思，挺热情的，大四下整天在寝室里就混的熟了，经常喜欢和我们聊几句。我们是在 3 楼，我开始住南面，斜对面就看得到女生楼，所以寝室很热闹，没几天我就逃到对面住了，正对工程学院大门，每天见的最多的就是工程的美女和门口停的 benz、bmw，刺激加打击，经常有兄弟拿我望远镜看完美女们一个个上车后，大吼一声：“xxx，n 年后老子开劳斯莱斯在工程门口晃。” 后来窗前的树砍了，失去了掩护，经常有 mm 发现我们的偷窥行为，记得有次我和一个美女对视了足有一分钟，最后还是我落荒而逃，惭愧惭愧！临离校前几天，一个晚上，看到我们经常看到的一个模特美女，和另个 mm 在工程门口的角落里失声痛哭，虽然我们并不知她哭是为毕业抑或是为爱情，却不由得触动了我们脆弱的神经。此后几天，我和寝室的哥们每天都坐在窗台上，把自己电脑里的 mp3 全放了一遍，音量放到最大，我们也高声唱和，唱那逝去的年华。看着楼下来来往往的人们，思绪也特别的伤感。走了，都要走了，再回来不知是何时了，有些人也许此生都不会再见了，那些一起踢球的兄弟，那些一起上课的同学，那些一起生活四年的哥们。离别，在这个非典之后的夏天，预料中而又猝不及防的击中了我们，却又无药可医。散伙的饭一顿顿的吃，告别的酒一杯杯的喝，每天都有喝的烂醉抬回来的人，我也真切的看过有个哥们喝醉后泪流满面，听过他一遍遍的我舍不得兄弟们啊！ 泪水，就在此刻肆虐，无可救药。也许真的是我太脆弱，曾经我那么的盼望工作，我喜欢工作。可现在真要离开了，这却是怎样的一种告别啊！！也许你并不怀念具体的某个人，可是这段生活，这段岁月，是你所能忘怀的吗？这是我们最最美好的一段年华啊，你年少的笑的痛的泪的爱都留在了这里，这个也许并不出名并不韶华的地方，这是怎样的一种特别。离开了学校，也就告别了自己真正的青春年华，告别了年少的自己，不管还有多少眷恋。</p>
<p>我写的太过伤感了，还是继续回忆我们的杭电吧。和 4 号楼毗邻的两层小楼，楼上是卫生所，非典时要到这里量体温。楼下也是个小超市，不过这里东西要多些，肉粽也都是剥好的，我们的早饭大都在这里解决。大四下的时候很懒，经常睡到 10 点钟起来，胡乱套上衣服，跑超市买瓶百事，对面报亭买上体坛，再爬回床上，躺着看完报纸，12 点广播台音乐响起，几个人起床洗梳，然后排着队去超市买泡面，在店里泡好（寝室没打水），回去打开电脑开始挑英雄无敌。还有一点，超市里收音机整天开 103.2，也就是后来 96.8 音乐调频，我们也就顺便听会歌。说到这里，想起来在学校时，每天晚上熄灯后躺被窝里大家就等着听 amy 的节目，我爱 qq 和 amy.com, 特别是周四吧，讲鬼故事啦，虽然很多故事并不可怕，可是大家似乎都乐此不疲，好怀念 amy 和可乐的声音。再过去就是 5 号楼，以前是住千届男生的，再早是 98 的，我一位好兄弟住过，现在他已经在遥远的加拿大了。在 5 号楼和工字楼中间，是电教楼，语音教室，电脑课都在这里上，杭电好像 edi、eci 什么的比较好，所以很多老师都做电脑教材的。大一入校英语摸底考试是在这里，大学里第一次考试；教我们计算机的老师是省二级出卷的，所以每次课我们班都会多几个人，最后讲重点时候，教室里挤满 n 多人，看看四周都是自己不认识的，我也带过好几个杭商的朋友来听课的；电教 408 以前是杭电最大的活动场地，经常举办活动，那个叫万峰的鸟人就在这里高谈过性，新生华能杯特长赛、辩论赛什么的都在这里，好像还办过圣诞晚会，印象不深了，后来改了机房；三楼定期放电影，我们外教上课也常放电影给我们看。<br>
转了一大圈，回到工字楼附近。斜对面靠 2 号楼一边是老食堂，我们进校时就有的，老生比较喜欢在这边吃饭。1 楼是小锅菜，非常贵，开始吃还好，后来吃多了也腻了，不觉得比大锅好吃多少，可惜我们太懒，下来吃饭很晚，所以只有这里还有；吃饭腻了就只有吃面，刀削面还有拉面，当然是说的好听，其实就是开水里烫烫，很难吃，可是却吃了四年，每次外面吃完回来，头几天完全没有胃口；2 楼云梦楼是可以点菜的，有包厢，服务还可以，但上菜实在是太慢了；外面食堂基本上都是 99 的人在吃，好像感觉比较干净，这层还是存饭卡的地方，中午都要排队的，不过好像有人一次会存 2000 的，晕！像我一个学期在学校里也吃不到 500 块；三楼好像面积是最大的，以前点菜是在这里的，12 点后非常的挤，吃的多了和师傅熟了，我好像买红烧肉都比别人大块的多的，对了，这里的红烧肉非常好吃，是杭电食堂第一美食。</p>
<p>三楼正对楼梯的地方，就是广播台。门口贴几个小字：杭电广播台，里面家当不多，一个大间隔成三间加个走廊。外间摆几台电脑，大都是学生自己的，既是广播台的会议室、资料库，又是红色家园的工作间。红色家园当年就是在这里诞生的，说起来我还是红色家园的元老了，虽然我其实也没干什么事，可是一批批人都是我招进来的，还记得那时和小余同志一起做红色家园创建的策划方案，讨论组织框架，安排人事计划什么的，好像还是在昨天，可他现在已经在中海油奋斗了，呵呵，纳斯达克啊！后来我还是挂了一年的红色家园学生站长，实在是很惭愧，真正劳苦功高的是 mr fu，miss nie 几位。里间是编辑室，一张桌子，几张椅子，一个抽屉架而已，很简单，也很温馨。最喜欢夏天站在窗口看远处的蓝天，似乎特别的纯净，夜里看则是繁星点点，银汉皎皎。冬天的时候，经常一大帮人做完中午节目，聚在这里晒晒太阳，聊聊天，何等的惬意啊！再里间就是播音间，一整套设备都是当年我和老台长一起去挑的，旧是旧的，我们也没舍得扔，教下届机务的时候我还像个宝贝似的拿了出来，台里的音像资料很多也都是我们台员自己贡献出来的。很多时候，我喜欢一个人待在台里，拉掉闸刀，放上自己喜欢的音乐，静静得抽支烟，什么也不想…… 说起广播台来，好像就没个完，毕竟自己大学里最大的兴趣就放在了这里，而且这里也承载了我太多的回忆留恋。台里的东西很多都是我亲手买回来的，一件件好像都和自己有些关系；台里的规章制度，人员节目安排一样样都是自己制定的，看到这些，好像就看到了当初的自己。而且，在这里还有我大学里的第一段感情，很多往事啊！<br>
对应老食堂的，是新食堂，1 楼 2 楼是食堂，千届好像比较多在这边吃饭，冬天的时候这里有现烧的热汤卖，还可以；世界杯的时候这里放过中国队三场比赛，真是万人空巷啊；每年 2 月时候这里会办招聘会，一批批毕业生从这里踏上了工作道路；三楼是学工处、团委和社团活动中心，对了，这里也有个机房；四楼是活动中心，是杭电后期举办活动的主力场地，我在这里做过 n 次的 dj，不过因为空间太大，音响效果很差，主要是搞一些校内部的活动，校际的大都放体育馆，广播台也在这里搞过建台二十周年纪念晚会的，记得送出了三个很大的玩偶，好像是我去庆春路买的。我们的毕业典礼也是在这里，完了后，我们就算是离开了杭电。<br>
最后一个地方，是真正中心的中心花坛，一棵树，还有四时换的花，好像经常会种那种看上去像卷心菜的花。这个花坛，也是杭电的中轴所在，是集合的好地方。<br>
本来，杭电是已经讲完了，可是我还是要在这里再罗嗦几句。校外宿舍，西溪的最幸福，98 届的时候是党员响应号召为解决学校困难搬出去住的，没想那里最快乐，通宵供电，楼上就是大专的女生宿舍，进出自由；喜苑的就在喜园酒店对面，好像房间很大，住的是四个人吧，和现在下沙差不多的；湖墅那边的最辛苦，好像条件很差，因为是女生宿舍，所以没进去过，不得而知。<br>
下沙，因为大三的时候筹建下沙的广播台，我跑的很多，基本上一周一次，328 都坐的我头痛，当然大部分时候能赶的上校车，但交通实在太差了，夜里打车过来，再要回去的时候，根本拦不到车，我有次一直走到下沙镇，才坐三轮机动车到东站再换的 taxi。记得我第一次去的时候，还是很荒凉，根本就还没有杭电的样子呢，我们是从大门东侧现在有几家店的那个位置拐进去，直接走工地的。再后来到无锡接过新生，再过去的时候已经有点样子了，不过大门那里还完全是烂泥堆。后来去的多了，才慢慢看着学校建立起来。后门的门卫总是要查我们校徽学生证，不习惯的我们可是只有身份证的；活动中心，我们在门口摆过招聘摊子，里面上次财经院队过来和 0102 财经踢球时我们在里面换过衣服，楼上的广播台我和主管老师讨论过以后的布局；教学楼很奇怪，居然是 u 型的，里面路很不好找，里面的天井也很奇怪；前面的大草坪看的我就有踢球的冲动，不知道是不是禁止踩踏的，我就看过 n 多人穿越，包括老师；现代教学楼什么的，我记不清名字了，经常在年级主任办公室活动的，还有后来二楼的财经分院办公室，视野很好，好几次能和他们聊上一个下午的；食堂，那时只有一栋楼，还记得有人嘲笑我吃土豆都要剥皮，我现在还有多余的下沙饭票；宿舍楼间很空旷，应该回声很大的，冬天实在是太冷了；我们的广播台居然设在女生楼里，我好像还领了个通行证；宿舍区的超市我特意买了东西带回去，告诉他们这可是下沙的东西，这帮家伙一边抢着吃一边叫：向下沙的弟兄们问好！<br>
套用现在流行的一句话：我们的青春泪流满面！</p>
<p>罗里罗嗦终于一大篇文字打完，手里的一包 esse 早已成了灰烬，在缭绕的烟雾中，我的思绪被越飘越远，慢慢回到了 1999 年 9 月 14，那个阳光明媚的上午，我第一天来到杭电，我的回忆从此开始……</p>
<p>以我的学长郑钧这段话开始我对杭电特别是文一路校区一些琐碎的回忆，权当给后来者更或说是自己留些关于老校区的声色影像吧！<br>
杭州，西湖区，文一路，杭州电子工业学院。<br>
文一路相当繁华，特别是对学生来说，有数不尽的小店。吃的比较出名的有：知味观——杭电校外食堂；太一——以廉价出名，是男人拼酒的首选；一品——冬天最爱，骨头砂锅相当不错；此外，教工路上雅兔对面的衢州三头够辣，财经东方分部门口的山东升风水饺很不错，上过快报推荐的。买衣服的话，以我爱我家为界，西边都是专卖店，东边则都是个性小店，大都挂外贸服饰的招牌。其他店家，东方对面有个藏银，据说东西都是西藏过来的，是否真实就不得而知了；杭师门口的游戏人间总卖一些新奇玩意，仔细淘的话，有不少好东东；再过去快乐小站，是个水吧，也是我们的一大据点，东西一般，但正对杭师院大门，坐靠窗位置，可阅美女无数；网吧，教工路上风影看电影的多，雅兔则是游戏天堂，杭电人相当的多；书店则首推江郎，很多学生自己去那里买教材的，可以打折的。<br>
杭电两侧各有一个大超市，翠苑的物美感觉东西多些，而且楼上就是翠苑电影大世界，外围全是座位，可以在物美或者 kfc 买好吃的，在楼上和 mm 坐一个下午，风景不错；东边的华润是今年新开的，去的不多，总感觉东西偏少，而且学生顾客没有物美多；说到超市，其实最喜欢的还是黄龙好又多，很多人宁可多跑路，也要去好又多的，虽然只有一层，但感觉就比较好，而且很多人喜欢那里的电炉烤鸡。小超市，最爱是可的，以前是在杭电诊所楼下的，经常有人半夜跑出去买东西吃，后来文一路整顿，搬了，附近就没什么好的地方了，现在的店都是后来开的。<br>
学校四周，比较有印象的还有：京典——洗头比理发好，和 11 路站的阿俊、杭师对面的千丝万缕算是这一带比较出名的；一家小店，在工程门口，现在叫不上名字了，家常菜做的不错，关键是价廉量足，非典过后那段时间每天吃饭都要排队的；天马——以前是网吧老大，后来没落，主要经营歌厅和陶吧；撞击酒吧——学校外教最喜欢去的地方，经常有这方面的活动；文二夜市——杭电人逛的最多的地方，淘碟、点卡、明星壁画还有其他小玩意；文二电影院——平时几乎都是情侣专场，只有有球赛的日子才有上座率，00 欧锦赛，02 世界杯，大家很多时候在这里度过。<br>
公交车，最熟悉的自然是 11 路，到保俶路站转 328 去下沙，到武林广场站去银泰，终点站城站回家，到现在我到杭州也总是要坐坐 11 路；58 路，是个环线，可以到杭大、湖墅电子市场、武林广场等，我在杭大学法语时天天坐这班；155 是到延安路的，25 到吴山，816 到舟山东路，等等。其他就记不大清了，不过有一点特别，杭电门口的站叫省委党校，而文一和教工路口站才叫电子学院。</p>
<p>外面绕了一大圈，回到杭电。学校大门最大的特点就是那棵老树，估计也是有点年头的，正说明了杭电校园的特点：绿化好；校名写的很小，不注意还发现不了，经常有女生整个寝室来拍照片的；门口门卫都很年轻，离校前在草坪聚会时，找他们吹过瓶子；旁边是邮递房，新生时信很多，越大就越懒了，记得我们的邮箱是 233；进校后的大草坪，是杭电一大聚会地点，迎新晚会、班级聚会、老乡聚会很多在这里，右边有几棵树，形成一个隐秘的小空间，情侣很多，记得吃完散伙饭那天我们霸占了草坪，赶走了 n 多情侣，呵呵。<br>
正对校门是实验楼，也是杭电最宏伟的建筑，好象是 9 层还是 10 层吧。每两个分院共用一层，各分院的办公室、实验室还有机房。我们财经是和管理一起在四层，以前三个分院的时候我们是一起的，叫二分院（工商管理），辩论超强，出过不少牛人。现在的财经是杭电第一大院，以人数称雄，体育也很强。实验楼有个很大的好处，夏天很凉快，所以不少学生晚上睡这里的，理由当然很多，有的是做实验，有的是看门，不一而足。这里的另一特色是电梯超慢，人狂多，经常等电梯还没走路快。二楼是文理分院，可能是大家除了本分院外最常去的地方，因为英语和数学教研室在这里，所以大英、高数、线代这些公共基础课的重修补考费就是这里缴的，心痛啊。里面有个老师是杭电第一名捕，常监考数学，捕人无数，我亲眼看到过的就不下二十来人。</p>
<p>实验楼东侧，是图书馆，藏书好像还可以吧，算不上多，也不会太少，就是旧了点。借书很有意思，正门很少人走的，都从旁边楼梯直接上三楼侧门，脱鞋进去后，里面就是夏天也是感觉冷的。借书是有限制的，我记得是文艺类一次只能借两本吧，超期要罚的，我弄丢了《欧美音乐赏析》，超期加赔花了 100 多。一楼自由阅览室是考研基地，位置永远紧俏的，经常晚上和早上晨跑时就有人抢了。在这里你看到的是杭电最努力最用功的一批人，至于成效如何，我们不得而知。而杭电最精英的人，此刻要么在实验室里埋头研究，要么就在寝室里砍 cs、星际，再或者就是觥筹交措、呼朋唤友中，总之，各有各的精彩。二楼和四楼记得都有期刊阅览室，这里是写论文的好地方，所以大四学生居多。<br>
图书馆后是花圃，还有一条越来越干净的西溪河，这里也是情侣的天堂，美中不足的是这里有住校工，而且有养狗。河拐出去的地方是东门，是后来新开的，这里管的比较松，24 小时可以进出。东门进来是杭电招待所，比较一般，好像是 150 标间，非典时外地回来学生在这里隔离，只要交每天 10 块钱伙食费就可以待一周，吃的不错，还有冠军杯看，羡慕。学校的宴席大都在这里摆，我吃过几次，味道一般。对面是以前的研究生楼，房间不大，两人一间吧，带厨房，感觉太暗，有些潮。一栋新楼是教师宿舍，学校老师基本上是住翠苑和文二的，这里住的大都是单身的还有外教，我们外教还养过小狗，非常可爱。楼侧面的墙是练习网球的好地方。<br>
这一段绿化特别的好，绿树丛中，还有个小院，不大容易发现，这里是 6 号楼，最早是诊所兼教师宿舍，后来住成教，再后来人越来越多了，就住的是千届学生，我记得是 2000 工管的，回字型的房子，男女生分住两头，我们曾经猜测过是否有人可以趁管理员不注意溜到对面去，无从考究。这里管理员好像对外来的不大友善，很多人就在院子里直接叫人，不过容易暴露身份。反正我是很不喜欢这个楼，从来只打电话的。<br>
再往里走，是两栋教学楼，一曰东教，一曰新东教，这也是杭电一大笑话，造新东教时学校还征集过楼名，记得还有不少人投了，结果最后命名新东教，举校倾倒。先说东教，杭电老建筑，也算得上雕梁画栋了，以前杭电的明信片上有图。木地板、木桌、铁椅，大而空，感觉就是两个字：阴冷，据说闹鬼，以前有人上吊过的，晚上很少人自习的。这里每层都有大教室，所以很多哲学这种公共课在这里上的，很多辩论赛也是在这里搞的，我在这里作过很多次的 dj。杭电的大教室有个共同点，就是后面的高一级，所以在两段高低上的位置是考试最佳，你视力好的话前面一览无余。顺便说一句，东教对面是杭商的女生宿舍，有时候能和她们聊上几句。</p>
<p>说起新东教，话题可就多了。这里最早是杭电花圃，有一条长廊，据说是爬满葡萄藤的，是杭电最出名的情人长廊，一到了晚上，非常……，演绎了很多杭电浪漫经典。好像说有些坏小子会偷偷跑去捣乱，呵呵，杭电毕竟还是光棍多啊。很可惜我们 99 届进校后，花圃就拆了盖楼，不过到现在我还记得那时我们的美女辅导员带我们去看长廊，讲过去的往事。变成新东教后，学校还很搞笑的在旁边原地建了一个亭子，种上了竹子什么的，可是亭子正对教室，恐怕没有人愿意在众目睽睽下卿卿我我吧。新东的教室，4 和 6 是大教室，很大的那种，好像所有分院都是在这里开年级会议的，我们 99 财经都是在 306。这里还是考试集中的地方，凡是两个班以上的大课，大都在这里上和考试，四级也是在这里，六级则在新教。新东最好的一点是阳光充足，特别是 4 和 6 的大教室，坐在后排都沐浴在和煦的阳光中，所以，嘿嘿，这里也是要抢的，好像有很多次前面五六排几乎没人。新东一个特点是逃课很难，想偷偷溜走不被发现，非常难，不过高手还是很多，羡慕！在这里上过的课中印象最深的是大二时候的美术欣赏，上的人很多，而且以男生居多，知道原因了吧，因为传说中教这门的 miss l 是个美女，所以很多可以说是完全没有艺术细胞的人也挤来凑热闹。你如果要问，传说是否属实，那就到杭电上美术欣赏吧，不过她挺凶的。新东四楼比较偏僻，上课自习的人不多，也是个小小的禁区吧，因为经常一个教室里就两个人，*#•￥ （以下省略 100 字），顶上好像是个天台，以前情侣很多的，不过出过一次事后就人少了，去年吧，有人从上面摔下来死了，原因众说不一。<br>
新东前面，是杭电一个好地方，是一大块草坪，完全沐浴在阳光中，冬天在这里聊天嗑瓜子打牌的很多，看报纸睡觉的也很多，我经常和同学在这里背书，要考试了。旁边则是中心花园，杭电喜欢用方位来命名地点。这个花园两部分，靠行政楼是草坪，也是冬天背书的好地方，不过我们一直奢望有天能在上面踢场球，这里最有特色是有棵孤零零的小树，什么树我忘了，可能是柳树一类吧，就这么一棵，非常滑稽，这边也是拍照好地方。靠实验楼一边比较乱，有亭子假山，这里是我们背书打牌的地方；有小林子，情侣的地盘；有一排石桌，这里是早上起来背英语人的。这个花园里好像很多乱七八糟的植物，名字都怪怪的，记得我们英语听说课，有次老外就要我们写中心花园，晕，那些名字看了就头痛哦。<br>
花园旁一条路兼是停车场，好车不多，都是老师的吧。学生，我知道的不多，只是听说自动化有个家伙开别克赛欧的。<br>
路的尽头是工字楼，我以前文章提过，是我们的老巢，住了三年。结果大家都住出感情来了，好处很多：位置优越，四通八达，吃饭打水踢球上课出门都很近；独门独院，没有人和声音骚扰；有自己独立的停车棚和晒衣场，不用和别人挤；房间小些，但只住六人，舒服多了；没有公共电扇，可以通宵供电…… 所以大四搬走后，经常夜里和凌晨会听到有人高叫：“打回工字楼，还我工字楼”可见怀念之深，后来这里成了研究生楼，郁闷！工字楼晚是要锁门的，以前还可以翻窗，后来封了，就只能叫门了，看夜的是个老头子，反应比较慢，有次我们一个兄弟喝高了，等久了，叫把玻璃给砸了，生猛啊，结果后来入不成党，可惜啊！杭电规矩是男生不得入女生楼，女生进男生宿舍呢，则要登记，还隔会就打传呼来催，工字楼好的是后门比较好进，所以常常有人在门口掩护，女生悄悄溜进来。不过有几个家伙和管理员混熟了，女朋友可以随意进出的，所以有时会遇上尴尬，大家都知道男生在寝室穿着是怎么的咯！我们班住在二楼的最东头，也是最热闹的地段，晚上熄灯后好多人聚在门口，打双扣的，看小说的，聊天的，吃东西的，比熄灯前还热闹，经常还有人会提议去 “调戏” 某人，于是，惨叫声起，一出悲剧在上演。<br>
从我们工南窗口望出去，是新教，这是在新东教之前东西教之后盖的，故曰新教楼。不过也很有点旧了，前年考试前，我们在寝室里复习时，就是轰的一声，四楼的楼梯断了，幸好无人受伤。新教是老生最爱，98 和 99 的喜欢在这里自习，千届则大都在新东教，还有些勇敢的则在东西教。新教单数是大教室，逃课最好的战场，双数是小教室，大部分小课在这里上。这里给我印象最深的是徐旭初老师的课，可能很多杭电人都应该知道的，他也算是杭电最受欢迎的老师吧，据说是出身于高官家庭。本人很有些才华，颇有些读书人的风骨。他的讲座在杭电也是很受欢迎的，我还曾经请他到学校电台作过几次节目，聊的还是很投机的。此外，出身社科部的几位如钱升老师、郑建功老师等在学校也是比较受欢迎的，或严谨，或风趣，个人认为是为杭电读书人之望。还有一位老夫子，是曾经教我们英语的，张唯新老师，一看就是那种老底子的人，每次看到他我就会想到上海的老克腊，虽然和他打的交道不多，但心里还是很敬佩他的。<br>
新教两侧就是前面提到过的行政楼和西教楼。行政楼和我们学生联系不大，我只是因为广播台的关系常去宣传部，也没什么特别的地方，顶楼会议室常有些招聘会什么的，学校派出所也在这里，打证明什么的要到这里。西教故事和东教差不多。过去一点还有学校的印刷厂，服务态度不好。<br>
再过去就是南门了，也就是学校的后门，好像很多人把学校后门外叫堕落街，可能也是有点吧。南门出去，有几栋居民楼，好像有些房子出租的，不过很紧俏，很难租到；临街门口有几个小摊，卖煎饼的，卖水果的，卖烧烤的，还有糯米团子，课间很多人出来解决早饭问题。往外走，是杭商东院，以前是中专，杭电没抢过杭商，结果让他们把学校伸到教工路东面来了，课间时候很多人进出，有不少美女。靠着杭商的门，一排依次是杭商招待所，房间很小，好像也是 150；速食店，开始主卖珍珠奶茶，后来卖炒粉、炒饭什么的，生意极其火暴，这里做的最好的是卤肉饭，5 元一份，浇上汤非常好吃，光我们班就平均每天有六七个人吃的，常年如此；一家超市，门口卖台湾香肠和肉饼，味道还可以；网吧，换了好几个老板，最后开网吧才稳定下来；街口是烧烤店，生意也很好，吃鱿鱼的人很多，卖的甘蔗不错。再出去就是文二路了，短短一段街上好几家中专大专学校，所以相应的店也很多，不过和文一路不同，这里大都是小店。<br>
再回到学校，南门口是跃进楼，成教的地盘，也是校学生会驻地，这里印象不深，很少有这里的课。靠着跃进楼是篮球场，自然是灌蓝高手们的天堂了，据我所知，杭电好像只有一个人能扣篮，是个体育老师。篮球场有三块，八个篮筐，杭电篮球自 98 届之后，就很少高手了，98 届打篮球很有几个出名人物的，不知道下沙那边水平如何？我只知道财经 01 届有几个特招生，所以女篮还打进过 cuba。旁边是排球场，学校打排球风气还是比较盛的，好像 97 时候水平不错，到了 99 就不行了，上次在杭电举行的浙江省大运会女排比赛，我们就打的很烂。说到排球，我们班排球很强，几次校内比赛，都成绩很好，也拿过冠军的。不过排球场和篮球场都经常被我抢来踢球的，永远怀念那些小场地比赛，衣服脱下一摆门，几个人一凑就开始了，还经常喜欢下雨天冒雨踢，真的特别痛快，那些岁月……<br>
再西边，就是足球场了。说实话，场地很烂，基本上没什么草，可每天都挤满了人，有时候抢场地还会吵起来甚至打架。通常整个场地里同时可以进行 10 几场比赛，纵横交错，合理利用每一寸土地，大概杭电的男生工程制图都学的很好吧，划分的很有效率。这里，是我们的 easy band，凝聚着我们太多的回忆，进球后的疯狂庆祝，赢球时的痛快高歌，丢球时的无奈沮丧，输掉比赛时的郁闷，甚至难过落泪，我们常为一个美妙的进球欢呼雀跃，紧紧拥抱在一起，也会因为一个低级的失误互相指责，总会有人站出来激励大家向前，所有一切都记录在这块小小的球场上，破旧的球门里，和我们每一个人的心里。不管你平时活跃或沉默，无论你球踢的好坏与否，足球给我们提供了一个舞台，宣泄心情的舞台，它实实在在给我们的生活带来了快乐。前段时间回学校去看看时，我特意拍了好多球场的照片，虽然看来有些单调，但何尝不是我们梦中的恋之风景呢！</p>
<p>球场的南边，是体育馆，建成后学校很多活动都在这里举行，女排比赛，冰红茶的校园歌手大赛，很多学术报告等，对了，健美课也是在这里上的，据说好像很好玩。西边是 8 号楼，00 届男生和 01 届成教先后住过，最早时候是租给外面的，后来学校人员紧张才收回的，这里一大好处是白天可以在窗口看不少比赛，坏处是晚上球场尽是情侣，对单身汉是极大的打击。北边，是原来的杭电俱乐部，早就出租给了证券公司做营业大厅，学校只有在大型活动时才租回来用，我印象里有两次，一次是我们入学时迎新大会，还有一次是杭电另一牛人——马云（可是上过 newsweekly 的主啊）回校作报告时，坐的很满，也很热情，不过报告完了提的问题实在有些逊，那天我是在第一排，是负责录音的，现在马云报告的磁带还在杭电广播台，只是不知搬迁后尚还在否？说起来，还可以自我吹捧一些，杭电我们所知两大风云人物郑钧是国贸的学长，而马 ceo 是国贸的老师，不知杭电下一个风云人物还会出在国贸吗？愿上苍保佑郑钧马云的后辈们，玩且笑之，拭目以待！<br>
在球场东南角，是浴室、理发室、水房。水房门口总是 n 多水瓶子，“勤劳的处女们——f4 美作语” 总是会打很多水，开始男生总在猜测哪里需要那么多水，据说有 n 多版本，后来传出来是因为女生不喜欢在浴室洗澡，呵呵，扯远了。她们（他们）为了防止被懒人顺手牵羊，于是形成了独特的热水瓶文化，瓶子上用记号笔写了许多让人忍俊不禁的话，来告诫偷水贼们，有的直接：住手，小偷；有的善良：水，你拿走，瓶子，请留下，谢谢；还有的恶毒，写满了诅咒。这大概也算是环境造就文化吧。理发室从没去过，只知道剪出来都差不多，不过理发室有个小孩子非常可爱，发型常变，世界杯时候留的就是 R9 的阿福头，呵呵，很想看他什么时候留上贝壳当年的莫西干 :-）浴室，应该每个经历过大学的人都知道，水龙头总是稀缺产品，都有过几个人共用一个的时候，相应的，就有人提早在开门前排队来抢得位置，也有人专门挑关门前半小时去洗，结果常常在本该拥挤的中间时间会很空，然后循环，颇有些敌进我退，敌退我进的游击战道理。而在这时候，人生一大乐事就不是什么金榜题名，洞房花烛，而是浴室逢战友了，碰到熟人，就意味着可以挤上一个水龙头了，哪管这个人也许才认识一天。浴室水很充足，这一点比在家里洗还舒服，但却要时刻忍受冷热交替的煎熬，时不时会听到一声惨叫，不是被烫着了就是冻着了。浴室最大的痛苦还不在于此，空气很不流通，经常会洗到一半觉得气闷，忍不住要晕倒，不过也有狠角色，我们寝室的毛比就经常一个澡能洗上两个小时，美其名曰效益最大化。<br>
离浴室最近的就是杭电的核心所在，1 号楼。之所以说是核心，当然不是指地理位置，这里僻居西北一隅，核心在于此乃女生楼所在，理工科学校的人该都知道学校里女生的珍贵程度，看看每天 1 号楼前络绎不绝的男生就有数了。1 号楼好像是 6 层吧，97 届在 2 层，99 届在 1 层、3 层和 5 层，其他不详。朝北寝室的特点是一年四季永远挂着的窗帘，还有每张床上的床罩，双重保护，好像也是挡不住对面 3 号楼如狼似虎的男生们，百多号望远镜天天盯着，总会有漏网之鱼的，于是…… 就在男生中传播。我也有个望远镜，当然地理位置始终不好，所以前三年不是外借就是闲置，直到大四搬到 4 号楼，才可以拿来看对面工程的模特美女们，可惜抢的人太多，哎，这班兄弟！记得以前我们杭电论坛 hder 上的一个兄弟拿数码相机从窗口拍对面一号楼的 mm 们然后发到网上，供饿狼们流口水，当然照片很健康，只是走在路上的场景而已，我还记得有一张是我们认为的杭电校花吴 mm 的，然后对她的讨论是论坛上最热的帖子，好像回复有几百条。呵呵，说起来，这位 mm 我们很多人曾经也是动过心的，不过据说她和男朋友是青梅竹马的，最后也就是曾经想想而已（其实最主要的原因是我和她们班一个 mm 谈过恋爱，所以…… 槽糕，暴露自己的本来面目了:-））。望远镜好像女生也有，只不过男生看女生，对方是躲，而女生看男生，是悉听尊便，男生总是要大方些的嘛。女生终究还是含蓄些的，不至于明目张胆的去偷窥，不过私底下的议论总是少不了的，我曾经听过一个女孩子录的她们寝室卧谈的内容，呵呵，很是有些…… 涉及隐私，我不该多说，女生们心里自然有数的。因为男生禁止入内，所以对大部分男生来说，他们大学里很大的一个遗憾，就是没进过女生楼。很不幸的，我进去过了，其实看门的大妈也是有人情味的，找好理由就成功了一半。我那时是放暑假时候，我 mm 和大妈很熟，跟她说我要帮她搬电视机，所以我就得到特许，抱着电视机上了二楼，当然时间不久，呵呵，那时候单纯，就是有不该看的也没有去看，其实也没什么特别的，只不过女生楼三个字特别而已。1 号楼，最热的当然是一楼口的传达室，这里的大妈也是杭电最忙碌的人们。楼前小小一块地方，不知留下了多少人的多少回忆，见面，接吻，吵架，哭泣，一幕幕似是而非的场景天天在上演，有的始终是两个人的游戏，有的年年岁岁花相近，岁岁年年人不同，或男主角，或女主角，变幻不停，爱情的故事永远不会终结。熄灯后，1 号楼前的小亭子里，总是会有人在，小两口恋恋不舍相伴到天明也是常有的事，然而我始终记得的是半夜独自哭泣的女孩，是为她的爱情，还是那曾经年轻的岁月。——思往事，惜流芳，易成伤，拟歌先敛，欲笑还颦，最断人肠。<br>
说到女生楼，自然离不开浪漫的爱情。记得以前在 hder，常有人问起杭电的浪漫经典，我所知道的不多，那时就发了个帖子——情已逝，杭电的风花雪月。提到有两件比较近的。一是我确实知道的，97 国贸的一个学姐，好像是国贸的系花哦，和男朋友闹别扭了，那位 gg 买了 999 朵玫瑰（那时侯可是 99 年），把整个寝室都堆满了；还有一件好像是有些久了，毕业的时候，好多的学长通宵坐在 1 号楼下，弹起吉他，为心中的那个她放声高歌，楼上的 jj 们也点歌、合唱，好多暗恋了四年的人们在那么多人前大声的说出了心中憋了四年的话，那一夜，杭电无眠。还有一件我无法证实，好像是有位 gg 用玫瑰在 1 号楼前摆出了 i love you，那位 jj 在全楼女生的鼓励下勇敢的冲出了 1 号楼，天下有情人终……。<br>
抛开风月，回到杭电。1 号楼对面是 3 号楼，以望远镜出名，而且好像卫生也是最差的，3 号楼的兄弟不要砍我。这里还有学校的西门，常年不开。1 号楼隔壁就是 2 号楼，这楼最出名的是有位自动化的 98 级兄弟从五楼跳下来，不过运气很差，挂住了树枝受了伤，后来如何就不得而知了。在 3 号楼隔壁是 4 号楼，我在那里住了一年，还是很有感情的。两楼中间是小超市，东西不多，我们常来买些瓜子、花生、锅巴、牛肉什么的，有时候有茶叶蛋，这里的肉粽不要买，因为要自己剥的，很粘手。超市门口有电话亭，好像是打 ic 的吧，大一时要排队，手机多了后就很空了。回过来讲 4 号楼，1 楼是宿管中心所在，我们闹保险丝的事情就是在这里，楼里的管理员陈老师很有意思，挺热情的，大四下整天在寝室里就混的熟了，经常喜欢和我们聊几句。我们是在 3 楼，我开始住南面，斜对面就看得到女生楼，所以寝室很热闹，没几天我就逃到对面住了，正对工程学院大门，每天见的最多的就是工程的美女和门口停的 benz、bmw，刺激加打击，经常有兄弟拿我望远镜看完美女们一个个上车后，大吼一声：“xxx，n 年后老子开劳斯莱斯在工程门口晃。” 后来窗前的树砍了，失去了掩护，经常有 mm 发现我们的偷窥行为，记得有次我和一个美女对视了足有一分钟，最后还是我落荒而逃，惭愧惭愧！临离校前几天，一个晚上，看到我们经常看到的一个模特美女，和另个 mm 在工程门口的角落里失声痛哭，虽然我们并不知她哭是为毕业抑或是为爱情，却不由得触动了我们脆弱的神经。此后几天，我和寝室的哥们每天都坐在窗台上，把自己电脑里的 mp3 全放了一遍，音量放到最大，我们也高声唱和，唱那逝去的年华。看着楼下来来往往的人们，思绪也特别的伤感。走了，都要走了，再回来不知是何时了，有些人也许此生都不会再见了，那些一起踢球的兄弟，那些一起上课的同学，那些一起生活四年的哥们。离别，在这个非典之后的夏天，预料中而又猝不及防的击中了我们，却又无药可医。散伙的饭一顿顿的吃，告别的酒一杯杯的喝，每天都有喝的烂醉抬回来的人，我也真切的看过有个哥们喝醉后泪流满面，听过他一遍遍的我舍不得兄弟们啊！ 泪水，就在此刻肆虐，无可救药。也许真的是我太脆弱，曾经我那么的盼望工作，我喜欢工作。可现在真要离开了，这却是怎样的一种告别啊！！也许你并不怀念具体的某个人，可是这段生活，这段岁月，是你所能忘怀的吗？这是我们最最美好的一段年华啊，你年少的笑的痛的泪的爱都留在了这里，这个也许并不出名并不韶华的地方，这是怎样的一种特别。离开了学校，也就告别了自己真正的青春年华，告别了年少的自己，不管还有多少眷恋。</p>
<p>我写的太过伤感了，还是继续回忆我们的杭电吧。和 4 号楼毗邻的两层小楼，楼上是卫生所，非典时要到这里量体温。楼下也是个小超市，不过这里东西要多些，肉粽也都是剥好的，我们的早饭大都在这里解决。大四下的时候很懒，经常睡到 10 点钟起来，胡乱套上衣服，跑超市买瓶百事，对面报亭买上体坛，再爬回床上，躺着看完报纸，12 点广播台音乐响起，几个人起床洗梳，然后排着队去超市买泡面，在店里泡好（寝室没打水），回去打开电脑开始挑英雄无敌。还有一点，超市里收音机整天开 103.2，也就是后来 96.8 音乐调频，我们也就顺便听会歌。说到这里，想起来在学校时，每天晚上熄灯后躺被窝里大家就等着听 amy 的节目，我爱 qq 和 amy.com, 特别是周四吧，讲鬼故事啦，虽然很多故事并不可怕，可是大家似乎都乐此不疲，好怀念 amy 和可乐的声音。再过去就是 5 号楼，以前是住千届男生的，再早是 98 的，我一位好兄弟住过，现在他已经在遥远的加拿大了。在 5 号楼和工字楼中间，是电教楼，语音教室，电脑课都在这里上，杭电好像 edi、eci 什么的比较好，所以很多老师都做电脑教材的。大一入校英语摸底考试是在这里，大学里第一次考试；教我们计算机的老师是省二级出卷的，所以每次课我们班都会多几个人，最后讲重点时候，教室里挤满 n 多人，看看四周都是自己不认识的，我也带过好几个杭商的朋友来听课的；电教 408 以前是杭电最大的活动场地，经常举办活动，那个叫万峰的鸟人就在这里高谈过性，新生华能杯特长赛、辩论赛什么的都在这里，好像还办过圣诞晚会，印象不深了，后来改了机房；三楼定期放电影，我们外教上课也常放电影给我们看。<br>
转了一大圈，回到工字楼附近。斜对面靠 2 号楼一边是老食堂，我们进校时就有的，老生比较喜欢在这边吃饭。1 楼是小锅菜，非常贵，开始吃还好，后来吃多了也腻了，不觉得比大锅好吃多少，可惜我们太懒，下来吃饭很晚，所以只有这里还有；吃饭腻了就只有吃面，刀削面还有拉面，当然是说的好听，其实就是开水里烫烫，很难吃，可是却吃了四年，每次外面吃完回来，头几天完全没有胃口；2 楼云梦楼是可以点菜的，有包厢，服务还可以，但上菜实在是太慢了；外面食堂基本上都是 99 的人在吃，好像感觉比较干净，这层还是存饭卡的地方，中午都要排队的，不过好像有人一次会存 2000 的，晕！像我一个学期在学校里也吃不到 500 块；三楼好像面积是最大的，以前点菜是在这里的，12 点后非常的挤，吃的多了和师傅熟了，我好像买红烧肉都比别人大块的多的，对了，这里的红烧肉非常好吃，是杭电食堂第一美食。</p>
<p>三楼正对楼梯的地方，就是广播台。门口贴几个小字：杭电广播台，里面家当不多，一个大间隔成三间加个走廊。外间摆几台电脑，大都是学生自己的，既是广播台的会议室、资料库，又是红色家园的工作间。红色家园当年就是在这里诞生的，说起来我还是红色家园的元老了，虽然我其实也没干什么事，可是一批批人都是我招进来的，还记得那时和小余同志一起做红色家园创建的策划方案，讨论组织框架，安排人事计划什么的，好像还是在昨天，可他现在已经在中海油奋斗了，呵呵，纳斯达克啊！后来我还是挂了一年的红色家园学生站长，实在是很惭愧，真正劳苦功高的是 mr fu，miss nie 几位。里间是编辑室，一张桌子，几张椅子，一个抽屉架而已，很简单，也很温馨。最喜欢夏天站在窗口看远处的蓝天，似乎特别的纯净，夜里看则是繁星点点，银汉皎皎。冬天的时候，经常一大帮人做完中午节目，聚在这里晒晒太阳，聊聊天，何等的惬意啊！再里间就是播音间，一整套设备都是当年我和老台长一起去挑的，旧是旧的，我们也没舍得扔，教下届机务的时候我还像个宝贝似的拿了出来，台里的音像资料很多也都是我们台员自己贡献出来的。很多时候，我喜欢一个人待在台里，拉掉闸刀，放上自己喜欢的音乐，静静得抽支烟，什么也不想…… 说起广播台来，好像就没个完，毕竟自己大学里最大的兴趣就放在了这里，而且这里也承载了我太多的回忆留恋。台里的东西很多都是我亲手买回来的，一件件好像都和自己有些关系；台里的规章制度，人员节目安排一样样都是自己制定的，看到这些，好像就看到了当初的自己。而且，在这里还有我大学里的第一段感情，很多往事啊！<br>
对应老食堂的，是新食堂，1 楼 2 楼是食堂，千届好像比较多在这边吃饭，冬天的时候这里有现烧的热汤卖，还可以；世界杯的时候这里放过中国队三场比赛，真是万人空巷啊；每年 2 月时候这里会办招聘会，一批批毕业生从这里踏上了工作道路；三楼是学工处、团委和社团活动中心，对了，这里也有个机房；四楼是活动中心，是杭电后期举办活动的主力场地，我在这里做过 n 次的 dj，不过因为空间太大，音响效果很差，主要是搞一些校内部的活动，校际的大都放体育馆，广播台也在这里搞过建台二十周年纪念晚会的，记得送出了三个很大的玩偶，好像是我去庆春路买的。我们的毕业典礼也是在这里，完了后，我们就算是离开了杭电。<br>
最后一个地方，是真正中心的中心花坛，一棵树，还有四时换的花，好像经常会种那种看上去像卷心菜的花。这个花坛，也是杭电的中轴所在，是集合的好地方。<br>
本来，杭电是已经讲完了，可是我还是要在这里再罗嗦几句。校外宿舍，西溪的最幸福，98 届的时候是党员响应号召为解决学校困难搬出去住的，没想那里最快乐，通宵供电，楼上就是大专的女生宿舍，进出自由；喜苑的就在喜园酒店对面，好像房间很大，住的是四个人吧，和现在下沙差不多的；湖墅那边的最辛苦，好像条件很差，因为是女生宿舍，所以没进去过，不得而知。<br>
下沙，因为大三的时候筹建下沙的广播台，我跑的很多，基本上一周一次，328 都坐的我头痛，当然大部分时候能赶的上校车，但交通实在太差了，夜里打车过来，再要回去的时候，根本拦不到车，我有次一直走到下沙镇，才坐三轮机动车到东站再换的 taxi。记得我第一次去的时候，还是很荒凉，根本就还没有杭电的样子呢，我们是从大门东侧现在有几家店的那个位置拐进去，直接走工地的。再后来到无锡接过新生，再过去的时候已经有点样子了，不过大门那里还完全是烂泥堆。后来去的多了，才慢慢看着学校建立起来。后门的门卫总是要查我们校徽学生证，不习惯的我们可是只有身份证的；活动中心，我们在门口摆过招聘摊子，里面上次财经院队过来和 0102 财经踢球时我们在里面换过衣服，楼上的广播台我和主管老师讨论过以后的布局；教学楼很奇怪，居然是 u 型的，里面路很不好找，里面的天井也很奇怪；前面的大草坪看的我就有踢球的冲动，不知道是不是禁止踩踏的，我就看过 n 多人穿越，包括老师；现代教学楼什么的，我记不清名字了，经常在年级主任办公室活动的，还有后来二楼的财经分院办公室，视野很好，好几次能和他们聊上一个下午的；食堂，那时只有一栋楼，还记得有人嘲笑我吃土豆都要剥皮，我现在还有多余的下沙饭票；宿舍楼间很空旷，应该回声很大的，冬天实在是太冷了；我们的广播台居然设在女生楼里，我好像还领了个通行证；宿舍区的超市我特意买了东西带回去，告诉他们这可是下沙的东西，这帮家伙一边抢着吃一边叫：向下沙的弟兄们问好！<br>
套用现在流行的一句话：我们的青春泪流满面！</p>
<p>罗里罗嗦终于一大篇文字打完，手里的一包 esse 早已成了灰烬，在缭绕的烟雾中，我的思绪被越飘越远，慢慢回到了 1999 年 9 月 14，那个阳光明媚的上午，我第一天来到杭电，我的回忆从此开始……</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django 1.8 事务]]></title>
        <id>https://me.iblogc.com/post/django-18-shi-wu/</id>
        <link href="https://me.iblogc.com/post/django-18-shi-wu/">
        </link>
        <updated>2016-07-12T10:24:10.000Z</updated>
        <content type="html"><![CDATA[<h3 id="atomic_requests">ATOMIC_REQUESTS</h3>
<p>将每个HTTP 请求封装在一个数据库事务中</p>
<pre><code class="language-python">DATABASES = {
    'default': {
      'ENGINE': 'django.db.backends.postgresql_psycopg2',
      'NAME': '',
      'USER': '',
      'PASSWORD': '',
      'HOST': '',
      'PORT': '',
      'ATOMIC_REQUESTS': True,
    }
  }
</code></pre>
<h3 id="autocommit">AUTOCOMMIT</h3>
<p>如果需要禁用Django 的事务管理并自己实现，设置它为False， 但不建议这样做。</p>
<h3 id="non_atomic_requests">non_atomic_requests</h3>
<p>无视全局事务设置，恢复Django默认的自动提交模式</p>
<h3 id="transactionatomic">transaction.atomic</h3>
<p>单个方法事务控制</p>
<pre><code class="language-python">from django.db import transaction
 
@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()
 
</code></pre>
<pre><code class="language-python"> 
from django.db import transaction
 
def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()
 
    with transaction.atomic():
        # This code executes inside a transaction.
        do_more_stuff()
 
</code></pre>
<h3 id="transactionsavepoint">transaction.savepoint()</h3>
<p>设置保存点</p>
<pre><code class="language-python">from django.db import transaction
 
# open a transaction
@transaction.atomic
def viewfunc(request):
 
    a.save()
    # transaction now contains a.save()
 
    sid = transaction.savepoint()
 
    b.save()
    # transaction now contains a.save() and b.save()
 
    if want_to_keep_b:
        # 释放保存点
        transaction.savepoint_commit(sid)
        # open transaction still contains a.save() and b.save()
    else:
        transaction.savepoint_rollback(sid)
        # open transaction now contains only a.save()
</code></pre>
<p>如果<code>b.save()</code>抛出异常，所有未提交操作都会回滚。<br>
调用 transaction.rollback() 回滚整个事物。任何未提交的数据库操作都会丢失。在此例中, 由 a.save()所保存的变更将会丢失,即使这个操作自身没有产生错误。</p>
<pre><code class="language-python">a.save() # Succeeds, but may be undone by transaction rollback
try:
    b.save() # Could throw exception
except IntegrityError:
    transaction.rollback()
c.save() # Succeeds, but a.save() may have been undone
</code></pre>
<p>如果<code>b.save()</code>抛出异常，<code>a.save()</code>后的未提交操作都会被回滚</p>
<pre><code class="language-python">a.save() # Succeeds, and never undone by savepoint rollback
sid = transaction.savepoint()
try:
    b.save() # Could throw exception
    # 释放保存点
    transaction.savepoint_commit(sid)
except IntegrityError:
    transaction.savepoint_rollback(sid)
c.save() # Succeeds, and a.save() is never undone
</code></pre>
<p><code>transaction.savepoint()</code>之前的代码永不会被回滚</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[npm 入门命令]]></title>
        <id>https://me.iblogc.com/post/npm入门命令/</id>
        <link href="https://me.iblogc.com/post/npm入门命令/">
        </link>
        <updated>2016-07-06T15:46:37.000Z</updated>
        <summary type="html"><![CDATA[<br/>]]></summary>
        <content type="html"><![CDATA[<br/>
<!--more-->
<h2 id="更新记录">更新记录</h2>
<p>2016-07-06 初稿</p>
<h2 id="基础命令">基础命令</h2>
<p>显示npm版号</p>
<pre><code>npm -v
# 或
npm version
</code></pre>
<p>安装模块</p>
<pre><code># 带-g为全局安装
# 本地安装：package会被下载到当前所在目录，也只能在当前目录下使用。
# 全局安装：package会被下载到到特定的系统目录下，安装的package能够在所有目录下使用。
npm install &lt;package&gt; -g
# 简写
npm i &lt;package&gt; -g
</code></pre>
<p>升级全局安装的指定模块</p>
<pre><code>npm update &lt;package&gt; -g
</code></pre>
<p>升级当前目录下的指定模块</p>
<pre><code>npm update &lt;package&gt;
</code></pre>
<p>升级当前目录下全部模块</p>
<pre><code>npm update
</code></pre>
<p>升级node自身</p>
<pre><code># 安装一个叫n的模块
npm install -g n
# 升级到最新稳定版
n stable
# 升级到指定版本
n v0.10.26
</code></pre>
<p>卸载移除指定模块</p>
<pre><code>npm uninstall &lt;package&gt;
# 别名：remove, rm, r, un, unlink
</code></pre>
<p>显示已安装模块</p>
<pre><code>npm list
</code></pre>
<p>显示模块详细信息</p>
<pre><code>npm show &lt;package&gt;
</code></pre>
<p>查看全局包安装路径</p>
<pre><code>npm root -g
</code></pre>
<p>查看当前包安装路径</p>
<pre><code>npm root
</code></pre>
<p>查看npm配置</p>
<pre><code>npm config list
</code></pre>
<p>查看帮助</p>
<pre><code>npm help
</code></pre>
<p>查看相关命令的帮助文档</p>
<pre><code>npm help &lt;command&gt;
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Chrome 扩展/插件/应用推荐]]></title>
        <id>https://me.iblogc.com/post/chrome扩展-插件-应用推荐/</id>
        <link href="https://me.iblogc.com/post/chrome扩展-插件-应用推荐/">
        </link>
        <updated>2016-06-01T15:22:35.000Z</updated>
        <summary type="html"><![CDATA[<br/>
本文过期，请前行住[自用软件推荐](/2019/10/12/awesome-software/)查看最新内容]]></summary>
        <content type="html"><![CDATA[<br/>
本文过期，请前行住[自用软件推荐](/2019/10/12/awesome-software/)查看最新内容
<!--more-->
## 插件/扩展
1. **Adblock Plus**
广告拦截器，谁用谁知道。
<ol start="2">
<li>
<p><strong>Checker Plus for Gmail™</strong><br>
Gmail/Inbox插件，不用打开网页处理邮件。</p>
</li>
<li>
<p><strong>Wappalyzer</strong><br>
探测当前网页正在使用的开源软件或者js类库，web开发者必备神器。</p>
</li>
<li>
<p><strong>crxMouse Chrome Gestures</strong><br>
鼠标手势。</p>
</li>
<li>
<p><strong>Ghostery</strong><br>
了解谁在跟踪您的网页浏览操作，并可禁用跟踪行为。</p>
</li>
<li>
<p><strong>Google Keep Chrome 扩展程序</strong><br>
将网页快速的保存到Google Keep中。</p>
</li>
<li>
<p><strong>Google翻译</strong><br>
支付在网页中划词翻译。</p>
</li>
<li>
<p><strong>划词翻译</strong><br>
划词翻译，支持谷歌、百度、有道、必应四大翻译和朗读引擎，访问Google比较因难的的可以用这个。</p>
</li>
<li>
<p><strong>LastPass</strong><br>
免费的密码管理管理器。</p>
</li>
<li>
<p><strong>MindMap Tab</strong><br>
在新标签页中快速编辑思维导图。</p>
</li>
<li>
<p><strong>Octotree</strong><br>
在浏览器左侧树形展示Github代码。</p>
</li>
<li>
<p><strong>Prism Pretty</strong><br>
美化代码（html、css、js、json……）</p>
</li>
<li>
<p><strong>Proxy SwitchyOmega</strong><br>
你懂的。</p>
</li>
<li>
<p><strong>Pushbullet</strong><br>
快速的往其它设备发送消息。</p>
</li>
<li>
<p><strong>Save to Pocket</strong><br>
快速保存网页到Pocket。</p>
</li>
<li>
<p><strong>Search by Image (by Google)</strong><br>
浏览器右键增加Google的以图搜图功能。</p>
</li>
<li>
<p><strong>v2ex plus</strong><br>
优雅便捷的 V2EX 扩展。</p>
</li>
<li>
<p><strong>Web Timer</strong><br>
每个网站停留时间统计。</p>
</li>
<li>
<p><strong>为知笔记网页剪辑器</strong><br>
配合为知笔记，快速保存网页内容到为知笔记。</p>
</li>
<li>
<p><strong>二维码（生成及识别）</strong><br>
生成或识别二维码。</p>
</li>
<li>
<p><strong>惠惠购物助手</strong><br>
在主流电商网站页面上提供商品的历史价格，及在同款商品在其它平台的价格比较。</p>
</li>
<li>
<p><strong>新浪微博图床</strong><br>
微博是个好图床。</p>
</li>
<li>
<p><strong>网页截图:注释&amp;批注</strong><br>
捕获整个页面或任何部分，矩形，圆形，箭头，线条和文字，模糊敏感信息，一键上传分享。</p>
</li>
</ol>
<h2 id="应用">应用</h2>
<ol>
<li>
<p><strong>Postman</strong><br>
功能强大的接口调试工具。</p>
</li>
<li>
<p><strong>Google Keep - 记事和清单</strong><br>
不解释。</p>
</li>
<li>
<p><strong>Pocket</strong><br>
不解释。</p>
</li>
<li>
<p><strong>JSON Editor</strong><br>
图形化json编辑工具。</p>
</li>
</ol>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django admin 笔记]]></title>
        <id>https://me.iblogc.com/post/django-admin-bi-ji/</id>
        <link href="https://me.iblogc.com/post/django-admin-bi-ji/">
        </link>
        <updated>2016-05-20T10:23:06.000Z</updated>
        <content type="html"><![CDATA[<h1 id="管理站点"><a href="http://python.usyiyi.cn/django/index.html">管理站点</a></h1>
<h2 id="注册模型到admin">注册模型到<code>admin</code></h2>
<pre><code class="language-python">from django.contrib import admin
from myproject.myapp.models import Author
 
admin.site.register(Author)
</code></pre>
<p>或是使用装饰器</p>
<pre><code class="language-python">from django.contrib import admin
from myproject.myqll.models import Author
 
@admin.register(Author[, Reader, Editor])
class PersonAdmin(admn.ModelAdmin):
    pass
# 或
@admin.register
class PersonAdmin(admn.ModelAdmin):
   model = Author
</code></pre>
<h2 id="定制选项">定制选项</h2>
<h3 id="list_display"><code>list_display</code></h3>
<p>控制在管理的列表页面显示的字段</p>
<h3 id="fields"><code>fields</code></h3>
<p>控制在表单页面显示的字段、字段顺序及行内分组，如果字段在<code>model</code>里设置了<code>editable=False</code>，则只有当此字段在<code>readonly_fields</code>中时，才能包含在<code>fields</code>中；</p>
<h3 id="exclude"><code>exclude</code></h3>
<p>控制在表单页面不需要显示的字段</p>
<h3 id=""></h3>
<p>在<code>apps.py</code>里</p>
<pre><code class="language-python">from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
 
 
class AuthorConfig(AppConfig):
    name = 'myapp'
    verbose_name = u'管理后台显示的名称'
    ```
在`__init__.py`加入
```python
default_app_config = 'myapp.apps.AuthorConfig'
</code></pre>
<h3 id="modeladmin-options"><code>ModelAdmin options</code></h3>
<p><code>ModelAdmin.actions</code><br>
列表， 列表页操作，详见下章</p>
<p><code>ModelAdmin.actions_on_top</code><br>
<code>ModelAdmin.actions_on_bottom</code><br>
布尔值，列表页操作控件显示的位置，上面可同时显示</p>
<p><code>ModelAdmin.actions_selection_counter</code><br>
布尔值，是否在列表页操作控件显示选中数量</p>
<p><code>ModelAdmin.date_hierarchy</code><br>
时间类型字段名字符，列表页顶部可根据指定日期字段生成一个智能过滤条</p>
<p><code>ModelAdmin.empty_value_display</code><br>
当字段值为<code>None</code>、空字符时显示的替代内容，默认为    <code>-</code></p>
<p><code>ModelAdmin.exclude</code><br>
哪些字段在列表页中不显示</p>
<p><code>ModelAdmin.view_on_site</code><br>
在站点上查看当前编辑的对象<br>
默认使用<code>get_absolute_url()</code>或可重写<code>view_on_site(self, obj)</code>方法返回url</p>
<h2 id="管理操作"><a href="http://python.usyiyi.cn/django/ref/contrib/admin/actions.html">管理操作</a></h2>
<p>列表页批量操作</p>
<pre><code class="language-python">from django.contrib import admin
from myapp.models import Article
 
def make_published(modeladmin, request, queryset):
    # queryset为用户所选的对象集合
    queryset.update(status='p')   
make_published.short_description = u'发布文章'
 
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'status']
    ordering = ['title']
    # 使用actions注册操作
    actions = [make_published]
 
admin.site.register(Article, ArticleAdmin)
</code></pre>
<p>因为<code>make_published</code>方法和<code>Article</code>模型是紧密耦合的，所以为了更好的设计，应该把<code>make_published</code>绑定到<code>ArticleAdmin</code>里面去。</p>
<pre><code class="language-python">class ArticleAdmin(admin.ModelAdmin):
    ...
 
    def make_published(self, request, queryset):
        rows_updated = queryset.update(status='p')
        if rows_updated == 1:
            message_bit = &quot;1 story was&quot;
        else:
            message_bit = &quot;%s stories were&quot; % rows_updated
        self.message_user(request, &quot;%s successfully marked as published.&quot; % message_bit)
</code></pre>
<p>操作的中间页面<br>
只要从你的操作返回HttpResponse（或其子类）就可以了。</p>
<pre><code class="language-python">from django.http import HttpResponse
from django.core import serializers
 
def export_as_json(modeladmin, request, queryset):
    response = HttpResponse(content_type=&quot;application/json&quot;)
    serializers.serialize(&quot;json&quot;, queryset, stream=response)
    return response
</code></pre>
<pre><code class="language-python">from django.contrib import admin
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect
 
def export_selected_objects(modeladmin, request, queryset):
    selected = request.POST.getlist(admin.ACTION_CHECKBOX_NAME)
    ct = ContentType.objects.get_for_model(queryset.model)
    return HttpResponseRedirect(&quot;/export/?ct=%s&amp;ids=%s&quot; % (ct.pk, &quot;,&quot;.join(selected)))
</code></pre>
<p>全站操作</p>
<pre><code class="language-python">from django.contrib import admin
 
# 第二个参数为禁用时使用的名字，如果方法没有short_description，也会使用这个名字
admin.site.add_action(export_selected_objects，u'导出')
</code></pre>
<p><code>export_selected_objects</code>方法同前面相同</p>
<p>禁用全站操作</p>
<pre><code class="language-python">from django.contrib import admin
# 禁用时使用add_action时的name，如果没有，直接使用方法名字字符串
admin.site.disable_action(u'导出')
</code></pre>
<p>如果想要局部启用被全局禁用的操作可以显示的放到<code>ModelAdmin.actions</code>列表中就可以</p>
<p>如果想要在某个<code>ModelAdmin</code>上禁用所有操作，可以把<code>ModelAdmin.actions</code>设置为<code>None</code>，这会所有列表上的批量操作全部禁用，包括<code>django</code>自带的操作，设置<code>ModelAdmin.actions</code>为空<code>list</code>并不能禁用全站级操作</p>
<p>根据每个请求来动态的开启或禁用操作</p>
<pre><code class="language-python">class MyModelAdmin(admin.ModelAdmin):
    ...
 
    def get_actions(self, request):
        actions = super(MyModelAdmin, self).get_actions(request)
        if request.user.username[0].upper() != 'J':
            if 'delete_selected' in actions:
                del actions['delete_selected']
        return actions
</code></pre>
<p><code>ModelAdmin.get_actions()</code>会返回当前<code>ModelAdmin</code>上的所有操作的有序字典，<code>key</code>为<code>action</code>的<code>name</code>（不是<code>short_description</code>），<code>value</code>为<code>(function, name, short_description)</code>元组<br>
<em><code>short_description</code>只是用来给人类看的，所以不太会用作参数</em></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django 1.9 文档阅读笔记]]></title>
        <id>https://me.iblogc.com/post/django1-9文档阅读笔记/</id>
        <link href="https://me.iblogc.com/post/django1-9文档阅读笔记/">
        </link>
        <updated>2016-04-04T07:27:20.000Z</updated>
        <content type="html"><![CDATA[<h2 id="更新记录">更新记录</h2>
<p>2016-04-04 初稿<br>
2016-06-30 更新内容</p>
<h2 id="模型"><a href="http://python.usyiyi.cn/django/topics/db/models.html">模型</a></h2>
<p><a href="http://python.usyiyi.cn/django_182/ref/models/meta.html#model-meta-field-api">Model _meta API</a></p>
<p>限制普通字段的选择范围<br>
<code>choices </code>，value-text，<br>
显示<code>get_foo_display()</code></p>
<p>模型继承</p>
<ol>
<li>抽象基类</li>
</ol>
<ul>
<li>默认继承元类</li>
</ul>
<ol start="2">
<li>多表继承</li>
</ol>
<ul>
<li>一般情况不继承元类</li>
</ul>
<ol start="3">
<li>代理模型</li>
</ol>
<p>多重继承主要用于<code>mix-in</code>类</p>
<p>多表继承时使用<code>parent_link=True</code>显示指定OneToOne字段</p>
<h2 id="模型字段参考"><a href="http://python.usyiyi.cn/django/ref/models/fields.html#lazy-relationships">模型字段参考</a></h2>
<p>与尚未定义的模型关联使用模型名字（字符串）而非本身（类）</p>
<p>关联自己使用<code>self</code></p>
<p><code>related_name</code> <code>relate_query_name</code></p>
<p>外键关联到特定字段<br>
<code>to_field </code></p>
<p>限制外键的选择范围（可以是一个字典、一个Q 对象或者一个返回字典或Q对象的可调用对象）<br>
<code>limit_choices_to </code></p>
<p>外键关联对象删除行为<br>
<code>on_delete</code></p>
<p>1.8以后保存模型时，未保存的外键对象将被忽略，除非设置<code>allow_unsaved_instance_assignment=True</code></p>
<p>关联自身的多对多关系默认对称，取消对称设置<code>symmetrical=False</code></p>
<p><code>ImageField</code>中的<code>height_field</code>和<code>width_field</code>是用来存储存入图片的高度和宽度值的</p>
<p>##<a href="http://python.usyiyi.cn/django/topics/db/queries.html#spanning-multi-valued-relationships">执行查询</a></p>
<p><a href="http://python.usyiyi.cn/django/howto/custom-lookups.html">可自定义查询（高级查找）</a><br>
<code>exclude</code>多条件查询时是用or关系而不是and关系</p>
<p><code>F()</code><br>
用于模型内部字段间的比较支持加法、减法、乘法、除法、取模以及幂计算等算术操作<br>
支持.bitand() 和.bitor()位操作，<code>update()</code>也可以使用<code>F()</code>但有限制（在update 中你不可以使用F() 对象引入join —— 你只可以引用正在更新的模型的字段）</p>
<p>查询集缓存<br>
当只对查询集的部分进行求值时会检查缓存， 但是如果这个部分不在缓存中，那么接下来查询返回的记录都将不会被缓存。</p>
<pre><code class="language-python">&gt;&gt;&gt; queryset = Entry.objects.all()
&gt;&gt;&gt; print([p.headline for p in queryset]) # Evaluate the query set.
&gt;&gt;&gt; print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
</code></pre>
<pre><code class="language-python">&gt;&gt;&gt; queryset = Entry.objects.all()
&gt;&gt;&gt; print queryset[5] # Queries the database
&gt;&gt;&gt; print queryset[5] # Queries the database again
</code></pre>
<pre><code>&gt;&gt;&gt; queryset = Entry.objects.all()
&gt;&gt;&gt; [entry for entry in queryset] # Queries the database
&gt;&gt;&gt; print queryset[5] # Uses cache
&gt;&gt;&gt; print queryset[5] # Uses cache
</code></pre>
<p><code>Q()</code><br>
可使用Q对象进行复杂查询</p>
<p>判断两相模型实例是否相同，直接使用<code>==</code>比较即可</p>
<p>默认批量删除对象时不会调用实例的<code>delete</code>方法</p>
<p>拷贝实例，把<code>pk</code>设置为<code>None</code>再<code>save</code>即可（如果是继承的，则<code>pk</code>和<code>id</code>都需要设置为<code>None</code>）</p>
<p><code>update()</code>方法也不会调用模型的<code>save()</code>方法，不会发出<code>pre_save</code>和<code>post_save</code>信号，字段的<code>auto_now</code>也不会起作用</p>
<p>一对多关联对象访问会缓存</p>
<pre><code class="language-python">&gt;&gt;&gt; e = Entry.objects.get(id=2)
&gt;&gt;&gt; print(e.blog)  # Hits the database to retrieve the associated Blog.
&gt;&gt;&gt; print(e.blog)  # Doesn't hit the database; uses cached version.
</code></pre>
<p>自定义反向管理器1.7+</p>
<pre><code class="language-python">from django.db import models
 
class Entry(models.Model):
    #...
    objects = models.Manager()  # Default Manager
    entries = EntryManager()    # Custom Manager
 
b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()
</code></pre>
<h2 id="查询集api-参考"><a href="http://python.usyiyi.cn/django/ref/models/querysets.html">查询集API 参考</a></h2>
<h2 id="聚合"><a href="http://python.usyiyi.cn/django/topics/db/aggregation.html">聚合</a></h2>
<p>一次创建多条数据（只有一条sql）<br>
<code>bulk_create</code></p>
<p>根据提供的一组<code>pk</code>查询出所有对应的对象<br>
<code>in_bulk</code></p>
<p>在查作者列表时要查每个作者有几篇博文</p>
<pre><code class="language-python">&gt;&gt;&gt; from django.db.models import Count
&gt;&gt;&gt; authors = Author.object.all().annotate(Count('blog'))
# authors[0]作者的博文数
&gt;&gt;&gt; authors[0].blog__count
3
# 或
&gt;&gt;&gt; authors = Author.object.all().annotate(number_of_blog=Count('blog'))
&gt;&gt;&gt; authors[0].number_of_blog
3
</code></pre>
<p>算出所有作者的年龄总合（不需要其它数据）</p>
<pre><code class="language-python">&gt;&gt;&gt; ageAuthor.objects.all().aggregate(Sum('age'))
{'age__sum': 26}
</code></pre>
<p><s><code>annotate</code>和</s><code>aggregate</code>都可写入多个注解表达式<br>
<code>annotate</code>和<code>aggregate</code>可聚合关联对象</p>
<p>对注解进行过滤</p>
<pre><code class="language-python"># 查询出作者数大于1的书本
# 只有一条sql
Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)
</code></pre>
<p>但顺序不一样，结果也不同，如：</p>
<pre><code class="language-python">Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)
Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book')) 
</code></pre>
<p>对注解项进行排序</p>
<pre><code class="language-python">Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
</code></pre>
<p><code>values()</code>使用注解时要小心，如果<code>values()</code>在注解之前，会对结果进行分组，注解会作用在分组上而不是整个查询集上</p>
<blockquote>
<p>与默认排序交换或order_by()¶</p>
</blockquote>
<blockquote>
<p>在查询集中的order_by() 部分(或是在模型中默认定义的排序项) 会在选择输出数据时被用到，即使这些字段没有在 values() 调用中被指定。这些额外的字段可以将相似的数据行分在一起，也可以让相同的数据行相分离。在做计数时，  就会表现地格外明显：</p>
</blockquote>
<blockquote>
<p>通过例子中的方法，假设有一个这样的模型：</p>
</blockquote>
<pre><code class="language-python">from django.db import models 
class Item(models.Model):
    name = models.CharField(max_length=10)
    data = models.IntegerField() 
    class Meta:
        ordering = [&quot;name&quot;]
</code></pre>
<blockquote>
<p>关键的部分就是在模型默认排序项中设置的name字段。如果你想知道每个非重复的data值出现的次数，可以这样写：</p>
</blockquote>
<pre><code class="language-python"># Warning: not quite correct!
Item.objects.values(&quot;data&quot;).annotate(Count(&quot;id&quot;))
</code></pre>
<blockquote>
<p>...这部分代码想通过使用它们公共的 data 值来分组 Item对象，然后在每个分组中得到  id 值的总数。但是上面那样做是行不通的。这是因为默认排序项中的 name也是一个分组项，所以这个查询会根据非重复的 (data, name) 进行分组，而这并不是你本来想要的结果。所以，你应该这样改写：</p>
</blockquote>
<pre><code class="language-python">Item.objects.values(&quot;data&quot;).annotate(Count(&quot;id&quot;)).order_by()
</code></pre>
<blockquote>
<p>...这样就清空了查询中的所有排序项。 你也可以在其中使用 data ，这样并不会有副作用，这是因为查询分组中只有这么一个角色了。</p>
</blockquote>
<blockquote>
<p>这个行为与查询集文档中提到的 distinct() 一样，而且生成规则也一样：一般情况下，你不想在结果中由额外的字段扮演这个角色，那就清空排序项，或是至少保证它仅能访问 values()中的字段。</p>
</blockquote>
<h2 id="静态文件">静态文件</h2>
<p>http://python.usyiyi.cn/django/intro/tutorial06.html<br>
http://python.usyiyi.cn/django/ref/templates/builtins.html</p>
<pre><code class="language-html">{% load static %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static user_stylesheet %}&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static 'polls/style.css' %}&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% get_static_prefix %}pools/style.css&quot; /&gt;
{% get_static_prefix as STATIC_PREFIX %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{{ STATIC_PREFIX }}pools/style.css&quot; /&gt;
{% static &quot;images/hi.jpg&quot; as myphoto %}
&lt;img src=&quot;{{ myphoto }}&quot;&gt;&lt;/img&gt;
</code></pre>
<p>还有<code>get_media_prefix</code></p>
<h2 id="模型实例参考"><a href="http://python.usyiyi.cn/django/ref/models/instances.html#django.db.models.Model">模型实例参考</a></h2>
<p>从数据库中重新加载值<br>
<code>Model.refresh_from_db(using=None, fields=None, **kwargs)</code></p>
<p>返回模型中当前所有延迟字段的属性名称<br>
<code> Model.get_deferred_fields()</code></p>
<p>验证对象</p>
<blockquote>
<p>字段的基本验证会最先跑，但不管前面运行是否通过，对于每个字段，如果Field.clean() 方法抛出 ValidationError，那么将不会调用该字段对应的clean_<fieldname>()方法。 但是，剩余的字段的验证方法仍然会执行。<br>
先跑<code>form</code>里验证，再跑<code>modle</code>验证<br>
先跑验证器，再跑<code>clean</code><br>
先跑单个字段验证，再跑整体验证<br>
<code>Model.clean_field()</code>会覆盖<code>Model</code>里所有字段的验证器，但不会对<code>Form</code>里的验证器产生影响</p>
</blockquote>
<p>验证模型的字段<code>Model.clean_fields(exclude=None)</code><br>
验证模型的完整性<code>Model.clean()</code><br>
验证模型的唯一性<code>Model.validate_unique(exclude=None)</code><br>
调用<code>full_clean()</code>时，上面三个方法都会执行（执行顺序即上面的书写顺序），<code>ModelForm</code>的<code>is_valid()</code>也会执行上所有验证<br>
<code>Model.full_clean(exclude=None, validate_unique=True)</code><br>
<code>save()</code>时，<code>full_clean()</code>不会被调用，如果想验证数据，可手动调用</p>
<p><code>Model.clean()</code>时，引发特定字段的异常<br>
使用一个字典实例化<code>ValidationError</code>即可或使用<code>add_error(field, msg)</code>方法</p>
<p>在数据库字段值的基础上进行简单的算法操作，应该尽量使用<code>F()</code>表达式，避免问题竞态条件</p>
<blockquote>
<p>指定要保存的字段</p>
</blockquote>
<blockquote>
<p>如果传递给save() 的update_fields 关键字参数一个字段名称列表，那么将只有该列表中的字段会被更新。如果你想更新对象的一个或几个字段，这可能是你想要的。不让模型的所有字段都更新将会带来一些轻微的性能提升。例如：</p>
</blockquote>
<pre><code class="language-python">product.name = 'Name changed again'
product.save(update_fields=['name'])
</code></pre>
<blockquote>
<p><code>update_fields</code> 参数可以是任何包含字符串的可迭代对象。空的<code>update_fields</code>可迭代对象将会忽略保存。如果为<code>None</code>值，将执行所有字段上的更新。</p>
</blockquote>
<blockquote>
<p>指定<code>update_fields</code>将强制使用更新操作。</p>
</blockquote>
<blockquote>
<p>当保存通过延迟模型加载（<code>only()</code> 或<code>defer()</code>）进行访问的模型时，只有从数据库中加载的字段才会得到更新。这种情况下，有个自动的<code>update_fields</code>。如果你赋值或者改变延迟字段的值，该字段将会添加到更新的字段中。</p>
</blockquote>
<p>new in 1.9<br>
使用<code>Model.delete()</code>删除多表继承的子表数据时，使用``keep_parents=True<code>可以保留上级数据，默认为</code>False`<br>
返回值为删除数据的条数</p>
<p><code>DateField</code>和<code>DateTimeField</code>字段如果<code>null=False</code>则支持下面两个方法</p>
<pre><code class="language-python">Model.get_next_by_FOO(**kwargs)¶ 
Model.get_previous_by_FOO(**kwargs)
</code></pre>
<h2 id="管理器"><a href="http://python.usyiyi.cn/django/topics/db/managers.html">管理器</a></h2>
<p>django遇到的第一个管理器为默认管理器</p>
<p>如果需要访问关联对象调用关联对象的默认管理器，需要在管理器中加<code>use_for_related_fields=True</code>，不然会调用朴素管理器</p>
<pre><code class="language-python"># -*- coding: utf-8 -*-
 
from django.db import models
 
 
class DefaultManager(models.Manager):
    def get_queryset(self):
        queryset = super(DefaultManager, self).get_quertset().filter(is_delete=False)
        return queryset
 
 
class Author(models.Model):
    name = models.CharField(max_length=100)
    is_delete = models.BooleanField()
    objects = DefaultManager()
 
 
class Post(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=100)
    content = models.TextField()
    is_delete = models.BooleanField()
    objects = DefaultManager()
 
 
author = Author.objects.get(pk=1)
post = Post.objects.get(pk=2)
 
# 调用DefaultManager管理器
author.post_set.all()
# 调用朴素管理器，如果要调用DefaultManager管理器，需要设置DefaultManager管理器的类变量use_for_related_fields=True
post.author
</code></pre>
<p><em>注：朴素管理器里找不到的方法会在默认管理器里查找</em></p>
<p><a href="http://python.usyiyi.cn/django/topics/db/managers.html#calling-custom-queryset-methods-from-the-manager">从Manager中调用自定义的QuerySet</a></p>
<h2 id="进行原始的sql查询"><a href="http://python.usyiyi.cn/django/topics/db/sql.html">进行原始的SQL查询</a></h2>
<pre><code class="language-python">Manager.raw(raw_query, params=None, translations=None)
</code></pre>
<blockquote>
<p>django.db.connection对象提供了常规数据库连接的方式。为了使用数据库连接，先要调用connection.cursor()方法来获取一个游标对象之后，调用cursor.execute(sql, [params])来执行sql语句，调用cursor.fetchone()或者cursor.fetchall()来返回结果行。</p>
</blockquote>
<h2 id="数据库事务"><a href="http://python.usyiyi.cn/django/topics/db/transactions.html">数据库事务</a></h2>
<p>详细笔记见django1.8事务.md</p>
<p>将每个HTTP请求封装在一个数据库事务中<br>
settings中设置<code>ATOMIC_REQUESTS=True</code></p>
<p>单独给一个方法加上数据库事务控制使用<code>atomic</code></p>
<pre><code class="language-python">from django.db import transaction
 
@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()
</code></pre>
<p>或</p>
<pre><code class="language-python">from django.db import transaction
 
def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()
 
    with transaction.atomic():
        # This code executes inside a transaction.
        do_more_stuff()
</code></pre>
<p>避免在 atomic里捕获异常!</p>
<h2 id="查询表达式">查询表达式</h2>
<p>使用数据库的方法</p>
<pre><code class="language-python">from django.db.models import Func, F 
queryset.annotate(field_lower=Func(F('field'), function='LOWER'))
</code></pre>
<p>或</p>
<pre><code class="language-python">class Lower(Func):
    function = 'LOWER' 
queryset.annotate(field_lower=Lower(F('field')))
</code></pre>
<h2 id="条件表达式"><a href="http://python.usyiyi.cn/django/ref/models/conditional-expressions.html">条件表达式</a></h2>
<p>高级用法查看在线版<br>
<code>When</code><br>
<code>Case</code></p>
<h2 id="数据库函数"><a href="http://python.usyiyi.cn/django/ref/models/database-functions.html">数据库函数</a></h2>
<p><code>Coalesce</code> 接收一个含有至少两个字段名称或表达式的列表，返回第一个非空的值（空字符串不认为是一个空值）</p>
<h2 id="将遗留数据库整合到django"><a href="http://python.usyiyi.cn/django/howto/legacy-databases.html">将遗留数据库整合到Django</a></h2>
<p>根据遗留数据库生成models</p>
<pre><code class="language-python">python manage.py inspectdb &gt; models.py
</code></pre>
<h2 id="为模型提供初始数据"><a href="http://python.usyiyi.cn/django/howto/initial-data.html">为模型提供初始数据</a></h2>
<p>使用fixtures</p>
<pre><code>[
  {
    &quot;model&quot;: &quot;myapp.person&quot;,
    &quot;pk&quot;: 1,
    &quot;fields&quot;: {
      &quot;first_name&quot;: &quot;John&quot;,
      &quot;last_name&quot;: &quot;Lennon&quot;
    }
  },
  {
    &quot;model&quot;: &quot;myapp.person&quot;,
    &quot;pk&quot;: 2,
    &quot;fields&quot;: {
      &quot;first_name&quot;: &quot;Paul&quot;,
      &quot;last_name&quot;: &quot;McCartney&quot;
    }
  }
]
</code></pre>
<p>导入数据命令</p>
<pre><code>python manage.py loaddata &lt;fixturename&gt;
</code></pre>
<h2 id="数据库访问优化">数据库访问优化</h2>
<p>添加索引，比任何查询语法优化都来的重要<br>
理解查询集<br>
QuerySets是延迟的。<br>
什么时候它们被计算出来。<br>
数据在内存中如何存储。</p>
<p>使用<code>cached_property</code>装饰器，只要是同一个实例，一个方法就只会执行一次<br>
使用<code>with</code>模版标签<br>
使用<code>iterator</code>迭代器</p>
<p>在数据库中而不是python中做数据库工作<br>
使用过滤器和反射过滤器对数据进行过滤<br>
使用<code>F()</code>表达式<br>
使用注解和聚合<br>
使用原始SQL</p>
<p>用唯一的或被索引的列来检索独立对象</p>
<p>在不同位置多次访问数据库，每次获取一个数据集，不如在一次查询中获取它们。比如循环的时候。</p>
<p>使用<code>select_related()</code>和<code>prefetch_related()</code></p>
<p>不检索你不需要的信息<br>
使用<code>QuerySet.values()</code>和<code>QuerySet.values_list()</code></p>
<p>使用<code>QuerySet.defer()</code>和<code>QuerySet.only()</code></p>
<p>计算数量不要使用<code>len(queryset)</code>而是使用<code>QuerySet.count()</code></p>
<p>判断是否存在结果使用<code>QuerySet.exists()</code>而不是用<code>if queryset</code></p>
<p>但不要过度使用<code>count()</code>和<code>exists()</code>，如果你本来就需要里面的数据，那就不要使用</p>
<p>使用<code>QuerySet.update()</code>和<code>QuerySet.delete()</code>批量操作数据</p>
<p>直接使用外键的值</p>
<pre><code class="language-python">entry.blog_id
# 而不是
entry.blog.id
</code></pre>
<p>如果你并在意结果集的顺序，不要进行排序，移除<code>Meta.ordering</code></p>
<p>创建对象时尽可能使用<code>bulk_create()</code>来减少sql查询数量<br>
这也适用于<code>ManyToManyFields</code>的情况，一起<code>add</code>而不是一个一个<code>add</code></p>
<pre><code class="language-python">my_band.members.add(me, my_friend) 
#更优于 
my_band.members.add(me)
my_band.members.add(my_friend)
</code></pre>
<h2 id="url调度器"><a href="http://python.usyiyi.cn/django/topics/http/urls.html">URL调度器</a></h2>
<p>url捕获的参数永远是字符串</p>
<p>在根url上获取的参数不影响参数传递</p>
<pre><code class="language-python"># In settings/urls/main.py
from django.conf.urls import include, url
 
urlpatterns = [
    url(r'^(?P&lt;username&gt;\w+)/blog/', include('foo.urls.blog')),
]
 
# In foo/urls/blog.py
from django.conf.urls import url
from . import views
 
urlpatterns = [
    url(r'^$', views.blog.index),
    url(r'^archive/$', views.blog.archive),
]
</code></pre>
<p>在上面的例子中，捕获的&quot;username&quot;变量将被如期传递给include()指向的URLconf。</p>
<p>可嵌套</p>
<pre><code class="language-python">from django.conf.urls import url
 
urlpatterns = [
    url(r'blog/(page-(\d+)/)?$', blog_articles),                  # bad
    url(r'comments/(?:page-(?P&lt;page_number&gt;\d+)/)?$', comments),  # good
]
</code></pre>
<p>传递额外的参数</p>
<pre><code class="language-python">from django.conf.urls import url
from . import views
 
urlpatterns = [
    url(r'^blog/(?P&lt;year&gt;[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]
</code></pre>
<p>当url捕获的参数和字典中传递的参数同名时，将忽略url捕获的参数而使用字典里的参数值</p>
<p>传递额外的参数给<code>include()</code></p>
<pre><code class="language-python"># main.py
from django.conf.urls import include, url
 
urlpatterns = [
    url(r'^blog/', include('inner'), {'blogid': 3}),
]
 
# inner.py
from django.conf.urls import url
from mysite import views
 
urlpatterns = [
    url(r'^archive/$', views.archive),
    url(r'^about/$', views.about),
]
</code></pre>
<p>效果等同</p>
<pre><code class="language-python"># main.py
from django.conf.urls import include, url
from mysite import views
 
urlpatterns = [
    url(r'^blog/', include('inner')),
]
 
# inner.py
from django.conf.urls import url
 
urlpatterns = [
    url(r'^archive/$', views.archive, {'blogid': 3}),
    url(r'^about/$', views.about, {'blogid': 3}),
]
</code></pre>
<p><a href="http://python.usyiyi.cn/django/topics/http/urls.html#reversing-namespaced-urls">反查带命名空间的URL</a></p>
<h2 id="编写视图"><a href="http://python.usyiyi.cn/django/topics/http/views.html">编写视图</a></h2>
<p><code>HttpResponse</code>子类，状态码</p>
<ul>
<li><code>HttpResponseRedirect</code> 临时重定向，302</li>
<li><code>HttpResponsePermanentRedirect</code> 永久重定向，301</li>
<li><code>HttpResponseNotModified</code> 没有任何修改，304</li>
<li><code>HttpResponseBadRequest</code> 语义有误码，当前请求不被服务器理解，400</li>
<li><code>HttpResponseNotFound</code> 页面没找到，404</li>
<li><code>HttpResponseForbidden</code> 服务器理解请求，但拒绝执行，403</li>
<li><code>HttpResponseNotAllowed</code> 请求中指定的请求方式不能用于请求相应资源，405</li>
<li><code>HttpResponseGone</code> 请求的资源在服务器上已经不可用，而且没有已知的转发地址，410</li>
<li><code>HttpResponseServerError</code> 服务器遇到了一个意外的错误，导致无法完成对请求的处理，500</li>
<li><code>HttpResponse(status=201)</code> 自定义返回状态码</li>
</ul>
<p>重写错误视图（在url中）</p>
<pre><code>handler404 = 'mysite.views.my_custom_page_not_found_view'
handler500 = 'mysite.views.my_custom_error_view' 
handler403 = 'mysite.views.my_custom_permission_denied_view'
handler400 = 'mysite.views.my_custom_bad_request_view' 
</code></pre>
<h2 id="django-的快捷函数"><a href="http://python.usyiyi.cn/django/topics/http/shortcuts.html">Django 的快捷函数</a></h2>
<p><code>template_name</code>可传一个模版序列，django将使用存在的第一个模版</p>
<p><code>redirect(to, [permanent=False, ]*args, **kwargs)[source]</code></p>
<blockquote>
<p>为传递进来的参数返回HttpResponseRedirect 给正确的URL 。<br>
参数可以是：</p>
<pre><code>一个模型：将调用模型的get_absolute_url() 函数
一个视图，可以带有参数：将使用urlresolvers.reverse 来反向解析名称
一个绝对的或相对的URL，将原样作为重定向的位置。
</code></pre>
<p>默认返回一个临时的重定向；传递permanent=True 可以返回一个永久的重定向。</p>
</blockquote>
<p><code>get_object_or_404(klass, *args, **kwargs)</code>可以传Model也可以传QuerySet实例和关联的管理器<br>
<code>get_list_or_404(klass, *args, **kwargs)</code>可以传Model也可以传QuerySet实例和关联的管理器</p>
<h2 id="视图装饰器"><a href="http://python.usyiyi.cn/django/topics/http/decorators.html">视图装饰器</a></h2>
<p><a href="http://python.usyiyi.cn/django/topics/conditional-view-processing.html">按需内容处理</a><br>
<code>django.views.decorators.http</code>包里的装饰器可以基于请求的方法来限制对视图的访问。若条件不满足会返回 django.http.HttpResponseNotAllowed。<br>
<code>require_http_methods(request_method_list)</code>限制视图只能服务于规定的http方法（需要大写）<br>
<code>require_GET()</code><br>
<code>require_POST()</code><br>
<code>require_safe()</code>只允许视图接受GET和HEAD方法的装饰器。</p>
<pre><code class="language-python">@condition(etag_func=None, last_modified_func=None)
</code></pre>
<pre><code class="language-python">@last_modified(last_modified_func)
</code></pre>
<p>根据最后修改时间来决定是否运行视图，可减少流量</p>
<pre><code class="language-python">@etag(etag_func)
</code></pre>
<p><code>etag</code>（版本？）和<code>last_modified</code>不能同时使用</p>
<p><code>GZip</code>对内容进行压缩，节省流量，但增加处理时间</p>
<p><code>vary_on_cookie</code><br>
<code>vary_on_headers</code><br>
基于特定的请求头部来控制缓存</p>
<p><code>never_cache</code></p>
<h2 id="request-对象和response-对象"><a href="http://python.usyiyi.cn/django/ref/request-response.html">Request 对象和Response 对象</a></h2>
<p><code>HttpRequest</code>对象(除非特殊说明，所有属性都是只读，<code>session</code>属性是个例外)<br>
<code>HttpRequest.scheme</code> 请求方案（通常为http或https）<br>
<code>HttpRequest.body</code> 字节字符串，表示原始http请求正文<br>
<code>HttpRequest.path</code> 字符串，表示请求的页面的完整路径，不包含域名<br>
<code>HttpRequest.path_info</code>    在某些Web 服务器配置下，主机名后的URL 部分被分成脚本前缀部分和路径信息部分。path_info 属性将始终包含路径信息部分，不论使用的Web 服务器是什么。使用它代替path 可以让代码在测试和开发环境中更容易地切换。<br>
例如，如果应用的WSGIScriptAlias 设置为&quot;/minfo&quot;，那么当path 是&quot;/minfo/music/bands/the_beatles/&quot; 时path_info 将是&quot;/music/bands/the_beatles/&quot;。<br>
<code>HttpRequest.method</code> 请求使用的http方法，大写<br>
<code>HttpRequest.encoding</code> 表示提交的数据的编码方式，可写<br>
<code>HttpRequest.GET</code><br>
<code>HttpRequest.POST</code><br>
<code>HttpRequest.REQUEST</code>不建议使用，使用<code>GET</code>和<code>POST</code>代替<br>
<code>HttpRequest.COOKIES</code> 字典，键和值都是字符串<br>
<code>HttpRequest.FILES</code> 类似字典的对象，包含所有的上传文件，<form>带有<code>enctype=&quot;multipart/form-data&quot;</code>才会有数据<br>
<code>HttpRequest.META</code> 标准的python字典，包含所有http请求头部<br>
<code>HttpRequest.user</code><br>
<code>HttpRequest.session</code> 类似字典的对象<br>
<code>HttpRequest.urlconf</code> 如果其它地方设置了，则用来取代<code>ROOT_URLCONF </code><br>
<code>HttpRequest.resolver_match</code> 会在url解析之后设置，一个<code>ResolverMatch</code>实例，表示解析之后的url<br>
<code>HttpRequest.get_host()</code> 获取原始主机地址<br>
<code>HttpRequest.get_port()</code> 获取请求端端口号<br>
<code>HttpRequest.get_full_path()</code> 返回完整的path，包括查询字符串<br>
<code>HttpRequest.build_absolute_uri(location)</code> 返回绝对url<br>
<code>HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)</code> 返回签名过的Cookie对应的值<br>
<code>HttpRequest.is_secure()</code> 如果请求是通过https发起的，则返回True<br>
<code>HttpRequest.is_ajax()</code> 如果请求是通过XMLHttpRequest发起的，则返回True</p>
<pre><code class="language-python">HttpRequest.read(size=None)
HttpRequest.readline()
HttpRequest.readlines()
HttpRequest.xreadlines()
HttpRequest.__iter__()
</code></pre>
<p>这几个方法实现类文件的接口用于读取HttpRequest· 实例</p>
<p><code>QueryDict</code>对象<br>
request.POST 和request.GET 的QueryDict 在一个正常的请求/响应循环中是不可变的。若要获得可变的版本，需要使用.copy()。</p>
<h2 id="templateresponse-和simpletemplateresponse"><a href="http://python.usyiyi.cn/django/ref/template-response.html">TemplateResponse 和SimpleTemplateResponse</a></h2>
<p><code>SimpleTemplateResponse</code><br>
<code>TemplateResponse</code><br>
TemplateResponse 对象和普通的django.http.HttpResponse 一样可以用于任何地方。它可以用来作为render() 和render_to_response() 的另外一种选择。</p>
<p>例如，下面这个简单的视图使用一个简单模板和包含查询集的上下文返回一个TemplateResponse：</p>
<pre><code class="language-python">from django.template.response import TemplateResponse
 
def blog_index(request):
    return TemplateResponse(request, 'entry_list.html', {'entries': Entry.objects.all()})
</code></pre>
<h2 id="文件上传"><a href="http://python.usyiyi.cn/django/topics/http/file-uploads.html">文件上传</a></h2>
<pre><code class="language-python">def handle_uploaded_file(f):
    with open('some/file/name.txt', 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)
</code></pre>
<p>遍历UploadedFile.chunks()，而不是使用read()，能确保大文件并不会占用系统过多的内存。</p>
<p>上传处理器</p>
<pre><code>(&quot;django.core.files.uploadhandler.MemoryFileUploadHandler&quot;,
&quot;django.core.files.uploadhandler.TemporaryFileUploadHandler&quot;,)
</code></pre>
<p>MemoryFileUploadHandler 和TemporaryFileUploadHandler一起提供了Django的默认文件上传行为，将小文件读取到内存中，大文件放置在磁盘中。</p>
<p>你可以编写自定义的处理器，来定制Django如何处理文件。例如，你可以使用自定义处理器来限制用户级别的配额，在运行中压缩数据，渲染进度条，甚至是向另一个储存位置直接发送数据，而不把它存到本地。关于如何自定义或者完全替换处理器的行为，详见编写自定义的上传处理器。</p>
<p>更改上传处理器的行为</p>
<pre><code>DEFAULT_FILE_STORAGE
FILE_CHARSET
FILE_UPLOAD_HANDLERS
FILE_UPLOAD_MAX_MEMORY_SIZE
FILE_UPLOAD_PERMISSIONS
FILE_UPLOAD_TEMP_DIR
MEDIA_ROOT
MEDIA_URL
</code></pre>
<p>在运行中更改上传处理器</p>
<pre><code class="language-pyhton">request.upload_handlers.insert(0, ProgressBarUploadHandler())
</code></pre>
<blockquote>
<p>注意</p>
</blockquote>
<blockquote>
<p>你只可以在访问request.POST或者request.FILES之前修改上传处理器-- 在上传处理工作执行之后再修改上传处理就毫无意义了。如果你在读取request.FILES之后尝试修改request.upload_handlers，Django会抛出异常。</p>
</blockquote>
<blockquote>
<p>所以，你应该在你的视图中尽早修改上传处理器。</p>
</blockquote>
<blockquote>
<p>CsrfViewMiddleware 也会访问request.POST，它是默认开启的。意思是你需要在你的视图中使用csrf_exempt()，来允许你修改上传处理器。接下来在真正处理请求的函数中，需要使用csrf_protect()。注意这意味着处理器可能会在CSRF验证完成之前开始接收上传文件。例如：</p>
</blockquote>
<blockquote>
<pre><code class="language-from"></code></pre>
</blockquote>
<p>@csrf_exempt<br>
def upload_file_view(request):<br>
request.upload_handlers.insert(0, ProgressBarUploadHandler())<br>
return _upload_file_view(request)<br>
@csrf_protect<br>
def _upload_file_view(request):<br>
... # Process request<br>
```</p>
<pre><code>
## [File对象](http://python.usyiyi.cn/django/ref/files/file.html)
`File`类
`ContentFile`类
`ImageFile`类 比`File`多了`width`和`height`属性
附加到对象的文件有额外的方法
</code></pre>
<p>File.save(name, content[, save=True])</p>
<pre><code>提供文件名和内容保存一个新文件，不会替换已存在文件，但会创建一个新文件，并且更新对象来指向它。
测试出来直接`car.save()`也不会覆盖已存在文件，如果有重写会在原有名字后面加字符串
如果save为True，模型的save()方法会在文件保存之后调用。这就是说，下面两行：
​```python
&gt;&gt;&gt; car.photo.save('myphoto.jpg', content, save=False)
&gt;&gt;&gt; car.save()
</code></pre>
<p>等价于：</p>
<pre><code class="language-python">&gt;&gt;&gt; car.photo.save('myphoto.jpg', content, save=True)
</code></pre>
<p>从模型实例中移除文件，并且删除内部文件</p>
<pre><code> File.delete([save=True])
</code></pre>
<p>在页面展示中，<code>ImageFile</code>自带的清除勾选框勾选后只是清除了数据库中这具字段的值，并不会删除文件系统里对应的文件，而<code>File.delete()</code>会删除文件系统里的文件</p>
<h2 id="文件储存api"><a href="http://python.usyiyi.cn/django/ref/files/storage.html">文件储存API</a></h2>
<p><code>DefaultStorage</code><br>
<code>FileSystemStorage</code><br>
<code>Storage</code></p>
<h2 id="管理文件"><a href="http://python.usyiyi.cn/django/topics/files.html">管理文件</a></h2>
<pre><code class="language-python">from django.db import models
 
class Car(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    photo = models.ImageField(upload_to='cars')
</code></pre>
<p><code>photo</code>有以下方法<br>
<code>photo.path</code>相对路径<br>
<code>photo.url</code>绝对路径</p>
<p><em>实际测试有出入</em></p>
<pre><code class="language-python"># 官方示例
&gt;&gt;&gt; car.photo.path
'/media/cars/chevy.jpg'
&gt;&gt;&gt; car.photo.url
'http://media.example.com/cars/chevy.jpg'

# 实际测试结果
&gt;&gt;&gt; car.photo.path
'E:\workspace\parking\parking\upload\20151230171832_0.jpg'
&gt;&gt;&gt; car.photo.url
'/upload/20151230171832_0.jpg'
</code></pre>
<p>更改一个文件的存储位置</p>
<pre><code class="language-python">&gt;&gt;&gt; import os
&gt;&gt;&gt; from django.conf import settings
&gt;&gt;&gt; initial_path = car.photo.path
&gt;&gt;&gt; car.photo.name = 'cars/chevy_ii.jpg'
&gt;&gt;&gt; new_path = settings.MEDIA_ROOT + car.photo.name
&gt;&gt;&gt; # Move the file on the filesystem
&gt;&gt;&gt; os.rename(initial_path, new_path)
&gt;&gt;&gt; car.save()
&gt;&gt;&gt; car.photo.path
'/media/cars/chevy_ii.jpg'
&gt;&gt;&gt; car.photo.path == new_path
True
</code></pre>
<h2 id="编写自定义存储系统"><a href="http://python.usyiyi.cn/django/howto/custom-file-storage.html">编写自定义存储系统</a></h2>
<ol>
<li>必须是<code>django.core.files.storage.Storage</code>的子类</li>
<li>Django必须能够不带任何参数来实例化</li>
<li>必须实现 _open() 和 _save()方法，以及任何适合于你的储存类的其它方法</li>
<li>你的储存类必须是 可以析构的，所以它在迁移中的一个字段上使用的时候可以被序列化。只要你的字段拥有自己可以序列化的参数，你就可以为它使用django.utils.deconstruct.deconstructible类装饰器（这也是Django用在FileSystemStorage上的东西）</li>
</ol>
<h2 id="基于类的视图"><a href="http://python.usyiyi.cn/django/topics/class-based-views/index.html">基于类的视图</a></h2>
<h2 id="基于类的内建通用视图"><a href="http://python.usyiyi.cn/django/topics/class-based-views/generic-display.html">基于类的内建通用视图</a></h2>
<p><code>ListView</code>类视图中，默认的对象列表名除了<code>object_list</code>，还有一个<code>&lt;model_name&gt;_list</code></p>
<h2 id="使用基于类的视图处理表单"><a href="http://python.usyiyi.cn/django/topics/class-based-views/generic-editing.html">使用基于类的视图处理表单</a></h2>
<p>如果对应模型存在<code>get_absolute_url</code>方法的前提下<code>CreateView</code>和<code>UpdateView</code>类视图的<code>success_url</code>默认使用<code>get_absolute_url</code></p>
<p>如何定义<code>form_class</code>，即使<code>form_class</code>是<code>ModelForm</code>也还是需要指定模型</p>
<p>如果没有定义<code>form_class</code>，则必须定义<code>fields</code>，<code>fields</code>和<code>form_class</code>不能同时存在</p>
<p>如果模型某个字段存的是模板路径，并且想通过此字段来动态的控制表单页的模板，可通过<code>template_name_field</code>来指定此字段。</p>
<h2 id="mixin"><a href="http://python.usyiyi.cn/django/topics/class-based-views/mixins.html">Mixin</a></h2>
<h2 id="基于类的视图的mixin"><a href="http://python.usyiyi.cn/django/ref/class-based-views/mixins.html">基于类的视图的Mixin</a></h2>
<p><code>ContextMixin</code>所有基于类的通用视图的这个模板Context 都包含一个view 变量指向视图实例。</p>
<blockquote>
<p>Use alters_data where appropriate<br>
注意，将视图实例包含在模板Context 中可能将有潜在危险的方法暴露给模板作者。为了避免在模板中被调用类似这样的方法，可以在这些方法上设置alters_data=True。更多信息，参见渲染模板Context 的文档。<br>
很显然，调用某些变量会带来副作用，允许模板系统访问它们将是愚蠢的还会带来安全漏洞。<br>
每个Django 模型对象的delete() 方法就是一个很好的例子。模板系统不应该允许下面的行为：<br>
I will now delete this valuable data. {{ data.delete }}<br>
设置可调用变量的alters_data 属性可以避免这点。如果变量设置alters_data=True ，模板系统将不会调用它，而会无条件使用string_if_invalid 替换这个变量。Django 模型对象自动生成的delete() 和save() 方法自动 设置alters_data=True。 例如：</p>
</blockquote>
<pre><code class="language-python">def sensitive_function(self):
        self.database_record.delete()
    sensitive_function.alters_data = True
</code></pre>
<blockquote>
<p>有时候，处于某些原因你可能想关闭这个功能，并告诉模板系统无论什么情况下都不要调用变量。设置可调用对象的do_not_call_in_templates 属性的值为True 可以实现这点。模板系统的行为将类似这个变量是不可调用的（例如，你可以访问可调用对象的属性）。<br>
<code>query_pk_and_slug</code>如果为<code>True</code>,<code>get_object()</code>将使用两者一起来查找。可以防止只使用<code>pk</code>时，如果<code>pk</code>连续，直接被攻击者都遍历<code>pk</code>获取整个列表</p>
</blockquote>
<h2 id="内建基于类的视图的api"><a href="http://python.usyiyi.cn/django/ref/class-based-views/index.html">内建基于类的视图的API</a></h2>
<pre><code class="language-python">urlpatterns = [
    url(r'^view/$', MyView.as_view(size=42)),
]
</code></pre>
<blockquote>
<p>视图参数的线程安全性<br>
传递给视图的参数在视图的每个实例之间共享。这表示不应该使用列表、字典或其它可变对象作为视图的参数。如果你真这么做而且对共享的对象做过修改，某个用户的行为可能对后面访问同一个视图的用户产生影响。</p>
</blockquote>
<h2 id="基于类的通用视图-索引"><a href="http://python.usyiyi.cn/django/ref/class-based-views/flattened-index.html">基于类的通用视图 —— 索引</a></h2>
<h2 id="使用django输出csv"><a href="http://python.usyiyi.cn/django/howto/outputting-csv.html">使用Django输出CSV</a></h2>
<pre><code class="language-python">import csv
from django.http import HttpResponse
 
def some_view(request):
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename=&quot;somefilename.csv&quot;'
 
    writer = csv.writer(response)
    writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
    writer.writerow(['Second row', 'A', 'B', 'C', '&quot;Testing&quot;', &quot;Here's a quote&quot;])
 
    return response
</code></pre>
<h2 id="使用django输出pdf"><a href="http://python.usyiyi.cn/django/howto/outputting-pdf.html">使用Django输出PDF</a></h2>
<pre><code class="language-python">from reportlab.pdfgen import canvas
from django.http import HttpResponse
 
def some_view(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=&quot;somefilename.pdf&quot;'
 
    # Create the PDF object, using the response object as its &quot;file.&quot;
    p = canvas.Canvas(response)
 
    # Draw things on the PDF. Here's where the PDF generation happens.
    # See the ReportLab documentation for the full list of functionality.
    p.drawString(100, 100, &quot;Hello world.&quot;)
 
    # Close the PDF object cleanly, and we're done.
    p.showPage()
    p.save()
    return response
</code></pre>
<h2 id="中间件"><a href="http://python.usyiyi.cn/django/topics/http/middleware.html">中间件</a></h2>
<p>中间件的顺序很重要<br>
接受请求时，自上向下调用中间件<br>
返回响应时，自下向上调用中间件<br>
<code>process_request(request)</code><br>
在django决定执行哪个视图之前（也就是解析url之前）被调用<br>
返回<code>None</code>继续处理请求<br>
返回<code>HttpResponse</code>不再去调用其它的request、view 或exception 中间件，或对应的视图，直接调用响应阶段的中间件，并返回结果</p>
<p><code>process_view(request, view_func, view_args, view_kwargs)</code><br>
<em>注：<code>view_args</code>和<code>view_kwargs</code>都不包含<code>request</code></em><br>
在django调用视图之前被调用<br>
返回<code>None</code>继续处理请求<br>
返回<code>HttpResponse</code>不再去调用其它的view 或exception 中间件，或对应的视图，直接调用响应阶段的中间件，并返回结果</p>
<blockquote>
<p>注意<br>
在中间件内部，从process_request 或process_view 中访问request.POST 或request.REQUEST 将阻碍该中间件之后的所有视图无法修改请求的上传处理程序，一般情况下要避免这样使用。<br>
类CsrfViewMiddleware可以被认为是个例外，因为它提供csrf_exempt() 和csrf_protect()两个装饰器，允许视图显式控制在哪个点需要开启CSRF验证。</p>
</blockquote>
<p><code>process_template_response(request, response)</code><br>
在视图刚好执行完毕之后被调用<br>
必须返回一个实现了<code>render</code>方法的响应对象</p>
<p><code>process_response(request, response)</code><br>
在所有响应返回浏览器之前被调用<br>
必须返回<code>HttpResponse</code>或者<code>StreamingHttpResponse</code>对象<br>
<em><strong><a href="http://python.usyiyi.cn/django/topics/http/middleware.html#dealing-with-streaming-responses">处理流式响应</a></strong></em></p>
<p><code>process_exception(request, exception)</code><br>
在视图抛出异常时被调用<br>
返回<code>None</code><br>
返回<code>HttpResponse</code> <code>process_template_response</code>和响应中间件会被调用<br>
<strong>在处理响应期间，中间件的执行顺序是倒序执行的，这包括process_exception，如果一个中间件的<code>process_exception</code>返回了一个响应，那么这个中间件上面的中间件中的<code>process_exception</code>都不会被调用</strong></p>
<p><code>__init__()</code><br>
大多数中间件类都不需要初始化方法<br>
django初始化中间件无需任何参数，所以不能定义一个有参数的<code>__init__方法 </code><strong>init</strong><code>不会每次请求都执行，只在Web服务器响应第一个请求时执行 标记中间件不被使用 </code><strong>init</strong><code>抛出</code>django.core.exceptions.MiddlewareNotUsed`异常，django会从中间件处理过程中移动这部分中间件，并且当DEBUG为True的时候在django.request记录器中记录调试信息。</p>
<ul>
<li>中间件类不能是任何类的子类</li>
<li>中间件可以放在python路径中的任务位置<br>
正常</li>
</ul>
<pre><code>A.init
B.init
C.init
D.init
A.process_request
B.process_request
C.process_request
D.process_request
A.process_view
B.process_view
C.process_view
D.process_view
 
D.process_template_response
C.process_template_response
B.process_template_response
A.process_template_response
D.process_responst
C.process_responst
B.process_responst
A.process_responst
</code></pre>
<p>视图异常</p>
<pre><code>A.init
B.init
C.init
D.init
A.process_request
B.process_request
C.process_request
D.process_request
A.process_view
B.process_view
C.process_view
D.process_view

D.process_responst
C.process_responst
B.process_responst
A.process_responst
</code></pre>
<h2 id="django中可用的中间件"><a href="http://python.usyiyi.cn/django/ref/middleware.html#middleware-ordering">django中可用的中间件</a></h2>
<h3 id="class-commonmiddleware"><code>class CommonMiddleware</code></h3>
<p><code>DISALLOWED_USER_AGENTS</code>禁用匹配的<code>user-agents</code>访问网站<br>
<code>APPEND_SLASH</code>如果url结尾没有斜杠结尾，并且没有找到匹配的url，django会在结尾加上斜杠再匹配一次<br>
<code>PREPEND_WWW</code>如果url会重定向到www到头的网址<br>
<code>USE_ETAGS</code>设置来处理ETag。如果设置USE_ETAGS为True，Django会通过MD5-hashing处理页面的内容来为每一个页面请求计算Etag，并且如果合适的话，它将会发送携带Not Modified的响应。</p>
<h3 id="class-brokenlinkemailsmiddleware"><code>class BrokenLinkEmailsMiddleware</code></h3>
<p>向<code>MANAGERS</code> 发送死链提醒邮件</p>
<h3 id="class-gzipmiddleware"><code>class GZipMiddleware</code></h3>
<p>为支持<code>GZip</code>压缩的浏览器压缩内容<br>
建议放在中间件配置列表的第一个<br>
可通过<code>gzip_page()</code>装饰器使用独立的<code>GZip</code>压缩</p>
<h3 id="class-conditionalgetmiddleware"><code>class ConditionalGetMiddleware</code></h3>
<h3 id="class-localemiddeware"><code>class LocaleMiddeware</code></h3>
<p>基于请求中的数据开启语言选择，它可以为每个用户进行定制。</p>
<h3 id="class-messagemiddleware"><code>class MessageMiddleware</code></h3>
<p>开启基于<code>Cookie</code>和会话的消息支持</p>
<h3 id="class-securitymiddleware"><code>class SecurityMiddleware</code></h3>
<p><a href="http://python.usyiyi.cn/django/ref/middleware.html#middleware-ordering">中间件的排序</a></p>
<h2 id="模版"><a href="http://python.usyiyi.cn/django/topics/templates.html">模版</a></h2>
<p><code>DjangoTemplates</code>引擎<code>OPTIONS</code>配置项中接受以下参数<br>
<code>string_if_invalid</code>当模版变量无效时，使用此值代替<br>
可使用<br>
comment<br>
和<br>
endcomment<br>
进行多行注释</p>
<h2 id="django模版语言"><a href="http://python.usyiyi.cn/django/ref/templates/language.html">Django模版语言</a></h2>
<p>当模版系统遇到<code>.</code>时，按下面顺序查询<br>
从技术上来说，当模版系统遇到点(&quot;.&quot;)，它将以这样的顺序查询：</p>
<ul>
<li>字典查询（Dictionary lookup）</li>
<li>属性或方法查询（Attribute or method lookup）</li>
<li>数字索引查询（Numeric index lookup）</li>
</ul>
<p>模版变量最终解释成字面量，而不是变量值</p>
<p>load<br>
可接受多个库名称<br>
load humanize i18n<br>
load<br>
不支持继承</p>
<h2 id="内置标签与过滤器"><a href="http://python.usyiyi.cn/django/ref/templates/builtins.html">内置标签与过滤器</a></h2>
<h3 id="标签">标签</h3>
<p><code>filter</code>对一段内容进行过滤，使用<code>|</code>对多个过滤器进行连接，且过滤器可以有参数<br>
<em>比如一段纯文本不能使用之前说的过滤器写法，则可以使用<code>filter</code></em><br>
<code>firstof</code>输出第一个不为<code>False</code>的参数</p>
<pre><code>{% firstof var1 var2|safe var3 &quot;&lt;strong&gt;fallback value&lt;/strong&gt;&quot;|safe %}
</code></pre>
<p><code>ifchanged</code>检查循环中的一个值从最近一次重复其是否改变，支持`else</p>
<p><code>with</code>可往<code>include</code>的模版里传上下文件变量</p>
<pre><code>{% include &quot;name_snippet.html&quot; with person=&quot;Jane&quot; greeting=&quot;Hello&quot; %}
</code></pre>
<pre><code>{% include &quot;name_snippet.html&quot; with greeting=&quot;Hi&quot; only %}
</code></pre>
<pre><code>{% lorem %}
</code></pre>
<p>设计人员工具，好像是生成随机单词和段落</p>
<pre><code class="language-django">{% lorem %}
{% lorem 3 p  %}
{% lorem 10 w random %}
</code></pre>
<h2 id="人性化"><a href="http://python.usyiyi.cn/django/ref/contrib/humanize.html">人性化</a></h2>
<p><code>apnumber</code>转换整数或整数的字符串形式为英文描述<br>
1 会变成one<br>
<code>intcomma</code>转换成第三位带一个逗号<br>
4500 会变成 4,500<br>
<code>intword</code>将大的整数转换为友好的文字表示<br>
1000000 会变成 1.0 million<br>
<code>naturalday</code>对于当天或者一天之内的日期， 返回“今天”，“明天”或者“昨天”，视情况而定。否则，使用传进来的格式字符串给日期格式化<br>
<code>naturaltime</code>对于日期时间的值，返回一个字符串来表示多少秒、分钟或者小时之前<br>
例如（其中“现在”是2007年2月17日16时30分0秒）：<br>
17 Feb 2007 16:30:00 会变成 now<br>
17 Feb 2007 16:29:31 会变成 29 seconds ago<br>
<code>ordinal</code>将一个整数或是整数的字符串，转换为它的序数词<br>
1 会变成 1st<br>
2 会变成  2nd<br>
3 会变成  3rd</p>
<h2 id="django-模板语言面向python程序员"><a href="http://python.usyiyi.cn/django/ref/templates/api.html">Django 模板语言：面向Python程序员</a></h2>
<p><code>string_if_invalid</code>建议只在调试时设置，调试完成后就关闭，开发时最好不要使用，不然可能会遇到渲染问题</p>
<p>每个上下文都包含<code>True</code> <code>False</code> <code>None</code></p>
<h3 id="使用context对象">[使用<code>Context</code>对象]</h3>
<p><em><a href="http://python.usyiyi.cn/django/ref/templates/api.html#playing-with-context-objects">这里比较难理解</a></em></p>
<pre><code class="language-python">Context.get(key, otherwise=None)
Context.pop()
Context.push()
Context.update(other_dict)
</code></pre>
<blockquote>
<p>上下文处理器应用的时机<br>
上下文处理器应用在上下文数据的顶端。也就是说，上下文处理器可能覆盖你提供给Context 或RequestContext 的变量，所以要注意避免与上下文处理器提供的变量名重复。<br>
如果想要上下文数据的优先级高于上下文处理器，使用下面的模式：</p>
<pre><code class="language-python">from django.template import RequestContext
request_context = RequestContext(request)
request_context.push({&quot;my_name&quot;: &quot;Adrian&quot;})
</code></pre>
</blockquote>
<pre><code>Django 通过这种方式允许上下文数据在render() 和 TemplateResponse 等API 中覆盖上下文处理器。
你还可以赋予`RequestContext `一个额外的处理器列表，使用第三个可选的位置参数processors。在下面的示例中，RequestContext 实例获得一个ip_address 变量
​```python
def some_view(request):
    # ...
    c = RequestContext(request, {
        'foo': 'bar',
    }, ['ip_address':'127.0.0.1'])
    return HttpResponse(t.render(c))
</code></pre>
<p>上面例子中<code>ip_address</code>也会加入到上下文中</p>
<h3 id="内建的模板上下文处理器">内建的模板上下文处理器</h3>
<p>下面是内奸的上下文处理器所添加的内容<br>
<code>django.contrib.auth.context_processors.auth</code></p>
<ul>
<li><code>user</code></li>
<li><code>perms</code></li>
</ul>
<p><code>django.template.context_processors.debug</code></p>
<ul>
<li>debug</li>
<li>sql_queryes<br>
一个{'sql': ..., 'time': ...} 字典的列表，表示请求期间到目前为止发生的每个SQL 查询及花费的时间。这个列表按查询的顺序排序，并直到访问时才生成。</li>
</ul>
<p><code>django.template.context_processors.i18n</code></p>
<ul>
<li><code>MEDIA_URL</code></li>
</ul>
<p><code>django.template.context_processors.static</code></p>
<ul>
<li><code>STATIC_URL</code></li>
</ul>
<p><code>django.template.context_processors.csrf</code></p>
<ul>
<li><code>csrf_token</code></li>
</ul>
<p><code>django.template.context_processors.request</code></p>
<ul>
<li><code>request</code></li>
</ul>
<p><code>django.contrib.messages.context_processors.messages</code></p>
<ul>
<li><code>messages</code></li>
<li><code>DEFAULT_MESSAGE_LEVELS</code></li>
</ul>
<h2 id="自定义模板标签和过滤器"><a href="http://python.usyiyi.cn/django/howto/custom-template-tags.html">自定义模板标签和过滤器</a></h2>
<h3 id="自定义过滤器">自定义过滤器</h3>
<pre><code class="language-python">from django import template
register = template.Library()
@register.filter(name='cut')
 
register.filter('cut', cut)
register.filter('lower', lower)
# or
def cut(value, arg):
    return value.replace(arg, '')
 
@register.filter
def lower(value):
    return value.lower()
</code></pre>
<p>可使用<code>SafeData</code>来验证是否是安全数据</p>
<pre><code class="language-python">if isinstance(value, SafeData):
    # Do something with the &quot;safe&quot; string.
    ...
</code></pre>
<p>或使用<code>is_safe</code>来控制只接收的安全的数据</p>
<pre><code class="language-python">@register.filter(is_safe=True)
def myfilter(value):
    return value
</code></pre>
<h3 id="自定义标签">自定义标签</h3>
<pre><code class="language-python">import datetime
from django import template
 
register = template.Library()
 
@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)
</code></pre>
<pre><code>{% show_results poll %}
</code></pre>
<p>写一个标签，实现下面的效果</p>
<pre><code class="language-html">&lt;ul&gt;
  &lt;li&gt;First choice&lt;/li&gt;
  &lt;li&gt;Second choice&lt;/li&gt;
  &lt;li&gt;Third choice&lt;/li&gt;
&lt;/ul&gt;
</code></pre>
<p>例子1开始</p>
<pre><code class="language-python">@register.inclusion_tag('results.html')
def show_results(poll):
    choices = poll.choice_set.all()
    return {'choices': choices}
</code></pre>
<p><code>results.html</code></p>
<pre><code class="language-html">&lt;ul&gt;
{% for choice in choices %}
    &lt;li&gt; {{ choice }} &lt;/li&gt;
{% endfor %}
&lt;/ul&gt;
</code></pre>
<p>例子1结束</p>
<p>可使用<code>takes_context=True</code>直接访问上下文件中的数据</p>
<pre><code class="language-python">@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
    # 因为takes_context=True所以这里的context就是上下文，可以从里面拿想要的数据，如果有多个参数，方法里的第一个参数名必须是context
    return {
        'link': context['home_link'],
        'title': context['home_title'],
    }
</code></pre>
<p><code>link.html</code></p>
<pre><code class="language-html">&lt;a href=&quot;{{ link }}&quot;&gt;{{ title }}&lt;/a&gt;.
</code></pre>
<p>页面直接写</p>
<pre><code class="language-html">{% jump_link %}
</code></pre>
<p>位置参数和关键字参数和<code>python</code>语法一样</p>
<pre><code class="language-python">@register.inclusion_tag('my_template.html')
def my_tag(a, b, *args, **kwargs):
    warning = kwargs['warning']
    profile = kwargs['profile']
    ...
    return ...
</code></pre>
<pre><code class="language-django">{% my_tag 123 &quot;abcd&quot; book.title warning=message|lower profile=user.profile %}
</code></pre>
<p>还有一个<code>register.assignment_tag</code>与<code>register.simple_tag</code>功能一样，不知道有什么特殊作用</p>
<h2 id="使用表单"><a href="http://python.usyiyi.cn/django/topics/forms/index.html">使用表单</a></h2>
<p>一些表单输入自带有html5的验证，要禁用这些验证可以设置<code>form</code>标签的<code>novalidate</code>属性</p>
<p><code>is_bound</code>可以判断一个表单是否具有绑定数据</p>
<pre><code class="language-python"># 未绑定表单
f = ContactForm()
data = {'subject': 'hello',
        'message': 'Hi there',
        'sender': 'foo@example.com',
        'cc_myself': True}
# 已绑定的表单
f = ContactForm(data)
</code></pre>
<p>当表单通过<code>is_valid()</code>方法验证后，可以直接在<code>form.cleaned_data</code>中拿值，并且是已经转换好的<code>python</code>格式的数据，但仍然可以从<code>request.POST</code>直接访问到未验证的数据。</p>
<p>表单排列<br>
<code>{{ form.as_table }}</code><br>
<code>{{ form.as_p }}</code><br>
<code>{{ form.as_ul }}</code></p>
<p>表单属性<br>
<code>{{ form.name }}</code>字段html标签<br>
<code>{{ form.name.label_tag }}</code>字段的<code>lable</code>html标签<br>
<code>{{ form.name.id_for_label }}</code>字段<code>lable</code>标签上的<code>for</code>值，也是字段标签上的<code>id</code></p>
<p><code>{{ form.hidden_fields }}</code>隐藏字段列表<br>
<code>{{ form.visible_fields }}</code>显示的字段列表</p>
<p>错误信息<br>
<code>{{ form.non_field_errors }}</code>不是特定字段的错误<br>
<code>{{ form.errors }}</code>全部错误，一个字典<br>
<code>{{ form.name.errors }}</code>字段错误</p>
<p>可从<code>form</code>从遍历出<code>field</code><br>
<code>{{ field }}</code>有以下属性<br>
<code>{{ field.label }}``Model</code>或是<code>Form</code>上的<code>label</code>的值<br>
<code>{{ field.label_tag }}</code>整个<code>label</code>标签，包含冒号<br>
<code>{{ field.id_for_label }}</code>字段的id<br>
<code>{{ field.value }}</code>字段的值<br>
<code>{{ field.html_name }}</code>字段的<code>name</code>，考虑表单的前缀<br>
<code>{{ field.help_text }}</code>字段的帮助文档<br>
<code>{{ field.errors }}</code>字段的错误<br>
<code>{{ field.is_hidden }}</code>判断字段是否隐藏<br>
<code>{{ field.field }}</code>表单类中<code>Field</code>的实例，可以使用它来访问<code>Field</code>属性，如</p>
<pre><code class="language-python">name.field.max_length
</code></pre>
<h2 id="表单-api"><a href="http://python.usyiyi.cn/django/ref/forms/api.html">表单 API</a></h2>
<pre><code class="language-python"># 未绑定表单
f = ContactForm()
data = {'subject': 'hello',
        'message': 'Hi there',
        'sender': 'foo@example.com',
        'cc_myself': True}
# 已绑定的表单
f = ContactForm(data)
</code></pre>
<p>表单实例一但创建，数据不可更改</p>
<h3 id="formclean"><code>Form.clean()</code></h3>
<h3 id="formis_valid"><code>Form.is_valid()</code></h3>
<h3 id="formerrors"><code>Form.errors</code></h3>
<blockquote>
<p><code>Form.errors</code><br>
访问errors 属性可以获得错误信息的一个字典：</p>
</blockquote>
<pre><code class="language-python">&gt;&gt;&gt; f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
</code></pre>
<p>在这个字典中，键为字段的名称，值为表示错误信息的Unicode 字符串组成的列表。错误信息保存在列表中是因为字段可能有多个错误信息。<br>
你可以在调用is_valid() 之前访问errors。表单的数据将在第一次调用is_valid() 或者访问errors 时验证。<br>
验证只会调用一次，无论你访问errors 或者调用is_valid() 多少次。这意味着，如果验证过程有副作用，这些副作用将只触发一次。</p>
<h3 id="formerrorsas_data"><code>Form.errors.as_data()</code></h3>
<blockquote>
<p>返回一个字典，它映射字段到原始的ValidationError 实例</p>
</blockquote>
<h3 id="formerrorsas_jsonescape_htmlfalse"><code>Form.errors.as_json(escape_html=False)</code></h3>
<blockquote>
<p>返回JSON 序列化后的错误。</p>
</blockquote>
<h3 id="formadd_errorfield-error"><code>Form.add_error(field, error)</code></h3>
<blockquote>
<p>这个方法允许在Form.clean() 方法内部或从表单的外部一起给字段添加错误信息<br>
Form.add_error() 会自动删除cleaned_data 中的相关字段</p>
</blockquote>
<h3 id="formhas_errorfield-codenone"><code>Form.has_error(field, code=None)</code></h3>
<blockquote>
<p>这个方法返回一个布尔值，指示一个字段是否具有指定错误code 的错误。当code 为None 时，如果字段有任何错误它都将返回True。<br>
若要检查非字段错误，使用NON_FIELD_ERRORS 作为field 参数。</p>
</blockquote>
<h3 id="formnon_field_errors"><code>Form.non_field_errors()</code></h3>
<blockquote>
<p>这个方法返回Form.errors 中不是与特定字段相关联的错误。它包含在Form.clean() 中引发的ValidationError 和使用Form.add_error(None, &quot;...&quot;) 添加的错误。</p>
</blockquote>
<p>未绑定表单的行为<br>
验证没有绑定数据的表单是没有意义的，下面的例子展示了这种情况：</p>
<pre><code class="language-python">&gt;&gt;&gt; f = ContactForm()
&gt;&gt;&gt; f.is_valid()
False
&gt;&gt;&gt; f.errors
{}
</code></pre>
<h3 id="forminitial"><code>Form.initial</code></h3>
<pre><code class="language-python">&gt;&gt;&gt; f = ContactForm(initial={'subject': 'Hi there!'})
</code></pre>
<p>这些值只显示在没有绑定的表单中，即使没有提供特定值它们也<em><strong>不会作为后备的值</strong></em>。<br>
优先级高于<code>Form</code>中的<code>initial</code></p>
<pre><code class="language-python">&gt;&gt;&gt; from django import forms
&gt;&gt;&gt; class CommentForm(forms.Form):
...     name = forms.CharField(initial='class')
...     url = forms.URLField()
...     comment = forms.CharField()
&gt;&gt;&gt; f = CommentForm(initial={'name': 'instance'}, auto_id=False)
&gt;&gt;&gt; print(f)
&lt;tr&gt;&lt;th&gt;Name:&lt;/th&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;name&quot; value=&quot;instance&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;Url:&lt;/th&gt;&lt;td&gt;&lt;input type=&quot;url&quot; name=&quot;url&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;Comment:&lt;/th&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;comment&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
</code></pre>
<h3 id="formhas_changed"><code>Form.has_changed()</code></h3>
<p><strong>也有<code>Field.has_changed()</code>方法</strong><br>
检查表单数据是否从初始数据发生改变<br>
当提交表单时，我们可以重新构建表单并提供初始值，这样可以实现比较：</p>
<pre><code class="language-python">&gt;&gt;&gt; f = ContactForm(request.POST, initial=data)
&gt;&gt;&gt; f.has_changed()
</code></pre>
<p>如果request.POST 中的数据与initial 中的不同，has_changed() 将为True，否则为False。 计算的结果是通过调用表单每个字段的Field.has_changed() 得到的。</p>
<p><code>Form.fields</code><br>
从表单中访问字段<br>
是一个<code>OrderedDict</code><br>
可你可以修改表单实例的字段来改变字段在表单中的表示：</p>
<pre><code class="language-python">&gt;&gt;&gt; f.as_table().split('\n')[0]
'&lt;tr&gt;&lt;th&gt;Name:&lt;/th&gt;&lt;td&gt;&lt;input name=&quot;name&quot; type=&quot;text&quot; value=&quot;instance&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;'
&gt;&gt;&gt; f.fields['name'].label = &quot;Username&quot;
&gt;&gt;&gt; f.as_table().split('\n')[0]
'&lt;tr&gt;&lt;th&gt;Username:&lt;/th&gt;&lt;td&gt;&lt;input name=&quot;name&quot; type=&quot;text&quot; value=&quot;instance&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;'
</code></pre>
<p>注意不要改变base_fields 属性，因为一旦修改将影响同一个Python 进程中接下来所有的ContactForm 实例：</p>
<pre><code class="language-python">&gt;&gt;&gt; f.base_fields['name'].label = &quot;Username&quot;
&gt;&gt;&gt; another_f = CommentForm(auto_id=False)
&gt;&gt;&gt; another_f.as_table().split('\n')[0]
'&lt;tr&gt;&lt;th&gt;Username:&lt;/th&gt;&lt;td&gt;&lt;input name=&quot;name&quot; type=&quot;text&quot; value=&quot;class&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;'
</code></pre>
<blockquote>
<p>cleaned_data 始终只 包含表单中定义的字段，即使你在构建表单 时传递了额外的数据。<br>
cleaned_data 始终只 包含表单中定义的字段，即使你在构建表单 时传递了额外的数据。<br>
当表单合法时，cleaned_data 将包含所有字段的键和值，即使传递的数据不包含某些可选字段的值。</p>
</blockquote>
<h3 id="formcleaned_data"><code>Form.cleaned_data</code></h3>
<h3 id="formas_p"><code>Form.as_p</code></h3>
<p><code>Form.as_ul</code><br>
<code>Form.as_table</code></p>
<h3 id="formerror_css_class-formrequired_css_class"><code>Form.error_css_class</code> <code>Form.required_css_class</code></h3>
<p>在<code>Form</code>类下可以用上面两个属性定义错误样式和必填样式，没有默认值，<code>required_css_class</code>也会回在<code>label</code>标签上</p>
<h2 id="formauto_id"><code>Form.auto_id</code></h2>
<p>控制表单上的<code>label</code>和表单元素的id，值为<code>True</code>，<code>False</code>或字符串，支持<code>%s</code>占位符，表示当前字段名</p>
<blockquote>
<p>如果auto_id 设置为任何其它的真值 —— 例如不包含%s 的字符串 —— 那么其行为将类似auto_id 等于True。<br>
默认情况下，auto_id 设置为'id_%s'。</p>
</blockquote>
<h3 id="formlabel_suffix"><code>Form.label_suffix</code></h3>
<p>默认为英文的<code>:</code></p>
<h3 id="boundfield"><code>BoundField</code></h3>
<pre><code class="language-python">form = ContactForm()
for boundfield in form:
    print(boundfield)
# 或
from['name']
</code></pre>
<p><code>BoundField.errors</code><br>
<code>BoundField.label_tag(contents=None, attrs=None, label_suffix=None)</code><br>
<code>BoundField.css_classes()</code><br>
<code>BoundField.value()</code><br>
提供初始值，会被绑定值覆盖<br>
<code>BoundField.id_for_label</code></p>
<h3 id="formis_multipart"><code>Form.is_multipart()</code></h3>
<p>可判断表单是否需要<code>multipart</code></p>
<pre><code class="language-django">{% if form.is_multipart %}
    &lt;form enctype=&quot;multipart/form-data&quot; method=&quot;post&quot; action=&quot;/foo/&quot;&gt;
{% else %}
    &lt;form method=&quot;post&quot; action=&quot;/foo/&quot;&gt;
{% endif %}
{{ form }}
&lt;/form&gt;
</code></pre>
<p>子类化表单时可通过设置<code>None</code>来删除从父类中继承过来的字段</p>
<pre><code class="language-python">&gt;&gt;&gt; from django import forms
 
&gt;&gt;&gt; class ParentForm(forms.Form):
...     name = forms.CharField()
...     age = forms.IntegerField()
 
&gt;&gt;&gt; class ChildForm(ParentForm):
...     name = None
 
&gt;&gt;&gt; ChildForm().fields.keys()
... ['age']
</code></pre>
<h3 id="formprefix"><code>Form.prefix</code></h3>
<p>如果在页面中需要放多个相同的表单，可以设置表单的前缀</p>
<pre><code class="language-python">&gt;&gt;&gt; father = PersonForm()
&gt;&gt;&gt; print(father.as_ul())
&lt;li&gt;&lt;label for=&quot;id_first_name&quot;&gt;First name:&lt;/label&gt; &lt;input type=&quot;text&quot; name=&quot;first_name&quot; id=&quot;id_first_name&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;label for=&quot;id_last_name&quot;&gt;Last name:&lt;/label&gt; &lt;input type=&quot;text&quot; name=&quot;last_name&quot; id=&quot;id_last_name&quot; /&gt;&lt;/li&gt;
&gt;&gt;&gt; mother = PersonForm(prefix=&quot;mother&quot;)
&gt;&gt;&gt; print(mother.as_ul())
&lt;li&gt;&lt;label for=&quot;id_mother-first_name&quot;&gt;First name:&lt;/label&gt; &lt;input type=&quot;text&quot; name=&quot;mother-first_name&quot; id=&quot;id_mother-first_name&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;label for=&quot;id_mother-last_name&quot;&gt;Last name:&lt;/label&gt; &lt;input type=&quot;text&quot; name=&quot;mother-last_name&quot; id=&quot;id_mother-last_name&quot; /&gt;&lt;/li&gt;
</code></pre>
<h2 id="表单字段"><a href="http://python.usyiyi.cn/django/ref/forms/fields.html">表单字段</a></h2>
<h3 id="fieldhas_change"><code>Field.has_change()</code></h3>
<p>检查字段的值是否从初始值发生改变</p>
<h3 id="内建字段">内建字段</h3>
<h4 id="booleanfield"><code>BooleanField</code></h4>
<p>Widget：<code>CheckboxInput</code><br>
错误信息的键：<code>required</code></p>
<h4 id="charfield"><code>CharField</code></h4>
<p>Widget：<code>TextInput</code><br>
错误信息的键：<code>required``max_length``min_length</code><br>
接收两个可选参数<br>
<code>max_length``min_length</code></p>
<h4 id="choicefield"><code>ChoiceField</code></h4>
<p>Widtget：<code>Select</code><br>
错误信息的键：<code>required``invalid_choice</code><br>
<code>invalid_choice</code>错误消息可能包含<code>%(value)s</code>，它将被选择的选项替换掉。<br>
接收一个额外的必选参数<br>
<code>choices</code><br>
是一个二元组组成的可迭代对象</p>
<h4 id="typechoicefield"><code>TypeChoiceField</code></h4>
<p>Widget：<code>Select</code><br>
错误信息的键：<code>required``invalid_choice</code><br>
接收额外的参数<br>
<code>choices</code><br>
是一个二元组组成的可迭代对象<br>
<code>coerce</code><br>
接收一个参数并返回强制转换后的值的一个函数。例如内建的int、float、bool 和其它类型。默认为id 函数。注意强制转换在输入验证结束后发生，所以它可能强制转换不在 choices 中的值<br>
<code>empty_value</code><br>
用于表示“空”的值。默认为空字符串；None 是另外一个常见的选项。注意这个值不会被coerce 参数中指定的函数强制转换，所以请根据情况进行选择</p>
<h4 id="datefield"><code>DateField</code></h4>
<p>Widget：<code>DateInput</code><br>
错误信息的键：<code>required``invalid</code><br>
接收一个可选参数<br>
<code>input_formats</code><br>
一个格式的列表，用于转换一个字符串为<code>datateim.date</code>对象<br>
默认为</p>
<pre><code>['%Y-%m-%d',      # '2006-10-25'
'%m/%d/%Y',       # '10/25/2006'
'%m/%d/%y']       # '10/25/06'
</code></pre>
<p>另外，如果你在设置中指定USE_L10N=False，以下的格式也将包含在默认的输入格式中：</p>
<pre><code>['%b %d %Y',      # 'Oct 25 2006'
'%b %d, %Y',      # 'Oct 25, 2006'
'%d %b %Y',       # '25 Oct 2006'
'%d %b, %Y',      # '25 Oct, 2006'
'%B %d %Y',       # 'October 25 2006'
'%B %d, %Y',      # 'October 25, 2006'
'%d %B %Y',       # '25 October 2006'
'%d %B, %Y']      # '25 October, 2006'
</code></pre>
<h4 id="datetimefield"><code>DateTimeField</code></h4>
<p>Widget：<code>DateTimeInput</code><br>
错误信息的键：<code>required``invalid</code><br>
接收一个可选参数<br>
<code>input_formats</code><br>
一个格式的列表，用于转换一个字符串为<code>datetime.datetime</code>对象<br>
默认为</p>
<pre><code>['%Y-%m-%d %H:%M:%S',    # '2006-10-25 14:30:59'
'%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
'%Y-%m-%d',              # '2006-10-25'
'%m/%d/%Y %H:%M:%S',     # '10/25/2006 14:30:59'
'%m/%d/%Y %H:%M',        # '10/25/2006 14:30'
'%m/%d/%Y',              # '10/25/2006'
'%m/%d/%y %H:%M:%S',     # '10/25/06 14:30:59'
'%m/%d/%y %H:%M',        # '10/25/06 14:30'
'%m/%d/%y']              # '10/25/06'
</code></pre>
<h4 id="decimalfield"><code>DecimalField</code></h4>
<p>Widget：当<code>Field.localize</code>是<code>False</code>时为NumberInput，否则为<code>TextInput</code><br>
错误信息的键：<code>required``invalid``max_value``min_digits``max_decimal_places``max_whole_digits</code><br>
<code>max_value</code>和<code>min_value</code>错误信息可能包含<code>%(limit_value)s</code>，它们将被真正的限制值替换。类似地，<code>max_digits</code>、<code>max_decimal_places</code>和 <code>max_whole_digits</code>错误消息可能包含<code>%(max)s</code><br>
接收四个可选参数<br>
<code>max_value</code><br>
<code>min_value</code><br>
<code>max_digits</code>最大位数<br>
<code>decimal_places</code>最大小数位</p>
<h4 id="durationfield"><code>DurationField</code></h4>
<p>Widget：<code>TextInput</code><br>
错误信息的键：<code>required``invalid</code></p>
<h4 id="emailfield"><code>EmailField</code></h4>
<p>Widget：<code>EmailInput</code><br>
错误信息的键：<code>required``invalid</code><br>
接收两个可选参数<br>
<code>max_length``min_length</code></p>
<h4 id="filefield"><code>FileField</code></h4>
<p>Widget：<code>ClearableFileInput</code><br>
错误信息的键：<code>required``invalid``missing``empty``max_length</code><br>
接收两个可选参数<br>
<code>max_length``allow_empty_file</code>如果提供，这两个参数确保文件名的最大长度，而且即使文件内容为空时验证也会成功<br>
<code>max_length</code>错误信息表示文件名的长度。在错误信息中，<code>%(max)d</code>将替换为文件的最大长度，%<code>(length)d</code> 将替换为当前文件名的长度</p>
<h4 id="filepathfield"><code>FilePathField</code></h4>
<p>Widget：<code>Select</code><br>
错误信息的键：<code>required``invalid_choice</code><br>
这个字段允许从一个特定的目录选择文件<br>
接收五个参数<br>
<code>path</code><br>
必须<br>
想要列出的目录的绝对路径<br>
<code>recursive</code><br>
可选<br>
布尔值，默认为<code>False</code>，是否需要递归这个目录<br>
<code>match</code><br>
可选<br>
正则表达式表示一个模式，只有匹配这个表达式的名称才会允许作为选项<br>
<code>allow_files</code><br>
可选<br>
布尔值，默认为<code>True</code>，表示是否应该包含指定位置的文件，它和<code>allow_folders</code>必须有一个为<code>True</code><br>
<code>allow_folders</code><br>
可选<br>
布尔值，默认为<code>True</code>，表示是否应该包含指定位置的目录，和<code>allow_files</code>必须有一个为<code>True</code></p>
<h4 id="floatfield"><code>FloatField</code></h4>
<p>Widget：当<code>Field.localize</code>是False 时为<code>NumberInput</code>，否则为<code>TextInput</code><br>
错误信息的键：<code>required``invalid``max_value``min_value</code><br>
接收两个可选参数<br>
<code>max_value``min_value</code></p>
<h4 id="imagefield"><code>ImageField</code></h4>
<p>Widget：<code>ClearableFileInput</code><br>
错误信息的键：<code>required``invalid``missing``empty``invalid_image</code></p>
<h4 id="integerfield"><code>IntegerField</code></h4>
<p>Widget：当<code>Field.localize</code>是<code>False</code>时为<code>NumberInput</code>，否则为<code>TextInput</code><br>
错误信息的键：<code>required``invalid``max_value``min_value</code><br>
接收两个可选参数<br>
<code>max_value``min_value</code></p>
<h4 id="ipaddressfield"><code>IPAddressField</code></h4>
<p>1.7弃用</p>
<h4 id="genericipaddressfield"><code>GenericIPAddressField</code></h4>
<p>Widget：<code>TextInput</code><br>
错误信息的键：<code>required``invalid</code><br>
接收两个可选参数<br>
<code>protocol``unpack_ipv4</code></p>
<h4 id="multiplechoicefield"><code>MultipleChoiceField</code></h4>
<p>Widget：<code>SelectMultiple</code><br>
错误信息的键：<code>required``invalid_choice``invalid_list</code></p>
<h4 id="typedmultiplechoicefield"><code>TypedMultipleChoiceField</code></h4>
<h4 id="nullbooleanfield"><code>NullBooleanField</code></h4>
<h4 id="regexfield"><code>RegexField</code></h4>
<h4 id="slugfield"><code>SlugField</code></h4>
<h4 id="timefield"><code>TimeField</code></h4>
<h4 id="urlfield"><code>URLField</code></h4>
<h4 id="uuidfield"><code>UUIDField</code></h4>
<p>输出时需要<code>.hex</code></p>
<h4 id="combofield"><code>ComboField</code></h4>
<pre><code class="language-python">&gt;&gt;&gt; from django.forms import ComboField
&gt;&gt;&gt; f = ComboField(fields=[CharField(max_length=20), EmailField()])
&gt;&gt;&gt; f.clean('test@example.com')
'test@example.com'
&gt;&gt;&gt; f.clean('longemailaddress@example.com')
Traceback (most recent call last):
...
ValidationError: ['Ensure this value has at most 20 characters (it has 28).']
</code></pre>
<h4 id="multivaluefield"><code>MultiValueField</code></h4>
<h4 id="splitdatetimefield"><code>SplitDateTimeField</code></h4>
<h4 id="modelchoicefield"><code>ModelChoiceField</code></h4>
<pre><code class="language-python"># A custom empty label
field1 = forms.ModelChoiceField(queryset=..., empty_label=&quot;(Nothing)&quot;)
 
# No empty label
field2 = forms.ModelChoiceField(queryset=..., empty_label=None)
</code></pre>
<h4 id="modelmultiplechoicefield"><code>ModelMultipleChoiceField</code></h4>
<h2 id="widgets"><a href="http://python.usyiyi.cn/django/ref/forms/widgets.html">Widgets</a></h2>
<p>处理文本输入的Widget</p>
<ul>
<li><code>TextInput</code></li>
<li><code>NumberInput</code></li>
<li><code>EmailInput</code></li>
<li><code>URLInput</code></li>
<li><code>PasswordInput</code></li>
<li><code>HiddenInput</code></li>
<li><code>DateInput</code></li>
<li><code>DateTimeInput</code></li>
<li><code>TimeInput</code></li>
<li><code>Textarea</code></li>
</ul>
<p>选择和复选框Widget</p>
<ul>
<li><code>CheckboxInput</code></li>
<li><code>Select</code></li>
<li><code>NullBooleanSelect</code></li>
<li><code>SelectMultiple</code></li>
<li><code>RadioSelect</code></li>
<li><code>CheckboxSelectMultiple</code></li>
</ul>
<p>文件上传<code>Widget</code></p>
<ul>
<li><code>FileInput</code></li>
<li><code>ClearableFileInput</code></li>
</ul>
<p>复合Widget</p>
<ul>
<li><code>MultipleHiddenInput</code></li>
<li><code>SplitDateTimeWidget</code></li>
<li><code>SplitHiddenDateTimeWidget</code></li>
<li><code>SelectDateWidget</code></li>
</ul>
<h2 id="从模型创建表单"><a href="http://python.usyiyi.cn/django/topics/forms/modelforms.html">从模型创建表单</a></h2>
<p>下面两种方法效果相同</p>
<pre><code class="language-python">author = Author(title='Mr')
form = PartialAuthorForm(request.POST, instance=author)
form.save()
# or
form = PartialAuthorForm(request.POST)
author = form.save(commit=False)
author.title = 'Mr'
author.save()
</code></pre>
<p>显式定义的字段不会从对于的模型中获取属性，例如 max_length 或required。 如果你希望保持模型中指定的行为，你必须设置在声明表单字段时显式设置相关的参数。</p>
<p>例如，如果Article 模型像下面这样：</p>
<pre><code class="language-python">class Article(models.Model):
    headline = models.CharField(max_length=200, null=True, blank=True,
                                help_text=&quot;Use puns liberally&quot;)
    content = models.TextField()
</code></pre>
<p>而你想为headline 做一些自定义的验证，在保持blank 和help_text 值的同时，你必须定义这样定义ArticleForm：</p>
<pre><code class="language-python">class ArticleForm(ModelForm):
    headline = MyFormField(max_length=200, required=False,
                           help_text=&quot;Use puns liberally&quot;)
 
    class Meta:
        model = Article
        fields = ['headline', 'content']
</code></pre>
<p>创建简单的表单或表单集可以使用<code>modelform_factory()``modelformset_factory()</code>方法来新建。</p>
<p>启用字段的本地化功能¶</p>
<p>默认情况下，ModelForm 中的字段不会本地化它们的数据。你可以使用Meta 类的localized_fields 属性来启用字段的本地化功能。</p>
<pre><code class="language-python">&gt;&gt;&gt; from django.forms import ModelForm
&gt;&gt;&gt; from myapp.models import Author
&gt;&gt;&gt; class AuthorForm(ModelForm):
...     class Meta:
...         model = Author
...         localized_fields = ('birth_date',)
</code></pre>
<p>如果localized_fields 设置为<code>'__all__' </code>这个特殊的值，所有的字段都将本地化。</p>
<p>提供的初始值会覆盖从实例取得的值</p>
<pre><code class="language-python">&gt;&gt;&gt; article = Article.objects.get(pk=1)
&gt;&gt;&gt; article.headline
'My headline'
&gt;&gt;&gt; form = ArticleForm(initial={'headline': 'Initial headline'}, instance=article)
&gt;&gt;&gt; form['headline'].value()
'Initial headline'
</code></pre>
<p>如果不需要很多自定义，可以直接使用工厂方法来生成表单类</p>
<pre><code class="language-python">&gt;&gt;&gt; from django.forms.models import modelform_factory
&gt;&gt;&gt; from myapp.models import Book
&gt;&gt;&gt; BookForm = modelform_factory(Book, fields=(&quot;author&quot;, &quot;title&quot;))
&gt;
</code></pre>
<pre><code class="language-python">&gt;&gt;&gt; from django.forms import Textarea
&gt;&gt;&gt; Form = modelform_factory(Book, form=BookForm,
...                          widgets={&quot;title&quot;: Textarea()})
</code></pre>
<pre><code class="language-python">&gt;&gt;&gt; Form = modelform_factory(Author, form=AuthorForm, localized_fields=(&quot;birth_date&quot;,))
</code></pre>
<p>表单集</p>
<pre><code class="language-python">&gt;&gt;&gt; from django.forms.models import modelformset_factory
&gt;&gt;&gt; from myapp.models import Author
&gt;&gt;&gt; AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
</code></pre>
<p>使用<code>model</code>生成的<code>formset </code>默认带一个包含全部对象的<code>queryset</code><br>
<code>formset``save()</code>之后，会有新的属性</p>
<pre><code class="language-python">models.BaseModelFormSet.changed_objects
models.BaseModelFormSet.deleted_objects
models.BaseModelFormSet.new_objects
</code></pre>
<p><code>max_num</code>为最大的表单数，如果初始<code>queryset</code>长度比<code>max_num</code>，则按照<code>queryset</code>来，<code>extra</code>是可以额外添加的空表单的个数，但<code>extra</code>和<code>queryset</code>长度相加如果大于<code>max_num</code>，则<code>extra</code>和实例设置可能表现不一样，如<code>queryset</code>长度为2，<code>max_num</code>为4，<code>extra</code>不管是2还是5，最终表现出来都是2。</p>
<pre><code class="language-python">AuthorFormSet = modelformset_factory(Author, fields=('name',), max_num=4, extra=2)
</code></pre>
<p><code>max_num</code>默认只影响显示，不影响验证，如果需要影响验证添加<code>validate_max=True</code>即可</p>
<h2 id="表单素材-media-类"><a href="http://python.usyiyi.cn/django/topics/forms/media.html">表单素材 ( Media 类)</a></h2>
<p><strong><code>Form</code>和<code>Widget</code>都可以定义素材</strong></p>
<pre><code class="language-python">from django import forms
 
class CalendarWidget(forms.TextInput):
    class Media:
        css = {
            'all': ('pretty.css',)
        }
        js = ('animations.js', 'actions.js')
</code></pre>
<p>使用<code>CalendarWidget</code>会自动引入下列资源</p>
<pre><code class="language-html">&lt;link href=&quot;http://static.example.com/pretty.css&quot; type=&quot;text/css&quot; media=&quot;all&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/animations.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/actions.js&quot;&gt;&lt;/script&gt;
</code></pre>
<p><code>Widget</code>会默认继承父类的素材，如果不想继承在<code>Media</code>里使用<code>extend</code>禁止。</p>
<p>动态定义</p>
<pre><code class="language-python">class CalendarWidget(forms.TextInput):
    def _media(self):
        return forms.Media(css={'all': ('pretty.css',)},
                           js=('animations.js', 'actions.js'))
    media = property(_media)
</code></pre>
<p>两个<code>Media</code>实例可以相加</p>
<pre><code class="language-python">&gt;&gt;&gt; from django import forms
&gt;&gt;&gt; class CalendarWidget(forms.TextInput):
...     class Media:
...         css = {
...             'all': ('pretty.css',)
...         }
...         js = ('animations.js', 'actions.js')
 
&gt;&gt;&gt; class OtherWidget(forms.TextInput):
...     class Media:
...         js = ('whizbang.js',)
 
&gt;&gt;&gt; w1 = CalendarWidget()
&gt;&gt;&gt; w2 = OtherWidget()
&gt;&gt;&gt; print(w1.media + w2.media)
&lt;link href=&quot;http://static.example.com/pretty.css&quot; type=&quot;text/css&quot; media=&quot;all&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/animations.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/actions.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/whizbang.js&quot;&gt;&lt;/script&gt;
</code></pre>
<p>表单<code>Media</code></p>
<pre><code class="language-python">&gt;&gt;&gt; class ContactForm(forms.Form):
...     date = DateField(widget=CalendarWidget)
...     name = CharField(max_length=40, widget=OtherWidget)
...
...     class Media:
...         css = {
...             'all': ('layout.css',)
...         }
 
&gt;&gt;&gt; f = ContactForm()
&gt;&gt;&gt; f.media
&lt;link href=&quot;http://static.example.com/pretty.css&quot; type=&quot;text/css&quot; media=&quot;all&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;link href=&quot;http://static.example.com/layout.css&quot; type=&quot;text/css&quot; media=&quot;all&quot; rel=&quot;stylesheet&quot; /&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/animations.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/actions.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;http://static.example.com/whizbang.js&quot;&gt;&lt;/script
</code></pre>
<h2 id="表单集"><a href="http://python.usyiyi.cn/django/topics/forms/formsets.html">表单集</a></h2>
<p>表单集控制<br>
<code>max_num</code><br>
<code>min_num</code><br>
<code>validate_max</code><br>
<code>validate_min</code><br>
<code>can_order</code><br>
<code>can_delete</code></p>
<p>其中<code>can_order``can_delete</code>默认以以下形式展现</p>
<p>如果是使用<code>Model</code>生成的表单集，如果<code>delete</code>后，在调用<code>formset.save()</code>会自动删除相应的数据，但如果调用了<code>formset.save(commit=False)</code>，则需要手动删除（1.6或更早版还是会自动删除）</p>
<pre><code class="language-python">&gt;&gt;&gt; instances = formset.save(commit=False)
&gt;&gt;&gt; for obj in formset.deleted_objects:
...     obj.delete()
</code></pre>
<p>如果要兼容1.6或更早版，可以这么写</p>
<pre><code class="language-python">&gt;&gt;&gt; try:
&gt;&gt;&gt;     # For Django 1.7+
&gt;&gt;&gt;     for obj in formset.deleted_objects:
&gt;&gt;&gt;         obj.delete()
&gt;&gt;&gt; except AssertionError:
&gt;&gt;&gt;     # Django 1.6 and earlier already deletes the objects, trying to
&gt;&gt;&gt;     # delete them a second time raises an AssertionError.
&gt;&gt;&gt;     pass
</code></pre>
<p>在表单集里添加额外的字段</p>
<pre><code class="language-python">&gt;&gt;&gt; from django.forms.formsets import BaseFormSet
&gt;&gt;&gt; from django.forms.formsets import formset_factory
&gt;&gt;&gt; from myapp.forms import ArticleForm
&gt;&gt;&gt; class BaseArticleFormSet(BaseFormSet):
...     def add_fields(self, form, index):
...         super(BaseArticleFormSet, self).add_fields(form, index)
...         form.fields[&quot;my_field&quot;] = forms.CharField()
 
&gt;&gt;&gt; ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
&gt;&gt;&gt; formset = ArticleFormSet()
&gt;&gt;&gt; for form in formset:
...     print(form.as_table())
&lt;tr&gt;&lt;th&gt;&lt;label for=&quot;id_form-0-title&quot;&gt;Title:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;form-0-title&quot; id=&quot;id_form-0-title&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;&lt;label for=&quot;id_form-0-pub_date&quot;&gt;Pub date:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;form-0-pub_date&quot; id=&quot;id_form-0-pub_date&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;th&gt;&lt;label for=&quot;id_form-0-my_field&quot;&gt;My field:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input type=&quot;text&quot; name=&quot;form-0-my_field&quot; id=&quot;id_form-0-my_field&quot; /&gt;&lt;/td&gt;&lt;/tr&gt;
</code></pre>
<p>表单集对应的模版写法</p>
<pre><code class="language-django">&lt;form method=&quot;post&quot; action=&quot;&quot;&gt;
    &lt;table&gt;
        {{ formset }}
    &lt;/table&gt;
&lt;/form&gt;
</code></pre>
<p>也可以手动渲染，不能缺少<code>{{ formset.management_form }}</code></p>
<pre><code class="language-django">&lt;form method=&quot;post&quot; action=&quot;&quot;&gt;
    {{ formset.management_form }}
    &lt;table&gt;
        {% for form in formset
        {{ form }}
        {% endfor
    &lt;/table&gt;
&lt;/form&gt;
</code></pre>
<p>如果是手动渲染，<code>can_order``can_delete</code>需要手动添加</p>
<pre><code class="language-django">&lt;form method=&quot;post&quot; action=&quot;&quot;&gt;
    {{ formset.management_form }}
    {% for form in formset
        &lt;ul&gt;
            &lt;li&gt;{{ form.title }}&lt;/li&gt;
            &lt;li&gt;{{ form.pub_date }}&lt;/li&gt;
            {% if formset.can_delete
                &lt;li&gt;{{ form.DELETE }}&lt;/li&gt;
            {% endif
        &lt;/ul&gt;
    {% endfor
&lt;/form&gt;
</code></pre>
<p>在一个视图中使用多个<code>FormSet</code>需要回前缀<code>prefix</code></p>
<pre><code class="language-python">from django.forms.formsets import formset_factory
from django.shortcuts import render_to_response
from myapp.forms import ArticleForm, BookForm
 
def manage_articles(request):
    ArticleFormSet = formset_factory(ArticleForm)
    BookFormSet = formset_factory(BookForm)
    if request.method == 'POST':
        article_formset = ArticleFormSet(request.POST, request.FILES, prefix='articles')
        book_formset = BookFormSet(request.POST, request.FILES, prefix='books')
        if article_formset.is_valid() and book_formset.is_valid():
            # do something with the cleaned_data on the formsets.
            pass
    else:
        article_formset = ArticleFormSet(prefix='articles')
        book_formset = BookFormSet(prefix='books')
    return render_to_response('manage_articles.html', {
        'article_formset': article_formset,
        'book_formset': book_formset,
    })
</code></pre>
<h2 id="表单验证和字段验证"><a href="http://python.usyiyi.cn/django/ref/forms/validation.html">表单验证和字段验证</a></h2>
<p>格式标准<br>
抛出单个错误</p>
<pre><code class="language-python">raise ValidationError(
    _('Invalid value: %(value)s'),
    code='invalid',
    params={'value': '42'},
)
</code></pre>
<p>招聘多个错误</p>
<pre><code class="language-python"># Good
raise ValidationError([
    ValidationError(_('Error 1'), code='error1'),
    ValidationError(_('Error 2'), code='error2'),
])
 
# Bad
raise ValidationError([
    _('Error 1'),
    _('Error 2'),
])
</code></pre>
<p>创建一个新的表单字段添加默认验证</p>
<pre><code class="language-python">from django import forms
from django.core.validators import validate_email
 
class MultiEmailField(forms.Field):
    def to_python(self, value):
        &quot;Normalize data to a list of strings.&quot;
 
        # Return an empty list if no input was given.
        if not value:
            return []
        return value.split(',')
 
    def validate(self, value):
        &quot;Check if value consists only of valid emails.&quot;
 
        # Use the parent's handling of required fields, etc.
        super(MultiEmailField, self).validate(value)
 
        for email in value:
            validate_email(email)
</code></pre>
<h2 id="django-的设置"><a href="http://python.usyiyi.cn/django/topics/settings.html">Django 的设置</a></h2>
<p>django-admin 工具</p>
<p>当使用django-admin 时， 你可以设置只设置环境变量一次，或者每次运行该工具时显式传递设置模块。</p>
<p>例如（Unix Bash shell）：</p>
<pre><code>export DJANGO_SETTINGS_MODULE=mysite.settings
django-admin runserver
</code></pre>
<p>例如（Windows shell）：</p>
<pre><code>set DJANGO_SETTINGS_MODULE=mysite.settings
django-admin runserver
</code></pre>
<p>使用--settings 命令行参数可以手工指定设置：</p>
<pre><code>django-admin runserver --settings=mysite.settings
</code></pre>
<p>使用下面的命令可以查询设置与默认设置的不同</p>
<pre><code class="language-python">python manage.py diffsettings
</code></pre>
<p>在django app中使用设置应使用以下导入方式</p>
<pre><code class="language-python">from django.conf import settings
</code></pre>
<p>注意，django.conf.settings 不是一个模块 —— 它是一个对象。所以不可以导入每个单独的设置：</p>
<pre><code class="language-python">from django.conf.settings import DEBUG  # This won't work.
</code></pre>
<p>不要在应用运行时改变设置</p>
<h2 id="完整列表设置settings"><a href="http://python.usyiyi.cn/django/ref/settings.html">完整列表设置(Settings)</a></h2>
<p><code>CSRF_COOKIE_SECURE=True</code>只通过<code>HTTPS</code>传递<code>cookie</code></p>
<p><code>DATABASES['CONN_MAX_AGE']</code>数据库连接的戚时间，默认为0（历史遗留行为），设置为<code>None</code>表示无限的持久连接<br>
<code>DECIMAL_SEPARATOR</code>类型数据的分隔符默认为点<code>.</code><br>
<code>DISALLOWED_USER_AGENTS</code>编写正则表达式元组禁用代码访问，需要启用<code>CommonMiddleware</code>中间件<br>
<code>INTERNAL_IPS</code>设置公司内容的ip，在些ip列表中的ip可以访问admindoc下的书签</p>
<h2 id="应用"><a href="http://python.usyiyi.cn/django_182/ref/applications.html">应用</a></h2>
<pre><code class="language-python"># rock_n_roll/apps.py
 
from django.apps import AppConfig
 
class RockNRollConfig(AppConfig):
    name = 'rock_n_roll'
    verbose_name = &quot;Rock ’n’ roll&quot;
</code></pre>
<pre><code class="language-python"># rock_n_roll/__init__.py
 
default_app_config = 'rock_n_roll.apps.RockNRollConfig'
</code></pre>
<p><code>AppConfig</code>可配置的属性</p>
<ul>
<li><code>AppConfig.name</code></li>
<li><code>AppConfig.label</code></li>
<li><code>AppConfig.verbose_name</code></li>
<li><code>Appconfig.path</code></li>
</ul>
<p><code>AppConfig</code>只读属性</p>
<ul>
<li><code>AppConfig.module</code></li>
<li><code>Appconfig.models_module</code></li>
</ul>
<p><code>AppConfig</code>方法</p>
<ul>
<li><code>AppConfig.get_models()</code></li>
<li><code>AppConfig..get_model(model_name)</code></li>
<li><code>AppConfigevaluate.ready()</code></li>
</ul>
<pre><code class="language-python">&gt;&gt;&gt; from django.apps import apps
&gt;&gt;&gt; apps.get_app_config('admin').verbose_name
'Admin'
</code></pre>
<p><code>APP</code></p>
<ul>
<li><code>apps.ready</code></li>
<li><code>apps.get_app_configs()</code></li>
<li><code>apps.get_app_config(app_label)</code></li>
<li><code>apps.is_installed(app_name)</code></li>
<li><code>apps.get_model(app_label, model_name)</code></li>
</ul>
<h2 id="django异常"><a href="python.usyiyi.cn/django_182/ref/exceptions.html">Django异常</a></h2>
<h3 id="核心异常">核心异常</h3>
<p><em><code>django.core.exceptions</code></em></p>
<ul>
<li><code>ObjectDoesNotExist</code><br>
对象不存在<br>
<code>DoesNotExist</code>的基类<br>
对ObjectDoesNotExist的try/except会为所有模型捕获到所有DoesNotExist 异常</li>
</ul>
<pre><code class="language-python">from django.core.exceptions import ObjectDoesNotExist
try:
    e = Entry.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print(&quot;Either the entry or blog doesn't exist.&quot;)
</code></pre>
<ul>
<li>
<p><code>FieldDoesNotExist</code><br>
当被请求的字段在模型或模型的父类中不存在时，<code>FieldDoesNotExist</code>异常由模型的 <code>_meta.get_field()</code>方法抛出</p>
</li>
<li>
<p><code>MultipleObjectsReturned</code><br>
查询时，预期只有一个对象，但是返回了多个对象会抛出此异常</p>
</li>
<li>
<p><code>SuspiciousOperation</code><br>
当用户进行的操作在安全方面可疑的时候，抛出此异常，例如，篡改<code>cookie</code><br>
子类</p>
<ul>
<li><code>DisallowedHost</code></li>
<li><code>DisallowedModelAdminLookup</code></li>
<li><code>DisallowedModelAdminToField</code></li>
<li><code>DisallowedRedirect</code></li>
<li><code>InvalidSessionKey</code></li>
<li><code>SuspiciousFileOperation</code></li>
<li><code>SuspiciousMultipartForm</code></li>
<li><code>SuspiciousSession</code></li>
</ul>
</li>
<li>
<p><code>PermissionDenied</code><br>
当用户不被允许来执行请求的操作时产生</p>
</li>
<li>
<p><code>ViewDoesNotExist</code><br>
当请求的视图不存在时抛出此异常</p>
</li>
<li>
<p><code>MiddlewareNotUsed</code><br>
当中间件没有在服务器配置中出现时，抛出此异常</p>
</li>
<li>
<p><code>ImproperlyConfigured</code><br>
django配置不当时抛出此异常，如<code>settings.py</code>中的值不正确或者不可解析</p>
</li>
<li>
<p><code>FieldError</code><br>
当模型上的字段出现问题时，抛出此异常，由以下原因造成：</p>
<ul>
<li>
<p>模型中的字段与抽象基类中的字段重名</p>
</li>
<li>
<p>排序造成了一个死循环</p>
</li>
<li>
<p>关键词不能由过滤器参数解析</p>
</li>
<li>
<p>字段不能由查询参数中的关键词决定</p>
</li>
<li>
<p>连接（join）不能在指定对象上使用</p>
</li>
<li>
<p>字段名称不可用</p>
</li>
<li>
<p>查询包含了无效的<code>order_by</code>参数</p>
</li>
<li>
<p><code>ValidationError</code><br>
当表单或模型字段验证失败时抛出此异常</p>
</li>
</ul>
</li>
<li>
<p><code>NON_FIELD_ERRORS</code><br>
在表单或者模型中不属于特定字段的<code>ValidationError</code>被归类为<code>NON_FIELD_ERRORS</code></p>
</li>
</ul>
<h3 id="url解析器异常">URL解析器异常</h3>
<ul>
<li>
<p><code>Resolver404</code><br>
<code>django.http.Http404</code>的子类<br>
当向<code>resolve</code>传递的路径不能匹配到对应视图时抛出此异常</p>
</li>
<li>
<p><code>NoReverseMatch</code><br>
当你的URLconf中的一个匹配的URL不能基于提供的参数识别时，抛出此异常</p>
</li>
</ul>
<h3 id="数据库异常">数据库异常</h3>
<p>数据库异常由django.db导入</p>
<ul>
<li><code>Error</code></li>
<li><code>InterfaceError</code></li>
<li><code>DatabaseError</code></li>
<li><code>DataError</code></li>
<li><code>OperationalError</code></li>
<li><code>IntegrityError</code></li>
<li><code>InternalError</code></li>
<li><code>ProgrammingError</code></li>
<li><code>NotSupportedError</code></li>
</ul>
<h3 id="http异常">HTTP异常</h3>
<p>HTTP异常由django.http导入</p>
<ul>
<li><code>UnreadablePostError</code><br>
用户取消上传时抛出此异常</li>
</ul>
<h3 id="事务异常">事务异常</h3>
<p>事务异常定义由<code>django.db.transaction</code>导入</p>
<h3 id="测试框架异常">测试框架异常</h3>
<p>由DJango django.test 包提供的异常</p>
<ul>
<li><code>RedirectCycleError</code><br>
当测试客户端检测到重定向的循环或者过长的链时抛出此异常</li>
</ul>
<h3 id="python异常"><code>Python</code>异常</h3>
<p>Django在适当的时候也会抛出Python的内建异常</p>
<h2 id="django-admin-and-managepy"><a href="http://python.usyiyi.cn/django_182/ref/django-admin.html">django-admin and manage.py</a></h2>
<ul>
<li>
<p><code>dumpdata</code><br>
该命令将所有与被命名应用相关联的数据库中的数据输出到标准输出。<br>
如果在dumpdate命令后面未指定Django应用名，则Django项目中安装的所有应用的数据都将被dump到fixture中<br>
<code>dumpdata --output data.json</code></p>
</li>
<li>
<p><code>flus</code><br>
清空数据库，重新装载初始数据</p>
</li>
<li>
<p><code>--noinput</code></p>
</li>
<li>
<p><code>--database</code></p>
</li>
<li>
<p><code>--no-initial-data</code></p>
</li>
<li>
<p><code>inspectdb</code><br>
根据数据库结构生成model</p>
</li>
</ul>
<pre><code class="language-python">python manage.py inspectdb &gt; models.py
</code></pre>
<ul>
<li>
<p><code>loaddata</code><br>
导入fixture数据</p>
</li>
<li>
<p><code>runserver</code><br>
启动本地上一个轻量级的Web服务器，默认多线程<br>
<code>--noreload</code>禁用自动重新载入<br>
<code>--nothreading</code>禁用多线程</p>
</li>
</ul>
<pre><code class="language-python">runserver 0.0.0.0:80
</code></pre>
<h2 id="添加自定义的命令"><a href="http://python.usyiyi.cn/django_182/howto/custom-management-commands.html"> 添加自定义的命令</a></h2>
<p>向应用下添加management/commands目录，Django会为此目录下的所有没有带下划线开头的python模块都注册一个<code>manage.py</code>命令。<br>
在Python 2上，请确保management和management/commands两个目录都包含<code>__init__.py</code> 文件。</p>
<pre><code class="language-python">from django.core.management.base import BaseCommand, CommandError
from polls.models import Poll
 
class Command(BaseCommand):
    help = 'Closes the specified poll for voting'
 
    def add_arguments(self, parser):
        # 命令行接收一个或多个poll_id值
        #
        parser.add_argument('poll_id', nargs='+', type=int)
 
    def handle(self, *args, **options):
        for poll_id in options['poll_id']:
            try:
                poll = Poll.objects.get(pk=poll_id)
            except Poll.DoesNotExist:
                raise CommandError('Poll &quot;%s&quot; does not exist' % poll_id)
 
            poll.opened = False
            poll.save()
 
            self.stdout.write('Successfully closed poll &quot;%s&quot;' % poll_id)
</code></pre>
<figure data-type="image" tabindex="1"><img src="https://me.iblogc.com/post-images/django-commands.png" alt="django-commands" loading="lazy"></figure>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[django-filter]]></title>
        <id>https://me.iblogc.com/post/django-filter/</id>
        <link href="https://me.iblogc.com/post/django-filter/">
        </link>
        <updated>2016-03-30T10:22:13.000Z</updated>
        <content type="html"><![CDATA[<h2 id="作用">作用</h2>
<p><code>django-filter</code>提供一种简单的方式为用户提供的参数（url传参）来过滤queryset</p>
<h2 id="写法">写法</h2>
<h3 id="model"><code>model </code></h3>
<pre><code>from django.db import models
 
class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.DecimalField()
    description = models.TextField()
    release_date = models.DateField()
    manufacturer = models.ForeignKey(Manufacturer)
</code></pre>
<h3 id="filter"><code>filter </code></h3>
<pre><code>import django_filters
 
class ProductFilter(django_filters.FilterSet):
    class Meta:
        model = Product
        fields = ['price', 'release_date']
</code></pre>
<p>url参数：<code>price </code> <code>release_date </code><br>
匹配模式：完全匹配</p>
<h4 id="重写过滤器">重写过滤器</h4>
<p>重写某个字段的过滤</p>
<pre><code>import django_filters
 
class ProductFilter(django_filters.FilterSet):
    price = django_filters.NumberFilter(lookup_type='lt')
    class Meta:
        model = Product
        fields = ['price', 'release_date']
</code></pre>
<p>url参数：<code>price </code> <code>release_date </code><br>
匹配模式：<code>price </code>小于输入值，<code>release_date</code>精确匹配<br>
附：<br>
<code>lt</code> 小于<br>
<code>lte</code> 小于等于<br>
<code>gt</code> 大于<br>
<code>gte</code> 大于等于</p>
<p>重写一个<code>CharField </code>类型字段的过滤</p>
<pre><code>class ProductFilter(django_filters.FilterSet):
    filter_overrides = {
        models.CharField: {
            'filter_class': django_filters.CharFilter,
            'extra': lambda f: {
                'lookup_type': 'icontains',
            }
        }
    }
 
    class Meta:
        model = Product
        fields = ['name']
</code></pre>
<p>url参数：<code>name</code><br>
匹配模式：完全匹配</p>
<h4 id="匹配模式">匹配模式</h4>
<pre><code>import django_filters
 
class ProductFilter(django_filters.FilterSet):
    class Meta:
        model = Product
        fields = {'price': ['lt', 'gt'],
                  'release_date': ['exact'],
                 }
</code></pre>
<p>url参数：<code>price__lt </code> <code>price__gt</code> <code>release_date</code><br>
<code>exact</code>是默认的</p>
<h4 id="关联表过滤">关联表过滤</h4>
<pre><code>class ProductFilter(django_filters.FilterSet):
    class Meta:
        model = Product
        fields = ['manufacturer__country']
</code></pre>
<h4 id="重写过滤">重写过滤</h4>
<h3 id="通用视图">通用视图</h3>
<p>类视图</p>
<pre><code># urls.py
from django.conf.urls import patterns, url
from django_filters.views import FilterView
from myapp.models import Product
 
urlpatterns = patterns('',
    (r'^list/$', FilterView.as_view(model=Product)),
)
</code></pre>
<p>需要提供一个模版名字为<code>&lt;app&gt;/&lt;model&gt;_filter.html</code>，在模版里可以获取名为<code>object_list</code>的<code>Product</code>QuerySet</p>
<p>方法视图<br>
虽然现在建议使用，但对针方法视图也有对应的方法</p>
<pre><code># urls.py
from django.conf.urls import patterns, url
from myapp.models import Product
 
urlpatterns = patterns('',
    (r'^list/$', 'django_filters.views.object_filter', {'model': Product}),
)
</code></pre>
<h3 id="参考">参考</h3>
<p>http://django-filter.readthedocs.org/en/latest/index.html</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django REST framework 之版本号 version]]></title>
        <id>https://me.iblogc.com/post/django-rest-framework之版本号version/</id>
        <link href="https://me.iblogc.com/post/django-rest-framework之版本号version/">
        </link>
        <updated>2016-01-28T15:29:58.000Z</updated>
        <summary type="html"><![CDATA[<br />]]></summary>
        <content type="html"><![CDATA[<br />
<!--more-->
drf支持以下形式传输版本号
- header
  ```
  GET /bookings/ HTTP/1.1
  Host: example.com
  Accept: application/json; version=1.0
  ```
- URL Path 
  ```
  GET /v1/bookings/ HTTP/1.1
  Host: example.com
  Accept: application/json
  ```
  ```
  urlpatterns = [
      url(
          r'^(?P<version>(v1|v2))/bookings/$',
          bookings_list,
          name='bookings-list'
      ),
  ]
  ```
- Namespace
  ```
  GET /v1/something/ HTTP/1.1
  Host: example.com
  Accept: application/json
  ```
  ```
  urlpatterns = [
      url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
      url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
  ]
  ```
- Host Name
  ```
  GET /bookings/ HTTP/1.1
  Host: v1.example.com
  Accept: application/json
  ```
- Query Parameter
  ```
  GET /something/?version=0.1 HTTP/1.1
  Host: example.com
  Accept: application/json
  ```
<p>drf默认是关闭版本控制功能，如需要开启，可在<code>settings.py</code>里添加对应的设置</p>
<pre><code class="language-python">REST_FRAMEWORK = {
    ……
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.AcceptHeaderVersioning',
    # 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    # 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
    # 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.HostNameVersioning',
    # 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',
    ……
}
</code></pre>
<p>当然，你也可以为每个视图单独添加，不过不建议这么做</p>
<pre><code class="language-python">class ProfileList(APIView):
    versioning_class = versioning.QueryParameterVersioning
</code></pre>
<p>开启版本控制之后，就可以从<code>request</code>取得版本号<code>request.version</code>（当然你<code>settings.py</code>里配置的是什么方式，就用什么方式传版本号，这样就才可以从<code>request</code>里获取到版本号）</p>
<pre><code class="language-python">def get_serializer_class(self):
    if self.request.version == 'v1':
        return AccountSerializerVersion1
    return AccountSerializer
</code></pre>
<p>启动版本控制后，url逆向解析方法需要传入<code>request</code>参数</p>
<pre><code class="language-python">from rest_framework.reverse import reverse
 
reverse('bookings-list', request=request)
</code></pre>
<p>如果是使用Namespace时的版本控制，因为配置了<code>DEFAULT_VERSIONING_CLASS</code>，所以设置view_name时不需要添加<code>v1:</code>前缀，见django rest framework入门笔记.md</p>
<p>最后在设置里添加以下全局设置来控制能访问的版本</p>
<pre><code class="language-python">'DEFAULT_VERSION': None, #默认版本，request里没有版本信息时，使用的版本，默认为None
'ALLOWED_VERSIONS': [None, 'v1', 'v2'], #允许访问的版本，如果访问的版本不在列表中，则会抛出异常
</code></pre>
<p>也可以为每个视图单独设置</p>
<pre><code class="language-python">from rest_framework.versioning import URLPathVersioning
from rest_framework.views import APIView
 
class ExampleVersioning(URLPathVersioning):
    default_version = ...
    allowed_versions = ...
    version_param = ...
 
class ExampleView(APIVIew):
    versioning_class = ExampleVersioning
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[curl 常用命令]]></title>
        <id>https://me.iblogc.com/post/curl常用命令/</id>
        <link href="https://me.iblogc.com/post/curl常用命令/">
        </link>
        <updated>2015-12-17T13:29:29.000Z</updated>
        <summary type="html"><![CDATA[<p>curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中，并且有DOS和Win32、Win64下的移植版本。</p>
]]></summary>
        <content type="html"><![CDATA[<p>curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中，并且有DOS和Win32、Win64下的移植版本。</p>
<!--more-->
<h3 id="访问url并输出结果">访问url并输出结果</h3>
<pre><code>curl http://www.example.com
</code></pre>
<h3 id="传递参数">传递参数</h3>
<p>默认curl使用GET方式请求数据，这种方式下直接通过URL传递数据<br>
可以通过 --data/-d 方式指定使用POST方式传递数据</p>
<pre><code># GET
curl -u username https://api.github.com/user?access_token=XXXXXXXXXX

# POST
curl -u username -d &quot;param1=value1&amp;param2=value&quot; https://api.github.com

# 也可以指定一个文件，将该文件中的内容当作数据传递给服务器端
curl -d @filename https://github.api.com/authorizations
</code></pre>
<p>注：默认情况下，通过POST方式传递过去的数据中若有特殊字符，首先需要将特殊字符转义在传递给服务器端，如value值中包含有空格，则需要先将空格转换成%20，如：</p>
<pre><code>curl -d &quot;value%201&quot; http://hostname.com
</code></pre>
<p>在新版本的CURL中，提供了新的选项 --data-urlencode，通过该选项提供的参数会自动转义特殊字符。</p>
<pre><code>curl --data-urlencode &quot;value 1&quot; http://hostname.com
</code></pre>
<h3 id="指定请求方式">指定请求方式</h3>
<p>除了使用GET和POST协议外，还可以通过 -X 选项指定其它协议，如：</p>
<pre><code>curl -I -X DELETE https://api.github.com
</code></pre>
<h3 id="设置请求头信息">设置请求头信息</h3>
<pre><code>curl -H 'Accept-Language: zh' http://cnn.com
</code></pre>
<p><code>-H</code>或被多次指定</p>
<pre><code>curl -H 'Host: 157.166.226.25'-H 'Accept-Language: zh'-H 'Cookie: ID=1234' http://cnn.com
</code></pre>
<p>对于&quot;User-Agent&quot;, &quot;Cookie&quot;, &quot;Host&quot;这类标准的HTTP头部字段，通常会有另外一种设置方法。curl命令提供了特定的选项来对这些头部字段进行设置：<br>
-A (or --user-agent): 设置 &quot;User-Agent&quot; 字段.<br>
-b (or --cookie): 设置 &quot;Cookie&quot; 字段.<br>
-e (or --referer): 设置 &quot;Referer&quot; 字段.</p>
<pre><code>curl -H &quot;User-Agent: my browser&quot; http://cnn.com
curl -A &quot;my browser&quot; http://cnn.com
</code></pre>
<h3 id="查看响应头信息">查看响应头信息</h3>
<pre><code>curl -I http://www.baidu.com
</code></pre>
<h3 id="提交表单">提交表单</h3>
<pre><code>curl --form &quot;fileupload=@filename.txt&quot; http://hostname/resource
</code></pre>
<h3 id="访问url并奖结果保存到本地文件中">访问url并奖结果保存到本地文件中</h3>
<p><code>-o</code>: 将文件保存为命令行中指定的文件名到本地<br>
<code>-O</code>: 使用url中默认的文件名保存文件到本地</p>
<pre><code>curl -o index.html http://www.example.com
# 或
curl  http://www.baidu.com &gt; index.html
# 在windows上没成功
curl -O http://www.example.com
</code></pre>
<h3 id="忽略证书错误">忽略证书错误</h3>
<p>工作中，经常需要用自签的假证书搭建开发环境。cURL在遇到证书错误时罢工，使用 -k 参数就可以让它不做证书校验。</p>
<pre><code>curl -k https://www.example.com
</code></pre>
<h3 id="获取重定向后的页面">获取重定向后的页面</h3>
<p>如果url重定向的话，curl默认是不会去获取重定向后的url页面的，使用<code>-L</code>可进行强制重定向</p>
<pre><code>curl -L http://www.example.com
</code></pre>
<h3 id="发送压缩的请求">发送压缩的请求</h3>
<p>cURL提供了一个 –compress 参数，可以用来发送支持压缩的请求。但使用了–compress之后，虽然传输过程是压缩的，cURL的输出还是解压之后的，难以看到效果。</p>
<p>自己写一个 Accept-Encoding 字段在头信息中。</p>
<pre><code>curl -H &quot;Accept-Encoding: gzip&quot; http://www.kuqin.com/
</code></pre>
<p>如果直接运行上面的命令，会得到一堆乱码，因为cURL输出的内容，是压缩后的数据。不妨在后面接一个gunzip试试。</p>
<pre><code># 使用gunzip解压
curl -H &quot;Accept-Encoding: gzip&quot; http://www.kuqin.com/ | gunzip
</code></pre>
<p>使用gunzip解压之后，信息又被还原了。</p>
<h3 id="断点续传">断点续传</h3>
<p>通过使用-C选项可对大文件使用断点续传功能</p>
<pre><code># 未下载完成即中断该进程
curl -o a.zip http://www.example.com/bigfile.zip

# 后面可以通过-C来继续下载
curl -C -o a.html http://www.example.com/bigfile.zip
</code></pre>
<h3 id="下载限速">下载限速</h3>
<p>使用-limit-rate进行限速</p>
<pre><code># 限速为100k/s
curl --limit-rate 1000k -o a.zip http://www.example.com/bigfile.zip
</code></pre>
<h3 id="根据文件修改时间来判断是否进行下载">根据文件修改时间来判断是否进行下载</h3>
<pre><code># 若文件的修改时间在2011/12/11之后，则下载
curl -z 21-Dec-11 http://www.example.com/bigfile.zip
</code></pre>
<h3 id="授权">授权</h3>
<p>在访问需要授权的页面时，可通过<code>-u</code>来提供用户名和密码进行授权</p>
<pre><code>curl -u username:password http://www.example.com
</code></pre>
<h3 id="ftp操作">ftp操作</h3>
<pre><code># 列出指定目录下的所有文件
curl -u ftpuser:ftppw -O ftp://ftp_server/public_html/

# 下载文件
curl -u ftpuser:ftppw -O
ftp://ftp_server/public_hmtl/bigfile.zip

# 上传文件
curl -u ftpuser:ftppw -T myfile.txt ftp://ftp_server/public_html/

# 上传多个文件
curl -u ftpuser:ftppw -T &quot;{myfile1.txt, myfile2.txt}&quot; ftp://ftp_server/public_html/

# 从标准输入获取内容保存到服务器的指定文件中
curl -u ftpuser:ftppw -T - ftp://ftp_server/public_html/1.txt
</code></pre>
<h3 id="设置代理">设置代理</h3>
<pre><code>curl -x proxyserver.com:1080 http://www.example.com
</code></pre>
<h3 id="保存与使用网站的cookie信息">保存与使用网站的cookie信息</h3>
<pre><code># 将网站的cookies信息保存到example_cookies文件中
curl -D example_cookies http://www.example.com

# 使用cookies信息访问url
curl -b example_cookies http://www.example.com/user/
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django 关联对象字段]]></title>
        <id>https://me.iblogc.com/post/django-guan-lian-dui-xiang-zi-duan/</id>
        <link href="https://me.iblogc.com/post/django-guan-lian-dui-xiang-zi-duan/">
        </link>
        <updated>2015-12-10T08:40:15.000Z</updated>
        <content type="html"><![CDATA[<h2 id="关联字段">关联字段</h2>
<h3 id="foreignkey多对一关系"><code>ForeignKey</code>多对一关系</h3>
<p>class ForeignKey(othermodel[, **options])</p>
<blockquote>
<p>如果关联模型还没有创建，可以使用该模型的名字而不是模型本身来指定</p>
</blockquote>
<p><em><strong>options</strong></em></p>
<h4 id="limit_chices_to"><code>limit_chices_to</code></h4>
<p>对关联对象进行过滤，可以是一个字典, 一个 Q 对象, 或者一个可调用的返回字典或者Q对象的对象</p>
<p><code>related_name</code><br>
在关联对象上返回这个对象的名称，替代默认的<code>xxx_set</code><br>
django字段选项related_name和related_query_name.md</p>
<h4 id="related_query_name"><code>related_query_name</code></h4>
<p>在关联对象上进行查询时使用的名称</p>
<h4 id="to_field"><code>to_field</code></h4>
<p>指定关联到关联对象的字段名称，默认使用关联对象主键</p>
<h4 id="db_constraint"><code>db_constraint</code></h4>
<p>是否在数据库中为这个外键创建约束的控制。默认值为 True, 而且这几乎一定是你想要的效果。如果设置成 False 对数据集成来说是很糟糕的。即便如此，有一些场景你也许想要这么设置：</p>
<ul>
<li>你有遗留的无效数据。</li>
<li>你正在对数据库缩容。<br>
如果被设置成 False, 访问一个不存在的关联对象将抛出 DoesNotExist 异常。</li>
</ul>
<h4 id="on_delete"><code>on_delete</code></h4>
<p>当此外键引用的对象被删除时，这个字段的行为</p>
<ul>
<li>CASCADE 级联删除，默认</li>
<li>PROTECT 抛出ProtectedError，阻止被引用对象删除，django.db.IntegrityError的一个子类</li>
<li>SET_NULL 把此字段设置为null，null必须为True</li>
<li>SET_DEFAULT 设置成默认值， default必须设置</li>
<li>SET() 设置一个自定义的值，可以是一个返回相应值的方法</li>
<li>DO_NOTHING 不做任务操作，不过可能会抛出IntegrityError异常，需要捕获异常并手动删除约束</li>
</ul>
<h4 id="swappable"><code>swappable</code></h4>
<h4 id="allow_unsaved_instance_assignment"><code>allow_unsaved_instance_assignment</code></h4>
<h3 id="manytomanyfield多对多关系"><code>ManyToManyField</code>多对多关系</h3>
<p>class ManyToManyField(othermodel[, **options])</p>
<h4 id="related_name"><code>related_name</code></h4>
<p>同ForeignKey.related_name</p>
<h4 id="related_query_name-2"><code>related_query_name</code></h4>
<p>同ForeignKey.related_query_name</p>
<h4 id="limit_choices_to"><code>limit_choices_to</code></h4>
<p>同ForeignKey.limit_choices_to</p>
<h4 id="symmetrical"><code>symmetrical</code></h4>
<p>只有在关联自身情况下使用，默认是对称的，即如果a是b的好友，默认b也是a的好友，要取消对称设置symmetrical为False</p>
<pre><code>from django.db import models
 
class Person(models.Model):
    friends = models.ManyToManyField(&quot;self&quot;)
</code></pre>
<h4 id="through"><code>through</code></h4>
<p>djagno默认会自动创建一个表来管理多对多的关系，但是你可以自己建立中介表，然后来手动指定。<a href="http://python.usyiyi.cn/django/ref/models/fields.html#django.db.models.ManyToManyField.through_fields">详细</a><br>
<strong>注意：手动指定中间表后，关联表的<code>add</code> <code>create</code> <code>remove</code>将不可用，但<code>clear</code>还是可以用的</strong><br>
(http://python.usyiyi.cn/django/ref/models/fields.html#django.db.models.ManyToManyField.through)</p>
<h4 id="through_fields"><code>through_fields</code></h4>
<p>在中间表中对应的字段名，只能在自定义中间表时使用[详细]</p>
<h4 id="db_table"><code>db_table</code></h4>
<p>指定中间表名称</p>
<h4 id="db_constraint-2"><code>db_constraint</code></h4>
<p>是否为外键创建约束，默认为True</p>
<h4 id="swappable-2"><code>swappable</code></h4>
<h4 id="allow_unsaved_instance_assignment-2"><code>allow_unsaved_instance_assignment</code></h4>
<h3 id="onetoonefield一对一关系"><code>OneToOneField</code>一对一关系</h3>
<p>这个是一个对一关联关系。总体上，这个字段类很像是ForeignKey设置了unique=True, 不同的是它会直接返回关系的另一边的对象。</p>
<pre><code>from django.conf import settings
from django.db import models
 
class MySpecialUser(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    supervisor = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='supervisor_of')

&gt;&gt;&gt; user = User.objects.get(pk=1)
&gt;&gt;&gt; hasattr(user, 'myspecialuser')
True
&gt;&gt;&gt; hasattr(user, 'supervisor_of')
True
</code></pre>
<h2 id="关联对象参考">关联对象参考</h2>
<pre><code>import datetime
from django.db import models
 
class Blog(models.Model):
    ...
 
class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    published = models.DateTimeField(default=datetime.datetime.now)
    ...
</code></pre>
<p>有以下方法<br>
blog.entry_set<br>
entry.blog</p>
<pre><code>class Topping(models.Model):
    # ...
    pass
 
class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)
</code></pre>
<p>有以下方法<br>
topping.pizza_set<br>
pizza.toppings</p>
<h4 id="addobj1-obj2">add(obj1[, obj2, ...])</h4>
<p>把指定的模型对象添加到关联对象集中。</p>
<pre><code>&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = Entry.objects.get(id=234)
&gt;&gt;&gt; b.entry_set.add(e) # Associates Entry e with Blog b.
</code></pre>
<p>不需要调用save()方法</p>
<h3 id="createkwargs">create(**kwargs)</h3>
<p>创建一个新的对象，保存对象，并将它添加到关联对象集之中。返回新创建的对象：</p>
<pre><code>&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = b.entry_set.create(
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )
# No need to call e.save() at this point -- it's already been saved.
</code></pre>
<p>这完全等价于（不过更加简洁于）：</p>
<pre><code>&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = Entry(
...     blog=b,
...     headline='Hello',
...     body_text='Hi',
...     pub_date=datetime.date(2005, 1, 1)
... )
&gt;&gt;&gt; e.save(force_insert=True)
</code></pre>
<p>要注意我们并不需要指定模型中用于定义关系的关键词参数。在上面的例子中，我们并没有传入blog参数给create()。Django会明白新的 Entry对象blog 应该添加到b中。</p>
<h4 id="removeobj1-obj2">remove(obj1[, obj2, ...])</h4>
<p>从关联对象集中移除执行的模型对象（不是删除）</p>
<pre><code>&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; e = Entry.objects.get(id=234)
&gt;&gt;&gt; b.entry_set.remove(e) # Disassociates Entry e from Blog b.
</code></pre>
<p>和add()相似，上面的例子中，e.save()可会执行更新操作。但是，多对多关系上的remove()，会使用QuerySet.delete()删除关系，意思是并不会有任何模型调用save()方法：如果你想在一个关系被删除时执行自定义的代码，请监听m2m_changed信号。</p>
<p>对于ForeignKey对象，这个方法仅在null=True时存在。如果关联的字段不能设置为None (NULL)，则这个对象在添加到另一个关联之前不能移除关联。在上面的例子中，从b.entry_set()移除e等价于让e.blog = None，由于blog的ForeignKey没有设置null=True，这个操作是无效的。</p>
<p>对于ForeignKey对象，该方法接受一个bulk参数来控制它如果执行操作。如果为True（默认值），QuerySet.update()会被使用。而如果bulk=False，会在每个单独的模型实例上调用save()方法。这会触发pre_save和post_save，它们会消耗一定的性能。</p>
<h4 id="clear">clear()</h4>
<p>从关联对象集中移除一切对象（不是删除）</p>
<pre><code>&gt;&gt;&gt; b = Blog.objects.get(id=1)
&gt;&gt;&gt; b.entry_set.clear()
</code></pre>
<p>注意这样不会删除对象 —— 只会删除他们之间的关联。<br>
就像 remove() 方法一样，clear()只能在 null=True的ForeignKey上被调用，也可以接受bulk关键词参数。</p>
<blockquote>
<p>注意<br>
注意对于所有类型的关联字段，add()、create()、remove()和clear()都会马上更新数据库。换句话说，在关联的任何<br>
端，都不需要再调用save()方法。<br>
同样，如果你再多对多关系中使用了中间模型，一些关联管理的方法会被禁用。</p>
</blockquote>
<h4 id="直接赋值">直接赋值</h4>
<p>通过赋值一个新的可迭代的对象，关联对象集可以被整体替换掉。</p>
<pre><code>&gt;&gt;&gt; new_list = [obj1, obj2, obj3]
&gt;&gt;&gt; e.related_set = new_list 
</code></pre>
<p>如果外键关系满足null=True，关联管理器会在添加new_list中的内容之前，首先调用clear()方法来解除关联集中一切已存在对象的关联。否则， new_list中的对象会在已存在的关联的基础上被添加。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[多说评论 UA 显示测试]]></title>
        <id>https://me.iblogc.com/post/duoshuo-comments-ua/</id>
        <link href="https://me.iblogc.com/post/duoshuo-comments-ua/">
        </link>
        <updated>2015-11-15T09:45:12.000Z</updated>
        <summary type="html"><![CDATA[<br/>]]></summary>
        <content type="html"><![CDATA[<br/>
<!--more-->
多说评论UA显示测试
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django 字段选项 related_name 和 related_query_name]]></title>
        <id>https://me.iblogc.com/post/django字段选项related-name和related-query-name/</id>
        <link href="https://me.iblogc.com/post/django字段选项related-name和related-query-name/">
        </link>
        <updated>2015-10-20T14:22:06.000Z</updated>
        <summary type="html"><![CDATA[<p><code>data</code></p>
<pre><code class="language-sqlite3">sqlite&gt; select * from author;
id      name    age
1       jim     12
2       tom     11
sqlite&gt; select * from book;
id      name    author_id
1       learn java      1
2       learn python    1
3       learn c++       2
</code></pre>
]]></summary>
        <content type="html"><![CDATA[<p><code>data</code></p>
<pre><code class="language-sqlite3">sqlite&gt; select * from author;
id      name    age
1       jim     12
2       tom     11
sqlite&gt; select * from book;
id      name    author_id
1       learn java      1
2       learn python    1
3       learn c++       2
</code></pre>
<!--more-->
<p><code>models.py</code></p>
<pre><code class="language-python"># -*- coding: UTF-8 -*-
from __future__ import unicode_literals
from django.db import models

# Create your models here.

class Author(models.Model):
    name = models.CharField(verbose_name='姓名', max_length=50)
    age = models.IntegerField(verbose_name='年龄')

class Book(models.Model):
    name = models.CharField(verbose_name='书名', max_length=100)
    author = models.ForeignKey(Author, verbose_name='作者')
</code></pre>
<p>执行语句</p>
<pre><code>&gt;&gt;&gt; Author.objects.filter(book__name='learn java')
[&lt;Author: jim&gt;]
&gt;&gt;&gt; author = Author.objects.get(pk=1)
&gt;&gt;&gt; author.book_set.all()
[&lt;Book: learn java&gt;, &lt;Book: learn python&gt;]
</code></pre>
<p>假如把类<code>Book</code>改成这样</p>
<pre><code>class Book(models.Model):
    name = models.CharField(verbose_name='书名', max_length=100)
    author = models.ForeignKey(Author, verbose_name='作者', related_name='bs', related_query_name='b')
</code></pre>
<p>那么上面查询代码就应该写成这样</p>
<pre><code>&gt;&gt;&gt; Author.objects.filter(b__name='learn java')
[&lt;Author: jim&gt;]
&gt;&gt;&gt; author = Author.objects.get(pk=1)
&gt;&gt;&gt; author.bs.all()
[&lt;Book: learn java&gt;, &lt;Book: learn python&gt;]
</code></pre>
<blockquote>
<p>如果<code>book </code>表里有两个字段都外键关联<code>author </code>表，这时<code>related_name</code>就非常有用了。</p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git stash 的简单运用]]></title>
        <id>https://me.iblogc.com/post/git-stash的简单运用/</id>
        <link href="https://me.iblogc.com/post/git-stash的简单运用/">
        </link>
        <updated>2015-10-20T14:16:18.000Z</updated>
        <summary type="html"><![CDATA[<p>当我们在分支上改代码改到一半时，突然项目发现了一个bug需要修复，这时需要切换到另一个分支进行修改，以前的做法可能是把工作区改到一半的代码先commit，然后切换分支修复bug，再切换回来继续写代码，但这样会生成很多不必要的提交，这时你就需要使用<code>git stash</code>命令。</p>
]]></summary>
        <content type="html"><![CDATA[<p>当我们在分支上改代码改到一半时，突然项目发现了一个bug需要修复，这时需要切换到另一个分支进行修改，以前的做法可能是把工作区改到一半的代码先commit，然后切换分支修复bug，再切换回来继续写代码，但这样会生成很多不必要的提交，这时你就需要使用<code>git stash</code>命令。</p>
<!--more-->
<p><code>git stash</code>命令可将工作区的改动存储git栈，运行<code>git stash</code>之后，可以再运行<code>git status -s</code>验证下发现目录和上交commit时是一致的，没有任何修改，这时你就可以切换到其它分支进行工作，当你完成工作后，再切换回来，使用<code>git stash pop</code>可以从Git栈中读取最近一次保存的内容，恢复到工作区。</p>
<pre><code>git stash: 备份当前的工作区的内容，从最近的一次提交中读取相关内容，让工作区保证和上次提交的内容一致。同时，将当前的工作区内容保存到Git栈中。
git stash save &quot;message&quot;: 备份当前的工作区的内容，并添加备注信息
git stash list: 显示git栈内的所有备份，可以利用这个列表来决定从那个地方恢复。
git stash pop stash@{0}: 从git栈中读取并恢复工作区，然后删除对应的记录，默认恢复最新的（stash@{0}为最新）
git stash apply stash@{0}: 同git stash pop，但不会删除对应的记录
git stash drop: 删除最新的一个备份
git stash clear: 清空git栈。此时使用gitg等图形化工具会发现，原来stash的哪些节点都消失了。
</code></pre>
<h2 id="参考">参考</h2>
<p>http://www.tuicool.com/articles/rUBNBvI<br>
及<code>git stash --help</code></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git patch 的简单运用]]></title>
        <id>https://me.iblogc.com/post/git-patch的简单运用/</id>
        <link href="https://me.iblogc.com/post/git-patch的简单运用/">
        </link>
        <updated>2015-10-15T12:49:40.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="生成patch">生成PATCH</h2>
<p>往前n个提前内容的patch</p>
<pre><code>git format-patch -n
</code></pre>
<p>某个commit（含）的及之前的n-1次提交的patch</p>
<pre><code>git format-patch -n SHA
</code></pre>
]]></summary>
        <content type="html"><![CDATA[<h2 id="生成patch">生成PATCH</h2>
<p>往前n个提前内容的patch</p>
<pre><code>git format-patch -n
</code></pre>
<p>某个commit（含）的及之前的n-1次提交的patch</p>
<pre><code>git format-patch -n SHA
</code></pre>
<!--more-->
<p>某个commit的patch</p>
<pre><code>git format-patch -1 SHA
</code></pre>
<p>当前分支所有超前master提交的patch</p>
<pre><code>git format-patch -M master
</code></pre>
<p>两个commit之间的所有patch（不包含较早SHA1提交的内容）</p>
<pre><code>git format-patch SHA1...SHA1
</code></pre>
<p>某个commit之后的所有patch</p>
<pre><code>git format-patch -s SHA
</code></pre>
<h2 id="应用patch">应用PATCH</h2>
<p>检查patch</p>
<pre><code>git apply --stat xxx.patch
</code></pre>
<p>检查能否应用成功</p>
<pre><code>git apply --check xxx.patch
</code></pre>
<p>打补丁</p>
<pre><code>git am -s xxx.patch
</code></pre>
<p>如果有冲突，整个PATCH都不会被集成，接来来解决冲突问题</p>
<pre><code># 把没有冲突的文件先合并了，剩下有冲突的作了标记
git apply PATCH --reject
# 这里手动解决冲突
# 把解决冲突的和PATCH里新加的文件全部add进来，因为git am并不会改变index
git add FIXED_FILES
git am --resolved
</code></pre>
<h2 id="参考">参考</h2>
<p>http://blog.csdn.net/daydring/article/details/42676987</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Material Design 资源]]></title>
        <id>https://me.iblogc.com/post/material-design资源/</id>
        <link href="https://me.iblogc.com/post/material-design资源/">
        </link>
        <updated>2015-10-15T12:19:42.000Z</updated>
        <summary type="html"><![CDATA[<p>Material Design是谷歌在Google I/O 2014推出了全新的设计语言，并荣获2014年的用户体验最佳贡献金奖。</p>
]]></summary>
        <content type="html"><![CDATA[<p>Material Design是谷歌在Google I/O 2014推出了全新的设计语言，并荣获2014年的用户体验最佳贡献金奖。</p>
<!--more-->
<h2 id="更新记录">更新记录</h2>
<p>2015-10-15 初稿</p>
<h2 id="演示视频">演示视频</h2>
<iframe src="http://player.youku.com/embed/XNzMxNzUyNzQ0" frameborder=0 allowfullscreen></iframe>
## Material Design 文档
官方
> http://www.google.com/design/spec/material-design/introduction.html
<p>Material Design 中文版（官网翻译而来）</p>
<blockquote>
<p>阅读: http://wiki.jikexueyuan.com/project/material-design/<br>
GitHub: https://github.com/1sters/material_design_zh_2<br>
PDF: http://pan.baidu.com/s/1fr1gi</p>
</blockquote>
<p>Material Design 中文版（官网翻译而来）</p>
<blockquote>
<p>阅读: http://design.1sters.com/ （已关停）<br>
GitHub: https://github.com/1sters/material_design_zh</p>
</blockquote>
<p>Google Material Design 正體中文版（官网翻译而来）</p>
<blockquote>
<p>阅读: https://www.gitbook.com/book/wcc723/google_design_translate/details<br>
GitHub: https://github.com/Wcc723/google_design_translate<br>
PDF: http://pan.baidu.com/s/1dD8X1Zj</p>
</blockquote>
<p>Material Design非官方中文指导手册</p>
<p>PDF: http://pan.baidu.com/s/1mg3P45i</p>
<h2 id="android资源">Android资源</h2>
<p>资源大合集<br>
https://github.com/lightSky/Awesome-MaterialDesign</p>
<p>十大Material Design开源项目<br>
http://www.csdn.net/article/2014-11-21/2822753-material-design-libs/1</p>
<h2 id="图标">图标</h2>
<p>Material icons guide</p>
<blockquote>
<p>主页: http://google.github.io/material-design-icons/<br>
GitHub: https://github.com/google/material-design-icons</p>
</blockquote>
<h2 id="网页框架">网页框架</h2>
<p>Angular Material（控件最全，样式不错，交互动态效果多）</p>
<blockquote>
<p>主页: https://material.angularjs.org/<br>
GitHub: https://material.angularjs.org/</p>
</blockquote>
<p>material ui（控件较多，样式不错，交互动态效果多）</p>
<blockquote>
<p>主页: http://material-ui.com/<br>
GitHub: https://github.com/callemall/material-ui</p>
</blockquote>
<p>bootstrap-material-design（控件数量还行，样式较好，交互动态效果还行，Demo里部分样式有错位问题）</p>
<blockquote>
<p>主页: http://fezvrasta.github.io/bootstrap-material-design/<br>
GitHub: http://fezvrasta.github.io/bootstrap-material-design/</p>
</blockquote>
<p>muicss（控件较少，样式普通，交互动态效果少）（体积小，css+js 11.4k）</p>
<blockquote>
<p>主页: https://www.muicss.com/<br>
GitHub: https://github.com/muicss/mui</p>
</blockquote>
<p>MaterializeCss（控件数量还行，样式较好，交互动态效果还行）</p>
<blockquote>
<p>主页: http://materializecss.com/<br>
GitHub: https://github.com/Dogfalo/materialize</p>
</blockquote>
<p>bootswatch里的parper主题</p>
<blockquote>
<p>主页: http://bootswatch.com/paper/<br>
GitHub: https://github.com/thomaspark/bootswatch</p>
</blockquote>
<p>jQuery Mobile Material Theme（控件较少，样式普通，无交互动态效果）</p>
<blockquote>
<p>主页: http://store.ququplay.com/</p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git 常用命令]]></title>
        <id>https://me.iblogc.com/post/git常用命令/</id>
        <link href="https://me.iblogc.com/post/git常用命令/">
        </link>
        <updated>2015-09-11T12:05:13.000Z</updated>
        <summary type="html"><![CDATA[<p>之前写过一篇<a href="/post/git%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/">Git命令使用指南</a>，但感觉那个写的太乱，不接地气，有时我自己找一个命令都难找，所以今天写一篇文章整理一些比较基础的，但又不常用的一些命令，后面会慢慢更新。</p>
]]></summary>
        <content type="html"><![CDATA[<p>之前写过一篇<a href="/post/git%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/">Git命令使用指南</a>，但感觉那个写的太乱，不接地气，有时我自己找一个命令都难找，所以今天写一篇文章整理一些比较基础的，但又不常用的一些命令，后面会慢慢更新。</p>
<!--more-->
<h2 id="查看修改">查看修改</h2>
<pre><code class="language-shell"># 查看文件修改/变动情况
git status -s
</code></pre>
<h2 id="提交">提交</h2>
<pre><code class="language-shell"># 把所有已跟踪的文件添加到暂存区
git add -u
# 把所有已跟踪并有更新的文件提交到本地仓库
git commit -am &quot;update message&quot;
</code></pre>
<h2 id="列出分支">列出分支</h2>
<pre><code># 列出本地分支
git branch
或
git branch -v

# 列出本地和远程所有分支
git branch -a
或
git branch -va
</code></pre>
<h2 id="删除分支">删除分支</h2>
<pre><code># 删除本地分支
git branck -D/-d &lt;branch&gt;

# 删除远程分支，注意冒号前有空格
git push origin :&lt;分支名&gt;
# 等价于
git push origin -d &lt;branch&gt;
</code></pre>
<h2 id="推送分支">推送分支</h2>
<pre><code># 推送当前分支到默认remote上，remote上没有对应分支则自动创建
git push

# 推送当前分支到指定remote，remote上没有对应分支则自动创建
git push &lt;remote&gt;

# 推送到指定分支到remote的指定分支上
git push &lt;remote&gt; &lt;remote_branch&gt;:&lt;loclal_branch&gt;
</code></pre>
<h2 id="重命名分支">重命名分支</h2>
<pre><code>git branch (-m | -M) [&lt;old-branch&gt;] &lt;new-branch&gt;
</code></pre>
<h2 id="拉取远程分支到本地">拉取远程分支到本地</h2>
<pre><code>git checkout -b &lt;branch&gt; &lt;remote&gt;/&lt;branch&gt;
</code></pre>
<p>或</p>
<pre><code>git checkout --track &lt;remote&gt;/&lt;branch&gt;
</code></pre>
<h2 id="撤消提交未push情况下">撤消提交（未<code>push</code>情况下）</h2>
<pre><code>git reset --mixed &lt;SHA1&gt; # 此SHA1之后的commit全部撤消，并回退index，工作空间代码不变，--mixed可省略
git reset --soft &lt;SHA1&gt; # 此SHA1之后的commit全部撤消，工作空间代码和index不变
git reset --hard &lt;SHA1&gt; # 此SHA1之后的commit全部撤消，工作空间代码和index全部退回
</code></pre>
<h2 id="查看commit记录">查看<code>commit</code>记录</h2>
<pre><code>git log --oneline -n # 单行显示最后n个commit的记录
</code></pre>
<h2 id="本地分支和远程分支做关联">本地分支和远程分支做关联</h2>
<pre><code>git branch --set-upstream-to=&lt;remote&gt;/&lt;remote_branch&gt; &lt;loclal_branch&gt;
</code></pre>
<h2 id="推送本地当前新分支到远程">推送本地当前新分支到远程</h2>
<pre><code>git push -u origin &lt;branch_name&gt;
</code></pre>
<h2 id="列出tag">列出tag</h2>
<pre><code>git tag -l
</code></pre>
<h2 id="删除tag">删除tag</h2>
<pre><code># 删除本地tag
git tag -d &lt;tag_name&gt;
# 删除远程tag
git push origin :refs/tags/&lt;tag_name&gt;
</code></pre>
<h2 id="推送本地tag到远程">推送本地tag到远程</h2>
<pre><code># 推送单个tag
git push origin &lt;tag_name&gt;
# 推送所有tag
git push origin --tags
</code></pre>
<h2 id="查看日志">查看日志</h2>
<pre><code># 当前分支日志
git log
# 所有本地分支日志
git log --all
# 指定本地分支日志
git log &lt;branch_name&gt;
# 指定远程分支日志
git log origin/&lt;branch_name&gt;
# 所有远程分支日志
git log --all origin
</code></pre>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Django 内置的 flatpages 应用]]></title>
        <id>https://me.iblogc.com/post/django内置的flatpages应用/</id>
        <link href="https://me.iblogc.com/post/django内置的flatpages应用/">
        </link>
        <updated>2015-09-08T13:17:55.000Z</updated>
        <summary type="html"><![CDATA[<p>不知道大家平时写网站时类似「关于页面」，「用户协议」这类页面是如何处理的。这里列出了这类页面的两个特点：</p>
<ol>
<li>页面数据简单（不会有很多动态数据展示）;</li>
<li>有更新但频率不高;</li>
</ol>
<p>如果要为这类页面展示建立多个数据表，显然是很浪费的行为，但如果直接写成静态页面文件，更新又比较很麻烦，这时候就可以使用<code>flatpages </code>来解决这类问题了。</p>
]]></summary>
        <content type="html"><![CDATA[<p>不知道大家平时写网站时类似「关于页面」，「用户协议」这类页面是如何处理的。这里列出了这类页面的两个特点：</p>
<ol>
<li>页面数据简单（不会有很多动态数据展示）;</li>
<li>有更新但频率不高;</li>
</ol>
<p>如果要为这类页面展示建立多个数据表，显然是很浪费的行为，但如果直接写成静态页面文件，更新又比较很麻烦，这时候就可以使用<code>flatpages </code>来解决这类问题了。</p>
<!--more-->
<p><code>django.contrib.flatpages</code>是<code>Django </code>的内置app，用于添加更新的一些简单的页面，具体设置，请继续查看以下步骤。</p>
<h2 id="安装">安装</h2>
<p>首先确保<code>INSTALLED_APPS</code>中已经存在<code>django.contrib.sites</code>，因为<code>django.contrib.flatpages</code>依赖于此包。<br>
<code>settings.py</code></p>
<pre><code class="language-python">INSTALLED_APPS = (
    # ...
    'django.contrib.sites',
    'django.contrib.flatpages',
)
# ...
# 如果没有设置`SITE_ID`值，则需要设置，这里直接设置为1
SITE_ID = 1
</code></pre>
<p>执行<code>python manage.py migrate</code>建表</p>
<h2 id="配置">配置</h2>
<p>路由配置可先以有多种形式<br>
<code>urls.py</code><br>
第一种（需放在最后，推荐）</p>
<pre><code>from django.contrib.flatpages import views
urlpatterns += [
    url(r'^(?P&lt;url&gt;.*/)$', views.flatpage),
]
</code></pre>
<p>第二种（每个页面都需要写一个url，推荐）</p>
<pre><code>from django.contrib.flatpages import views
urlpatterns = [
    url(r'^about-us/$', views.flatpage, {'url': '/about-us/'}, name='about'),
    url(r'^license/$', views.flatpage, {'url': '/license/'}, name='license'),
]
</code></pre>
<p>或者，如果你不想配置路由，还有一种更简单的方法，直接在<code>settings.py</code>的里添加中间件</p>
<pre><code>MIDDLEWARE_CLASSES = (
    # ...
    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
)
</code></pre>
<p><em>为确保配置生效，保险的方法是把<code>django.contrib.flatpages.middleware.FlatpageFallbackMiddleware</code>放在最后一行</em></p>
<h2 id="管理flatpages">管理<code>flatpages</code></h2>
<p>默认的你可以登录超级管理员后台（如果开启），找到<code>Flat pages</code>，进去点击添加，可以看到可配置的选项有，<code>URL</code> <code>Title</code> <code>Content</code> <code>Site</code> <code>Enable comments</code> <code>Registration required</code> <code>Template name</code></p>
<h3 id="数据项说明">数据项说明</h3>
<ul>
<li><code>URL</code>:  页面所处的 URL，不包括域名，但是包含前导斜杠 (例如 /about/contact/ )</li>
<li><code>Title</code>: 页面的标题，框架不对它作任何特殊处理。由你通过模板来显示它</li>
<li><code>Content</code>: 页面的内容 (即 HTML 页面)，框架不会对它作任何特别处理。由你负责使用模板来显示</li>
<li><code>Site</code>: 页面放置的站点，该项设置集成了 Django 多站点框架</li>
<li><code>Enable comments</code>: 是否允许该简单页面使用评论，框架不对此做任何特别处理。你可在模板中检查该值并根据需要显示评论窗体</li>
<li><code>Registration required</code>: 是否注册用户才能查看此简单页面，该设置项集成了 Djangos 验证/用户框架，该框架于第十二章详述。</li>
<li><code>Template name</code>: 用来解析该简单页面的模板名称，这是一个可选项，如果未指定模板或该模板不存在，系统会退而使用默认模板 <code>flatpages/default.html</code>（我在<code>Django1.8.4</code>里死活没找到，只好自己写好一个扔进去）</li>
</ul>
<p>当添加相应的数据后，剩下工作就交给<code>flatpages</code>吧，如果你是使用中间件形式的，则<code>flatpages </code>会在配置完所有<code>urls.py</code>后，没有找到配置到对应的<code>URL</code>，才会到<code>flatpages </code>中查找，如果还是找不到，则会引发<code>Http404</code>异常，即<code>FlatpageFallbackMiddleware </code>只在<code>404</code>时会被激活，而不会在<code>500</code>或其它错误响应时被激活。</p>
<p>如果你需要自己定制，则可以针对<code>django/contrib/flatpages/models.py</code>自己写增删改方法就可以。<br>
<code>models.py</code></p>
<pre><code class="language-python">class FlatPage(models.Model):
    url = models.CharField(_('URL'), max_length=100, db_index=True)
    title = models.CharField(_('title'), max_length=200)
    content = models.TextField(_('content'), blank=True)
    enable_comments = models.BooleanField(_('enable comments'), default=False)
    template_name = models.CharField(_('template name'), max_length=70, blank=True,
        help_text=_(
            &quot;Example: 'flatpages/contact_page.html'. If this isn't provided, &quot;
            &quot;the system will use 'flatpages/default.html'.&quot;
        ),
    )
    registration_required = models.BooleanField(_('registration required'),
        help_text=_(&quot;If this is checked, only logged-in users will be able to view the page.&quot;),
        default=False)
    sites = models.ManyToManyField(Site)
 
    class Meta:
        db_table = 'django_flatpage'
        verbose_name = _('flat page')
        verbose_name_plural = _('flat pages')
        ordering = ('url',)
 
    def __str__(self):
        return &quot;%s -- %s&quot; % (self.url, self.title)
 
    def get_absolute_url(self):
        # Handle script prefix manually because we bypass reverse()
        return iri_to_uri(get_script_prefix().rstrip('/') + self.url)
</code></pre>
<h2 id="模板">模板</h2>
<p>默认模板路径为<code>flatpages/default.html</code></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;{{ flatpage.title }}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
{{ flatpage.content }}
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<blockquote>
<p>在实际应用中，我们不太可能会使用默认的模板，你可能需要自己写一个漂亮模板，比如有一个头部和底部，头部可能还需要添加<code>requeset.user</code>显示用户信息等。</p>
</blockquote>
<h2 id="高级应用">高级应用</h2>
<p>获取<code>flatpages</code>实例列表</p>
<pre><code>{% load flatpages %}
{% get_flatpages as flatpages %}
</code></pre>
<p>获取当前用户能打开的<code>flatpages</code>实例列表</p>
<pre><code>{% load flatpages %}
{% get_flatpages for request.user as about_pages %}
</code></pre>
<p>获取链接以<code>/about/</code>为开头的<code>flatpages</code>实例列表</p>
<pre><code>{% load flatpages %}
{% get_flatpages '/about/' as about_pages %}
</code></pre>
<p>上面两种也可以组合使用</p>
<pre><code>{% load flatpages %}
{% get_flatpages '/about/' for someuser as about_pages %}
</code></pre>
<h2 id="生成sitemapsxml">生成<code>sitemaps.xml</code></h2>
<pre><code>from django.conf.urls import url
from django.contrib.flatpages.sitemaps import FlatPageSitemap
from django.contrib.sitemaps.views import sitemap
 
urlpatterns = [
    # ...
 
    # the sitemap
    url(r'^sitemap\.xml$', sitemap,
        {'sitemaps': {'flatpages': FlatPageSitemap}},
        name='django.contrib.sitemaps.views.sitemap'),
]
</code></pre>
<h2 id="容易踩的坑">容易踩的坑</h2>
<p>最好把<code>settings.py</code>里的<code>APPEND_SLASH</code>设置为<code>Ture</code>， 这样不管是<code>/about-us</code>还是<code>/about-us/</code>都可以访问到。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>https://docs.djangoproject.com/en/1.8/ref/contrib/flatpages/</li>
<li>http://djangobook.py3k.cn/2.0/chapter16/</li>
</ul>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 Synergy 实现多台电脑共享一套键鼠]]></title>
        <id>https://me.iblogc.com/post/使用synergy实现多台电脑共享一套键鼠/</id>
        <link href="https://me.iblogc.com/post/使用synergy实现多台电脑共享一套键鼠/">
        </link>
        <updated>2015-08-29T15:52:41.000Z</updated>
        <summary type="html"><![CDATA[<p>因为家里有一台台式和一台笔记本，台式<code>Windows10</code>为日常使用，笔记本<code>Ubuntu&amp;Windows7</code>，以前一直在<code>Windows</code>上敲代码，现在正转向<code>Ubuntu</code>，但家里桌子上摆放两台电脑已经有点挤了，如果再来两套键鼠那就成二手电脑配件甩卖铺了，所以上网查了下看看有没有软件能实现两台电脑共享一套键盘的，还真找到了一款叫<code>Synergy</code>的软件，支持<code>Windows</code>, <code>Mac OS X</code>, <code>Linux </code>三大系统，软件是下载收费，使用使用免费，官网上标明基础版$10，高级版$29。下面我说说我自己的配置过程。</p>
]]></summary>
        <content type="html"><![CDATA[<p>因为家里有一台台式和一台笔记本，台式<code>Windows10</code>为日常使用，笔记本<code>Ubuntu&amp;Windows7</code>，以前一直在<code>Windows</code>上敲代码，现在正转向<code>Ubuntu</code>，但家里桌子上摆放两台电脑已经有点挤了，如果再来两套键鼠那就成二手电脑配件甩卖铺了，所以上网查了下看看有没有软件能实现两台电脑共享一套键盘的，还真找到了一款叫<code>Synergy</code>的软件，支持<code>Windows</code>, <code>Mac OS X</code>, <code>Linux </code>三大系统，软件是下载收费，使用使用免费，官网上标明基础版$10，高级版$29。下面我说说我自己的配置过程。</p>
<!--more-->
<h2 id="安装">安装</h2>
<p>在<code>Ubuntu</code>里打开终端，输入以下命令进行安装</p>
<pre><code>sudo apt-get install synergy
</code></pre>
<p>在<code>Windwos</code>上双击安装。</p>
<p><em><code>Ubuntu</code>我安装的是1.6.2，<code>Windwos</code>上是1.7.4 x64</em></p>
<h2 id="配置">配置</h2>
<h3 id="服务器设置">服务器设置</h3>
<blockquote>
<p><code>synergy</code>需要一台电脑做为服务端，其它电脑做为客户端来连接服务端。<br>
本来是我想选择<code>Ubuntu</code>做为服务端的，但设置好后链接失败提示为<code>WARNING: failed to connect to server: incompatible client 1.5</code>，似乎是不兼容，但我的客户端版本是<code>1.6.2</code>不是<code>1.5</code>啊，所以作罢，只得选用<code>Windwos</code>来做服务端。</p>
</blockquote>
<ol>
<li>
<p>我们选用<code>Windwos</code>来做服务端，在<code>Windwos</code>打开软件，选择「server」；<br>
Synergy-Windows-1.png)</p>
</li>
<li>
<p>点击「设置服务端」进行添加客户端操作；<br>
Synergy-Windows-3.png)</p>
</li>
<li>
<p>从右上角手动电脑图标到下方的格子里，这里的格式位置对应你当前几台电脑的实际的以我把电脑图标拖到中间左侧的格子里；<br>
Synergy-Windows-4.png)</p>
</li>
<li>
<p>双击电脑图标进行编辑，这里我们只需要输入客户端电脑的计算机名，其它都默认；<br>
Synergy-Windows-5.png)</p>
</li>
<li>
<p>设置好后点击再次ok，回到设置首页，点击「开始」启动服务端；<br>
Synergy-Windows-2.png)</p>
</li>
</ol>
<h3 id="客户端设置">客户端设置</h3>
<ol>
<li>客户端比较简单，在<code>Ubuntu</code>上打开<code>Synergy</code>，选择「Client」，在<code>Server IP</code>里输入服务端的IP，点击「Start」即可；<br>
Synergy-Ubuntu-1.png)</li>
</ol>
<h3 id="开机自启">开机自启</h3>
<p>要在<code>Ubuntu</code>开机在登录界面前启动<code>synergy</code>，编辑<code>/etc/lightdm/lightdm.conf</code>文件添加<code>display-setup-script=/usr/bin/synergyc 192.168.9.102</code>，把<code>192.168.9.102</code>换成你自己的<code>synergy</code>服务端IP。</p>
<h2 id="完成">完成</h2>
<p>到这里你就会发现你可鼠标可以在两个电脑屏幕上移动了，像我刚才配置的是在<code>Windows</code>左侧添加了<code>Ubuntu</code>，所以当我在<code>Windows</code>上把鼠标向左移动，并移到边界，再继续左移时，鼠标就会出现在<code>Ubuntu</code>屏幕上，键盘的行为跟随鼠标的，即鼠标在哪个屏幕，键盘输入就对应哪个屏幕的系统。</p>
<blockquote>
<p>网上说<code>Synergy</code>支持在不同电脑间复制粘贴，目前我自己没有试成功，有知道朋友可以和说。</p>
</blockquote>
<h2 id="相关">相关</h2>
<p>项目主页：https://github.com/synergy/synergy</p>
<h2 id="更新记录">更新记录</h2>
<p>2015-08-29 初稿<br>
2015-09-03 补充<code>Ubuntu</code>客户端自启明说</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ubuntu 从 14.10 升级到 15.04]]></title>
        <id>https://me.iblogc.com/post/ubuntu从14-10升级到15-04/</id>
        <link href="https://me.iblogc.com/post/ubuntu从14-10升级到15-04/">
        </link>
        <updated>2015-08-29T10:23:11.000Z</updated>
        <summary type="html"><![CDATA[<p>ubuntu 15.04 已经发布几个月了，今天准备把家里的笔记本升级下，在这记录下，因为笔记本是14.10的所以下面内容只适用于从14.10升级到15.04的，如果你的系统是14.04，则需要先升级到14.10然后再升级到15.04。</p>
]]></summary>
        <content type="html"><![CDATA[<p>ubuntu 15.04 已经发布几个月了，今天准备把家里的笔记本升级下，在这记录下，因为笔记本是14.10的所以下面内容只适用于从14.10升级到15.04的，如果你的系统是14.04，则需要先升级到14.10然后再升级到15.04。</p>
<!--more-->
<p>先检查系统更新，如果有则进行更新，在终端中输入以下命令</p>
<pre><code>sudo apt-get update &amp;&amp; sudo apt-get dist-upgrade
</code></pre>
<p>然后检查是否有可用的版本</p>
<pre><code>sudo update-manager -d
</code></pre>
<p>回车后会弹出软件更新器，等检查完成后点击升级，后面还会出来很多个确认对话框，一路确定下去就好。</p>
<p>最后点击开始升级，然后就等着更新自动下载更新吧。<br>
ubuntu-14.10-15.04-01.png)</p>
<p>更新完后重启系统即可完成升级。<br>
ubuntu-15.04-info.png)</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[XY-Problem]]></title>
        <id>https://me.iblogc.com/post/xy-problem/</id>
        <link href="https://me.iblogc.com/post/xy-problem/">
        </link>
        <updated>2015-07-04T15:51:40.000Z</updated>
        <content type="html"><![CDATA[<p>XY问题，一个对我来说比较新的词汇，但可能我们大家平时都遇到过这种问题。</p>
<p>对于“XY问题”，不同的人有不同的解释：</p>
<blockquote>
<p>你想做X，但你认为Y是实现X最好的方法。你不问关于X的事，反而问起Y的事。</p>
</blockquote>
<blockquote>
<p>— 来自 Re: sequencial file naming by Abigail<!--more--></p>
</blockquote>
<hr>
<blockquote>
<p>你尝试去做X，但你想起了Y方案。于是，你开始问关于Y方案的事，完全不提X。问题是，也许会有更好的方案，但如果你不描述X是什么，我们根本无法出谋划策。<br>
— 来自 Re: How do I keep the command line from eating the backslashes? by revdiablo</p>
</blockquote>
<hr>
<blockquote>
<p>有些人问如何去做Y，但他们实际是想做X。他们之所以问如何做Y，因为他们相信Y是实现X最好的方法。人们用各种的“试试这个”来给予帮助，而结果往往是“这不行，因为….”。这给我们提示，依赖环境的不同，你的问题可能会有其它更好的方案。<br>
— 来自 Re: Re: Re: Re: regex to validate e-mail addresses and phone numbers by Limbic~Region</p>
</blockquote>
<hr>
<blockquote>
<p>在不理解更大的问题(上下文)X的情况下，去回答问题Y，往往完全无助于解决问题X。<br>
— 来自 <a href="mailto:m18zt5muq9.fsf_-_@halfdome.holdit.com">m18zt5muq9.fsf_-_@halfdome.holdit.com</a> by merlyn</p>
</blockquote>
<hr>
<blockquote>
<p>也叫做“过早下结论”：有疑问的人希望能解决一些阐述的并不清楚的问题X，他们断定Y是解决方案的一个要素，于是他们就询问如何实现Y。<br>
— from <a href="mailto:Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch">Pine.GHP.4.21.0009061210570.8800-100000@hpplus03.cern.ch</a> by Alan J. Flavell</p>
</blockquote>
<hr>
<blockquote>
<p>XY问题是指，当你需要做X时，你认为可以用Y来实现X，于是你问如何做Y，而你实际应该做的是说明你的X问题是什么。也许会有一个比Y更好的Z解决方案，但如果X没有被说出来，没有人能提出更好的建议。<br>
— 来自 <a href="mailto:slrn89um8j.5g9.tadmc@magna.metronet.com">slrn89um8j.5g9.tadmc@magna.metronet.com</a> by Tad McClellan</p>
</blockquote>
<hr>
<blockquote>
<p>当有人来问如何做一些傻事时，我真的不知道如何去做。我只能照实回答，说我可不要告诉别人如何做傻事…..</p>
</blockquote>
<blockquote>
<p>但是，一旦我这样做了，人们就会蹦到我面前自作聪明。这种事情经常发生。(“别呀，帮帮这个可怜的人吧，如果你知道他们真的需要知道如何做，你干嘛不告诉他们呢？”)</p>
</blockquote>
<blockquote>
<p>. . .</p>
</blockquote>
<blockquote>
<p>另一方面，我可以从另一个层面上回答他们，给他们一个更好的方案，但这种执教也许会让他们脸上不好看。如果他们接受倒好，如果不接受，你会很伤心看到自己的努力和好建议被忽略。同样，人们会蹦到你面前指责你没有直接回答他们的问题。（“谁要你告诉他该怎么做了，你只要回答他的问题就行了。”）</p>
</blockquote>
<blockquote>
<p>. . .</p>
</blockquote>
<blockquote>
<p>我想这种两种回答方式生活中都经常会有。但也许没有一种回答会得到好结果。<br>
— 来自 <a href="mailto:6lnb70$lct$1@monet.op.net">6lnb70$lct$1@monet.op.net</a> by MJD</p>
</blockquote>
<hr>
<p>部分内容引用自<a href="http://www.vaikan.com/xy-problem/">外刊IT评论</a></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[python 中的 UUID]]></title>
        <id>https://me.iblogc.com/post/python中的UUID/</id>
        <link href="https://me.iblogc.com/post/python中的UUID/">
        </link>
        <updated>2015-06-02T15:32:59.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="简介维基百科">简介（维基百科）</h2>
<blockquote>
<p>通用唯一识别码（英语：Universally Unique Identifier，简称UUID）是一种软件建构的标准，亦为开放软件基金会组织在分散式计算环境领域的一部份。<br>
UUID的目的，是让分散式系统中的所有元素，都能有唯一的辨识资讯，而不需要透过中央控制端来做辨识资讯的指定。如此一来，每个人都可以建立不与其它人冲突的UUID。在这样的情况下，就不需考虑资料库建立时的名称重复问题。目前最广泛应用的UUID，是微软公司的全局唯一标识符（GUID），而其他重要的应用，则有Linux ext2/ext3档案系统、LUKS加密分区、GNOME、KDE、Mac OS X等等。另外我们也可以在e2fsprogs套件中的UUID函式库找到实现。[[3]][1]</p>
</blockquote>
]]></summary>
        <content type="html"><![CDATA[<h2 id="简介维基百科">简介（维基百科）</h2>
<blockquote>
<p>通用唯一识别码（英语：Universally Unique Identifier，简称UUID）是一种软件建构的标准，亦为开放软件基金会组织在分散式计算环境领域的一部份。<br>
UUID的目的，是让分散式系统中的所有元素，都能有唯一的辨识资讯，而不需要透过中央控制端来做辨识资讯的指定。如此一来，每个人都可以建立不与其它人冲突的UUID。在这样的情况下，就不需考虑资料库建立时的名称重复问题。目前最广泛应用的UUID，是微软公司的全局唯一标识符（GUID），而其他重要的应用，则有Linux ext2/ext3档案系统、LUKS加密分区、GNOME、KDE、Mac OS X等等。另外我们也可以在e2fsprogs套件中的UUID函式库找到实现。<a href="http://zh.wikipedia.org/zh-hans/%E9%80%9A%E7%94%A8%E5%94%AF%E4%B8%80%E8%AF%86%E5%88%AB%E7%A0%81" title="通用唯一识别码">[3]</a></p>
</blockquote>
<!--more-->
<h2 id="定义">定义 :</h2>
<p>UUID是由一组32位数的16进位数字所构成，是故UUID理论上的总数为1632=2128，约等于3.4 x 1038。也就是说若每纳秒产生1兆个UUID，要花100亿年才会将所有UUID用完，，它保证对在同一时空中的所有机器都是唯一的（重复机率请参考<a href="http://zh.wikipedia.org/zh-hans/%E9%80%9A%E7%94%A8%E5%94%AF%E4%B8%80%E8%AF%86%E5%88%AB%E7%A0%81">随机UUID的重复机率</a>）。</p>
<h2 id="算法">算法</h2>
<ol>
<li>uuid1()——基于时间戳<br>
由MAC地址、当前时间戳、随机数生成。可以保证全球范围内的唯一性，<br>
但MAC的使用同时带来安全性问题，局域网中可以使用IP来代替MAC。</li>
<li>uuid2()——基于分布式计算环境DCE（Python中没有这个函数）<br>
算法与uuid1相同，不同的是把时间戳的前4位置换为POSIX的UID。<br>
实际中很少用到该方法。</li>
<li>uuid3()——基于名字的MD5散列值<br>
通过计算名字和命名空间的MD5散列值得到，保证了同一命名空间中不同名字的唯一性，<br>
和不同命名空间的唯一性，但同一命名空间的同一名字生成相同的uuid。</li>
<li>uuid4()——基于随机数<br>
由伪随机数得到，有一定的重复概率，该概率可以计算出来。</li>
<li>uuid5()——基于名字的SHA-1散列值<br>
算法与uuid3相同，不同的是使用 Secure Hash Algorithm 1 算法</li>
</ol>
<h2 id="在python中在生成uuid">在<code>python</code>中在生成UUID</h2>
<p><code>import uuid</code>后即可使用<br>
示例代码</p>
<pre><code class="language-python">import uuid
uuid.uuid1()
uuid.uuid3(namespace, name)
uuid.uuid4()
uuid.uuid5(namespace, name)
</code></pre>
<h2 id="参考">参考</h2>
<blockquote>
<p>http://zh.wikipedia.org/zh-hans/%E9%80%9A%E7%94%A8%E5%94%AF%E4%B8%80%E8%AF%86%E5%88%AB%E7%A0%81<br>
https://docs.python.org/2/library/uuid.html</p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Apache Rewrite]]></title>
        <id>https://me.iblogc.com/post/Apache-Rewrite/</id>
        <link href="https://me.iblogc.com/post/Apache-Rewrite/">
        </link>
        <updated>2015-05-18T13:46:34.000Z</updated>
        <summary type="html"><![CDATA[<p>本文是对今天使用Apache的Rewrite技术做一个简单的记录。</p>
<blockquote>
<p>Apache的rewrite模块，提供了一个基于规则的重写(rewrite,也许译为重构更为合适)引擎，来实时重写发送到Apache的请求URL。因功能极其强大，被称为URL重写的“瑞士军刀”。</p>
</blockquote>
<p>这个模块使用一个基于正则表达式解析器开发的重写引擎，根据web管理员定义的规则来实时(on the fly)重写请求URL。它支持任意数目的重写规则，以及附加到一条规则上的任意数目的规则条件，从而提供了一套非常灵活和功能强大的URL处理机制。 URL处理操作的实施与否，依赖于各种各样的条件检查，如检查服务器变量、环境变量、HTTP头字段、时间戳的值，甚至外部数据库的检索结果。这个模块可 以在服务器范围内(http.conf)、目录范围内(.htaccess)或请求串(query-string)的一部分处理有关的URL。重写的结果 URL，可以指向一个站内的处理程序、指向站外的重定向或者一个站内的代理。与灵活和功能强大相随的是设置的复杂。</p>
]]></summary>
        <content type="html"><![CDATA[<p>本文是对今天使用Apache的Rewrite技术做一个简单的记录。</p>
<blockquote>
<p>Apache的rewrite模块，提供了一个基于规则的重写(rewrite,也许译为重构更为合适)引擎，来实时重写发送到Apache的请求URL。因功能极其强大，被称为URL重写的“瑞士军刀”。</p>
</blockquote>
<p>这个模块使用一个基于正则表达式解析器开发的重写引擎，根据web管理员定义的规则来实时(on the fly)重写请求URL。它支持任意数目的重写规则，以及附加到一条规则上的任意数目的规则条件，从而提供了一套非常灵活和功能强大的URL处理机制。 URL处理操作的实施与否，依赖于各种各样的条件检查，如检查服务器变量、环境变量、HTTP头字段、时间戳的值，甚至外部数据库的检索结果。这个模块可 以在服务器范围内(http.conf)、目录范围内(.htaccess)或请求串(query-string)的一部分处理有关的URL。重写的结果 URL，可以指向一个站内的处理程序、指向站外的重定向或者一个站内的代理。与灵活和功能强大相随的是设置的复杂。</p>
<!--more-->
<h2 id="更新历史">更新历史</h2>
<p>2015年05月18日 - 初稿</p>
<h2 id="开启模块">开启模块</h2>
<p>在<code>http.conf</code>中找到</p>
<pre><code># LoadModule rewrite_module modules/mod_rewrite.so
</code></pre>
<p>取消注释</p>
<h2 id="定义规则">定义规则</h2>
<p>在<code>http.conf</code>中加入下列代码（如果启用了<code>httpd-vhosts.conf</code>，请在<code>httpd-vhosts.conf</code>里做配置）</p>
<pre><code>&lt;IfModule rewrite_module&gt;
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^www.a.com [NC]
    RewriteRule ^/(.*) http://www.b.com/$1 [R=301,l]
&lt;IfModule&gt;
</code></pre>
<p><code>RewriteCond</code>义重写发生的条件，在一条RewriteRule指令前面可能会有一条或多条RewriteCond指令，只有当自身的模板(pattern)匹配成功且这些条件也满足时规则才被应用于当前URL处理，上面代码的<br>
<code>NC</code>：不区分大小写<br>
<code>RewriteRule</code>满足<code>^/(.*)</code>此规则的所有URL都重定向到<code>http://www.b.com/$1</code>，<code>$1</code>使用前面<code>(.*)</code>匹配后的字符填充</p>
<p>所以前面的规则就是的最终效果是访问<code>www.a.com</code>的所以页面都会被重定向到<code>www.b.com</code>相应路径下的页面</p>
<h2 id="参考">参考</h2>
<blockquote>
<p><a href="http://blog.chinaunix.net/uid-20639775-id-154471.html">http://blog.chinaunix.net/uid-20639775-id-154471.html</a><br>
<a href="http://man.lupaworld.com/content/manage/Apache2.2_chinese_manual/mod/mod_rewrite.html">http://man.lupaworld.com/content/manage/Apache2.2_chinese_manual/mod/mod_rewrite.html</a><br>
<a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html">http://httpd.apache.org/docs/current/mod/mod_rewrite.html</a></p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[在 Android Chrome 以 app 形式打开网站]]></title>
        <id>https://me.iblogc.com/post/chrome-add-to-homescreen/</id>
        <link href="https://me.iblogc.com/post/chrome-add-to-homescreen/">
        </link>
        <updated>2015-05-11T02:34:14.000Z</updated>
        <summary type="html"><![CDATA[<p>前面一篇文章我讲到了在UC网站可以以app形式打开，其实在Android Chrome浏览器上也支持此功能。</p>
]]></summary>
        <content type="html"><![CDATA[<p>前面一篇文章我讲到了在UC网站可以以app形式打开，其实在Android Chrome浏览器上也支持此功能。</p>
<!--more-->
<h2 id="添加配置文件">添加配置文件</h2>
<p>在网站根目录添加<code>manifest.json</code>，并进行相应配置</p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;iblogc&quot;,
  &quot;icons&quot;: [
    {
      &quot;src&quot;: &quot;launcher-icon-0-75x.png&quot;,
      &quot;sizes&quot;: &quot;36x36&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;density&quot;: &quot;0.75&quot;
    },
    {
      &quot;src&quot;: &quot;launcher-icon-1x.png&quot;,
      &quot;sizes&quot;: &quot;48x48&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;density&quot;: &quot;1.0&quot;
    },
    {
      &quot;src&quot;: &quot;launcher-icon-1-5x.png&quot;,
      &quot;sizes&quot;: &quot;72x72&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;density&quot;: &quot;1.5&quot;
    },
    {
      &quot;src&quot;: &quot;launcher-icon-2x.png&quot;,
      &quot;sizes&quot;: &quot;96x96&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;density&quot;: &quot;2.0&quot;
    },
    {
      &quot;src&quot;: &quot;launcher-icon-3x.png&quot;,
      &quot;sizes&quot;: &quot;144x144&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;density&quot;: &quot;3.0&quot;
    },
    {
      &quot;src&quot;: &quot;launcher-icon-4x.png&quot;,
      &quot;sizes&quot;: &quot;192x192&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;density&quot;: &quot;4.0&quot;
    }
  ],
  &quot;start_url&quot;: &quot;index.html&quot;,
  &quot;display&quot;: &quot;standalone&quot;,
  &quot;orientation&quot;: &quot;portrait&quot;
}

</code></pre>
<h2 id="在网站公用头部引入配置文件">在网站公用头部引入配置文件</h2>
<pre><code>&lt;link rel=&quot;manifest&quot; href=&quot;manifest.json&quot;&gt;
</code></pre>
<h2 id="查看效果">查看效果</h2>
<p>在Android使用Chrome打开网站，点击memu，选择“添加到主屏幕”选项，点击就可以添加到主屏幕了，步骤及显示效果截图如下：<br>
<img src="https://me.iblogc.com/post-images/chrome-add-to-homescreen-01.png" alt="chrome-add-to-homescreen-01" loading="lazy"><br>
<img src="https://me.iblogc.com/post-images/chrome-add-to-homescreen-02.png" alt="chrome-add-to-homescreen-02" loading="lazy"><br>
PS:地址栏是不是不见了,看着像app而不是网页<br>
<img src="https://me.iblogc.com/post-images/chrome-add-to-homescreen-03.png" alt="chrome-add-to-homescreen-03" loading="lazy"><br>
<img src="https://me.iblogc.com/post-images/chrome-add-to-homescreen-04.png" alt="chrome-add-to-homescreen-04" loading="lazy"></p>
<h2 id="扩展">扩展</h2>
<p>ios的safari也有此功能，因手头无ios设备测试不了，所以内容不写了，大家可以参考此文章<a href="http://www.prower.cn/technic/2314">http://www.prower.cn/technic/2314</a></p>
<h2 id="参考资料">参考资料</h2>
<blockquote>
<p><a href="https://developer.chrome.com/multidevice/android/installtohomescreen">https://developer.chrome.com/multidevice/android/installtohomescreen</a></p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[UC 和 QQ 浏览器私有 META]]></title>
        <id>https://me.iblogc.com/post/UC和QQ浏览器私有META/</id>
        <link href="https://me.iblogc.com/post/UC和QQ浏览器私有META/">
        </link>
        <updated>2015-05-05T16:43:08.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="什么是meta">什么是META</h2>
<blockquote>
<p>META是HTML语言中的一个标签，也称作元标记。<META> 元素可提供有关页面的元信息（meta-information），比如针对搜索引擎和更新频度的描述和关键词。<meta> 标签位于文档的头部，不包含任何内容，<meta> 标签的属性定义了与文档相关联的名称/值对。</p>
</blockquote>
]]></summary>
        <content type="html"><![CDATA[<h2 id="什么是meta">什么是META</h2>
<blockquote>
<p>META是HTML语言中的一个标签，也称作元标记。<META> 元素可提供有关页面的元信息（meta-information），比如针对搜索引擎和更新频度的描述和关键词。<meta> 标签位于文档的头部，不包含任何内容，<meta> 标签的属性定义了与文档相关联的名称/值对。</p>
</blockquote>
<!--more-->
<h2 id="uc的私有meta">UC的私有META</h2>
<h3 id="屏幕方向">屏幕方向</h3>
<p>强制横屏/强制竖屏</p>
<pre><code>&lt;meta name=&quot;screen-orientation&quot; content=&quot;landscape/portrait&quot;&gt;
</code></pre>
<h3 id="全屏">全屏</h3>
<pre><code>&lt;meta name=&quot;full-screen&quot; content=&quot;yes&quot;&gt;
</code></pre>
<h3 id="xhtml适应屏幕排版">xhtml适应屏幕排版</h3>
<p>默认值为uc-fitscreen=no，即不启用此功能，此时浏览器的缩放行为与标准一致。当设置为uc-fitscreen=yes，则当进行缩放操作时，仅放大图片和文字等页面元素，但不放大屏幕宽度，从而避免了左右滚动条的产生。</p>
<pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;uc-fitscreen=yes&quot;/&gt;

</code></pre>
<h3 id="排版模式">排版模式</h3>
<p>Uc浏览器提供两种排版模式，分别是适屏模式及标准模式，其中适屏模式简化了一些页面的处理，使得页面内容更适合进行页面阅读、节省流量及响应更快，而标准模式则能按照标准规范对页面进行排版及渲染。通过新定义的标签及js api接口，可以让网页设计者执行决定采用何种排版方式向用户展现页面。</p>
<pre><code>&lt;meta name=&quot;layoutmode&quot; content=&quot;fitscreen/standard&quot; /&gt;
</code></pre>
<h3 id="夜间模式">夜间模式</h3>
<p>允许进入夜间模式/禁止进入夜间模式</p>
<pre><code>&lt;meta name=&quot;nightmode&quot; content=&quot;enable/disable&quot;/&gt;
</code></pre>
<h3 id="强制显示图片不受浏览器无图设置影响">强制显示图片，不受浏览器无图设置影响</h3>
<pre><code>&lt;meta name=&quot;imagemode&quot; content=&quot;force&quot;/&gt;
</code></pre>
<h3 id="应用模式">应用模式</h3>
<p>默认将全屏，禁止长按菜单，禁止手势，标准排版</p>
<pre><code>&lt;meta name=&quot;browsermode&quot; content=&quot;application&quot;/&gt;
</code></pre>
<h2 id="qq浏览器的私有meta">QQ浏览器的私有META</h2>
<h3 id="屏幕方向-2">屏幕方向</h3>
<p>强制横屏/强制竖屏/自动（默认）</p>
<pre><code>&lt;meta name=&quot;x5-orientation&quot; content=&quot;landscape/portrait/auto&quot;/&gt;

</code></pre>
<h3 id="全屏-2">全屏</h3>
<p>强制全屏/跟随浏览器（默认）</p>
<pre><code class="language-html">&lt;meta name=&quot;x5-fullscreen&quot; content=&quot;true/auto&quot;/&gt;
</code></pre>
<h3 id="页面模式">页面模式</h3>
<p>普通浏览模式（默认）/网页应用模式（定制工具栏，全屏显示）</p>
<pre><code class="language-html">&lt;meta name=&quot;x5-page-mode&quot; content=&quot;default/app&quot;/&gt;
</code></pre>
<h2 id="参考资料">参考资料</h2>
<blockquote>
<p>http://www.uc.cn/business/developer/<br>
http://open.mb.qq.com/doc?id=1201#_1</p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Ubuntu 安装 Android Studio 出现错 unable to run mksdcard sdk tool]]></title>
        <id>https://me.iblogc.com/post/ubuntu安装android-studio出现错unable-to-run-mksdcard-sdk-tool/</id>
        <link href="https://me.iblogc.com/post/ubuntu安装android-studio出现错unable-to-run-mksdcard-sdk-tool/">
        </link>
        <updated>2015-03-28T09:54:03.000Z</updated>
        <content type="html"><![CDATA[<p>错误信息：<br>
<code>unable to run mksdcard sdk tool</code><br>
原因：缺少库文件<br>
解决方法：<br>
<code>sudo apt-get install lib32z1 lib32ncurses5 lib32bz2-1.0 lib32stdc++6</code></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ubuntu 安装 JDK8]]></title>
        <id>https://me.iblogc.com/post/ubuntu安装JDK8/</id>
        <link href="https://me.iblogc.com/post/ubuntu安装JDK8/">
        </link>
        <updated>2015-03-28T09:29:42.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="下载jdk8">下载JDK8</h2>
<p>到<a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html">oracle</a>网站下载JDK8</p>
]]></summary>
        <content type="html"><![CDATA[<h2 id="下载jdk8">下载JDK8</h2>
<p>到<a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html">oracle</a>网站下载JDK8</p>
<!--more-->
<h2 id="解压安装">解压安装</h2>
<pre><code>sudo tar xzvf jdk-8u40-linux-x64.tar.gz
mkdir -p /usr/lib/jvm
sudo mv  /usr/lib/jvm jdk1.8.0_40 /usr/lib/jvm
cd /usr/lib/jvm
sudo ln -s jdk1.8.0_40 java-8
</code></pre>
<h2 id="配置环境变量">配置环境变量</h2>
<p>添加PATH,CLASSPATH,JAVA_HOME环境变量<br>
<code>gedit ~/.bashrc</code><br>
在打开的窗口里添加以下内容</p>
<pre><code>export JAVA_HOME=/usr/lib/jvm/java-8
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
</code></pre>
<p>保存退出，执行命令使配置生效<br>
<code>source ~/.bashrc</code></p>
<h2 id="配置默认jdk版本">配置默认JDK版本</h2>
<p>在有的系统中会预装OpenJDK，系统默认使用的是这个，而不是刚才装的。所以这一步是通知系统使用Oracle的JDK，非OpenJDK。</p>
<pre><code>sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-8/bin/java 300
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-8/bin/javac 300
sudo update-alternatives --config java
sudo update-alternatives --config javac
</code></pre>
<h2 id="验证是否成功">验证是否成功</h2>
<p><code>java -version</code></p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ubuntu 下运行 shadowsocks 客户端]]></title>
        <id>https://me.iblogc.com/post/ubuntu运行shadowsocks客户端/</id>
        <link href="https://me.iblogc.com/post/ubuntu运行shadowsocks客户端/">
        </link>
        <updated>2015-03-28T08:54:44.000Z</updated>
        <content type="html"><![CDATA[<p>Shadowsocks 是一个安全的socks5代理,用于保护网络流量,是一个开源项目,<a href="https://github.com/shadowsocks/shadowsocks">项目地址</a>。<!--more--></p>
<h2 id="下载">下载</h2>
<pre><code>sudo apt-get install python-pip python-m2crypto
sudo pip install shadowsocks
</code></pre>
<h2 id="配置">配置</h2>
<p><code>sudo gedit /etc/shadowsocks/config.json</code></p>
<pre><code>{
    &quot;server&quot;:&quot;remote-shadowsocks-server-ip-addr&quot;,
    &quot;server_port&quot;:8883,
    &quot;local_address&quot;:&quot;127.0.0.1&quot;,
    &quot;local_port&quot;:1080,
    &quot;password&quot;:&quot;abcdef&quot;,
    &quot;timeout&quot;:300,
    &quot;method&quot;:&quot;aes-256-cfb&quot;,
    &quot;fast_open&quot;:false,
    &quot;workers&quot;:1
}
</code></pre>
<blockquote>
<p>请根据实际情况配置</p>
</blockquote>
<h2 id="启动客户端">启动客户端</h2>
<p><code>sslocal -c /etc/shadowsocks.json</code></p>
<h2 id="浏览器扩展">浏览器扩展</h2>
<p>Firefox可使用FoxyProxy Standard<br>
Chrome可使用Proxy SwitchOmega<br>
配置请自行Google/百度</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[ubuntu 更新 NV 显卡驱动]]></title>
        <id>https://me.iblogc.com/post/ubuntu更新NV显卡驱动/</id>
        <link href="https://me.iblogc.com/post/ubuntu更新NV显卡驱动/">
        </link>
        <updated>2015-03-27T12:26:27.000Z</updated>
        <content type="html"><![CDATA[<p>昨天在笔记本上新安装了ubuntu14.04LTS，顺便更新了下NV驱动，这里做下记录。<!--more--></p>
<h2 id="下载驱动">下载驱动</h2>
<p>首页在<a href="http://www.geforce.cn/drivers">http://www.geforce.cn/drivers</a>选择相应的显卡型号下载对应的驱动，下载完成后重命令为NVIDIA.run</p>
<h2 id="关闭x-server">关闭X server</h2>
<p>输入<code>sudo /etc/init.d/gdm stop</code>或<code>sudo /etc/init.d/lightdm stop</code>停止X server，这时桌面会消失，按Ctrl+Alt+F1进入文本模式</p>
<h3 id="安装驱动">安装驱动</h3>
<p>进入驱动所在文件夹，执行<code>sudo sh NVIDIA.run</code>，安装驱动过程中会有几次对话框需要确认。</p>
<h2 id="启动gdm">启动GDM</h2>
<p><code>sudo /etc/init.d/gdm start</code>或<code>sudo /etc/init.d/lightdm start</code></p>
<h2 id="重启电脑">重启电脑</h2>
<p>sudo reboot</p>
<p>这样NV的驱动就安装好了。</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 ngrok 配置微信公众号本地开发环境（ADSL 屏蔽 80 端口问题）]]></title>
        <id>https://me.iblogc.com/post/使用ngrok配置微信公众号本地开发环境（ADSL屏蔽80端口问题）/</id>
        <link href="https://me.iblogc.com/post/使用ngrok配置微信公众号本地开发环境（ADSL屏蔽80端口问题）/">
        </link>
        <updated>2015-02-04T15:35:40.000Z</updated>
        <content type="html"><![CDATA[<h2 id="更新记录">更新记录</h2>
<p>2016-03-04 更新教程<br>
2015-09-16 添加旧版软件下载<br>
2015-02-04 初稿</p>
<p>鉴于国内大部分ADSL屏蔽80端口，而微信公众号开发只支持80端口，所以在本地开发测试微信公众号就是一个问题了，这里我们可以使用软件ngrok来解决这个问题。<!--more--></p>
<h2 id="配置步骤">配置步骤</h2>
<h3 id="1-注册ngrok账号">1. <a href="https://dashboard.ngrok.com/user/signup">注册ngrok账号</a></h3>
<p>注册成功后拿到授权码<code>auth token</code>，使用ngrok时并不强制用户注册，但注册后会附加更多功能(如自定义二级域名)；</p>
<h3 id="2-下载ngrok解压">2. <a href="https://ngrok.com/download">下载ngrok</a>，解压；</h3>
<h3 id="3-启动">3. 启动</h3>
<h5 id="方式一">方式一:</h5>
<p>让本地的‘http://127.0.0.1:80’ 可以让外网访问</p>
<pre><code class="language-yml">ngrok http 80
</code></pre>
<p>ngrok会随机分配一个二级域名，可直接通过外网可通过<code>http://xxxx.tunnel.mobi</code>来访问本机的<code>http://127.0.0.1:80</code>网站</p>
<h5 id="方式二使用配置文件启动">方式二：使用配置文件启动:</h5>
<p>在<code>ngrok.exe</code>目录下执行命令（不带尖括号），生成配置文件（配置文件会在<code>C:\Users\用户名/.ngrok2/ngrok.yml</code>下「windows」）</p>
<pre><code>ngrok authtoken &lt;you authtoken&gt;
</code></pre>
<p>修改配置文件，可配置多个tunnel（注意，配置文件是yaml格式，冒号后面如果还有内容需要加空格）</p>
<pre><code>authtoken:&lt;you authtoken&gt;
tunnels:
  # 自定义隧道名 
  iblogc:
    #本地服务端口 
    addr: 4000
    # 用于http/https里的身份认证
    #auth: &quot;username:password&quot;
    proto: http
    # 二级域名，如果运行提示重复，换一个就行
    subdomain: iblogc
  django:
    addr: 8000
    auth: &quot;abc:123456&quot;
    proto: http
    subdomain: django
  weixin:
    addr: 80
    proto: http
    subdomain: weixin
</code></pre>
<p>现在执行</p>
<pre><code>ngrok start iblogc
</code></pre>
<p>试试，如果你设置的的二级域名没有被占用的话，那么就会启动成功，否则请更换一个二级域重试。<br>
<code>http://iblogc.ngrok.io</code> <code>https://iblogc.ngrok.io</code> 协议均可以访问。<br>
ngrok-start-iblogc.png)</p>
<p>你也可以同时启动两个tunnel</p>
<pre><code>ngrok start iblogc django weixin
</code></pre>
<p>ngrok-start-iblogc-django-weixin.png)</p>
<p>因为我的django tunnel配置文件里添加了<code>auth</code>配置所以访问<code>http://django.ngrok.io</code>需要输入用户名密码。<br>
ngrok-auth.png)</p>
<p>假设<code>weixin</code>就是我本地跑在80端口的微信项目，现在就可以在微信公众平台「开发者中心」可以使用<code>weixin.ngrok.com</code>进行配置了，所有发向此域名的请求都会转发到你的本地<code>127.0.0.1:80</code>上。</p>
<h3 id="4-查看详细信息如果想查看详细的请求信息可以在浏览器里打开http1270014040查看详细信息">4. 查看详细信息如果想查看详细的请求信息可以在浏览器里打开<code>http://127.0.0.1:4040</code>查看详细信息</h3>
<p>nrok-web-interface.png)</p>
<h3 id="5-参考文档">5. 参考文档</h3>
<p>官方文档：https://ngrok.com/docs</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Git 命令使用指南]]></title>
        <id>https://me.iblogc.com/post/Git命令使用指南/</id>
        <link href="https://me.iblogc.com/post/Git命令使用指南/">
        </link>
        <updated>2015-01-16T14:33:30.000Z</updated>
        <summary type="html"><![CDATA[<p>Git是软件开发人员在开发中常用的一种工具,是开发之利器。</p>
<blockquote>
<p>Git是一个开源的分布式版本控制系统，用以有效、高速的处理从很小到非常大的项目版本管理。</p>
</blockquote>
]]></summary>
        <content type="html"><![CDATA[<p>Git是软件开发人员在开发中常用的一种工具,是开发之利器。</p>
<blockquote>
<p>Git是一个开源的分布式版本控制系统，用以有效、高速的处理从很小到非常大的项目版本管理。</p>
</blockquote>
<!--more-->
<h2 id="工作流图示">工作流图示</h2>
<figure data-type="image" tabindex="1"><img src="https://me.iblogc.com/post-images/git-reset_drbfhd.png" alt="工作流图示" loading="lazy"></figure>
<h2 id="命令">命令</h2>
<h3 id="配置">配置</h3>
<ul>
<li>
<p><code>git config --global user.name 'Your Name'</code> 设置git提交显示的名字</p>
</li>
<li>
<p><code>git config --global user.email your_email@example.com</code> 设置git提交显示的邮箱</p>
</li>
<li>
<p><code>git config --global alias.unstage &quot;reset HEAD&quot;</code> 替换命令 <code>git reset HEAD</code>命令改为 <code>git unstage</code></p>
</li>
<li>
<p><code>ssh-keygen -t rsa -C your_email@example.com</code> 生成SSH Key</p>
</li>
<li>
<p><code>git config --global core.editor emacs</code> 设置文件编辑器</p>
</li>
<li>
<p><code>git config --global merge.tool vimdiff</code> 设置差异分析工具</p>
</li>
<li>
<p><code>git config --list</code> 查看配置信息</p>
</li>
</ul>
<h3 id="简洁版">简洁版</h3>
<p>初始化仓库<br>
<code>git init</code></p>
<p>添加远程仓库<br>
<code>git remote add &lt;自定义名字&gt; &lt;远程仓库url&gt; </code></p>
<p>给某个仓库名再添加另一个远程仓库url（可实现一次提交到两个远程仓库）<br>
<code>git remote set-url --add &lt;自定义名字&gt; &lt;远程仓库url&gt;</code></p>
<p>更新项目<br>
<code>git pull</code></p>
<p>合并分支到当前分支<br>
<code>git merge &lt;分支名&gt;</code></p>
<p>创建标签<br>
<code>git tag &lt;标签名字&gt; &lt;提交id前10位字符&gt;</code> <em>可通过<code>git log</code>获取</em></p>
<p>获取log<br>
<code>git log</code></p>
<p>切换分支<br>
<code>git checkout &lt;分支名&gt;</code></p>
<p>创建分支并切换过去<br>
<code>git checkout -b &lt;分支名&gt;</code></p>
<p>删除分支<br>
<code>git branch -D &lt;分支名&gt;</code></p>
<p>推送<br>
<code>git push origin &lt;分支名/标签名&gt;</code></p>
<p>强制推送更新<br>
<code>git push -f origin &lt;分支名/标签名&gt;</code></p>
<p>推送所有分支<br>
<code>git push origin --all</code></p>
<p>推送所有标签<br>
<code>git push origin --tags</code></p>
<p>撤消本地改动（新文件和提交到缓存区的改动，不受影响）<br>
<code>git checkout -- &lt;目录&gt;&lt;文件名&gt;</code></p>
<p>撤消本地所有提交与改动<br>
<em><strong>假如你想要丢弃你所有的本地改动与提交，可以到服务器上获取最新的版本并将你本地主分支指向到它</strong></em><br>
<code>git fetch origin</code><br>
<code>git reset --hard origin/master</code></p>
<p>其它命令</p>
<ul>
<li><code>gitk </code> 获取当前分支图形个界面
<ul>
<li>参数<code>&lt;分支名&gt;</code>: 获取某分支图形界面</li>
<li>参数<code>=--all</code>: 获取所有分支图形个界面</li>
<li><code>cat &lt;目录&gt;&lt;文件名&gt; </code> 查看文件内容</li>
</ul>
</li>
</ul>
<hr>
<h3 id="详细版">详细版</h3>
<p>初始</p>
<ul>
<li>
<p><code>git init</code> 初始化仓库</p>
</li>
<li>
<p><code>ls</code> 显示目录下文件及文件夹（不包含隐藏文件即名字前带点的）</p>
<ul>
<li>参数<code>-a</code>显示目录下所有文件及文件夹</li>
</ul>
</li>
<li>
<p><code>git clone &lt;url&gt;</code> 克隆项目</p>
</li>
</ul>
<p>提交</p>
<ul>
<li>
<p><code>git add &lt;目录&gt;&lt;文件名&gt;</code> 添加文件到版本库，<em>可以多个文件一起添加，中间用空格隔开</em></p>
</li>
<li>
<p><code>git add *</code> 或 <code>git add .</code> 添加所有文件到版本库</p>
</li>
</ul>
<figure data-type="image" tabindex="2"><img src="http://iblogc.qiniudn.com/iblogcd60500d5-addf-4022-ae4f-c1a57d1f5dd1112.png" alt="status示例图" loading="lazy"></figure>
<ul>
<li>
<p><code>git status</code> 查看项目当前状态，详细信息</p>
<ul>
<li>参数<code>-s</code>: 显示简洁版</li>
</ul>
<blockquote>
<p>绿色表示已经提交的缓存区，红色表示在工作区未提交到缓存区的<br>
A新增  M修改  D删除 U冲突 R重命名？<br>
push会把绿色部分提交，红色部分不提交<br>
已有记录文件做过改动和新文件，需要<code>git add</code></p>
</blockquote>
</li>
<li>
<p><code>git diff </code> 查看整个项目里的文件改动情况（工作区和缓存区比较）</p>
<ul>
<li>参数<code>&lt;目录&gt;&lt;文件名&gt;</code>: 查看单个文件改动情况（工作区和缓存区比较）<br>
-参数<code>&lt;标签名&gt;</code>: 查看自当前标签发布之后项目的改动情况</li>
<li>参数<code>--cached</code>: 查看整个项目里的文件改动情况（缓存区和本地仓库比较）</li>
<li>参数 <code>HEAD</code>: 查看整个项目里的文件改动情况（工作区和本地仓库比较）</li>
<li>参数<code>--stat</code>: 显示摘要，而非完整diff</li>
</ul>
</li>
<li>
<p><code>git commit</code>: 提交到缓存</p>
<ul>
<li>参数<code>-m</code>: 后面空格接提交信息</li>
<li>参数<code>-a</code>: 为所有已有记录文件执行<code>git add</code>（新添加文件还是需要手动<code>git add</code>）</li>
</ul>
</li>
<li>
<p><code>git reset HEAD</code> 取消缓存已缓存的内容</p>
<ul>
<li>参数<code>&lt;目录&gt;&lt;文件名&gt;</code>: 单个文件取消缓存已缓存内容</li>
</ul>
</li>
<li>
<p><code>git rm &lt;目录&gt;&lt;文件名&gt;</code>:  将文件从缓存区和硬盘上移除</p>
<ul>
<li>参数<code>--cached</code>: 删除缓存中的文件，保留硬盘上的文件</li>
</ul>
</li>
<li>
<p><code>git mv</code> 不推荐用</p>
</li>
<li>
<p><code>git log</code> 显示当前分支提交记录</p>
<ul>
<li>参数<code>--author=&lt;authorname&gt;</code>: 只寻找某个特定作者的提交</li>
<li>参数<code>--oneline</code>: 显示简洁版
<ul>
<li>参数<code>--oneline -&lt;数字N&gt;</code>: 显示简洁版，显示最近N次提交的记录</li>
</ul>
</li>
<li>参数<code>--graph</code>: 显示拓扑图（查看历史中什么时候出现了分支、合并）</li>
<li>参数<code>--grep=&lt;关键字&gt;</code>: 根据提交注释关键字过滤提交记录</li>
</ul>
<blockquote>
<p>Git 会对所有的 --grep 和 --author 参数作逻辑或。 如果你用 --grep 和 --author 时，想看的是某人写作的并且有某个特殊的注释内容的提交记录， 你需要加上 --all-match 选项。 在这些例子中，我会用上 --format 选项，这样我们就可以看到每个提交的作者是谁了。详细参考：<a href="http://gitref.org/zh/inspect/">Git参考手册:检查与比较</a></p>
</blockquote>
<ul>
<li>参数<code>&lt;分支名&gt;</code>:显示指定分支“可及”的提交记录</li>
<li>参数<code>&lt;分支名1&gt; ^&lt;分支名1&gt;</code>: 查看在分支1不在分支2中的提交记录</li>
</ul>
<blockquote>
<p>分支可以是本地的也可以是远端的</p>
</blockquote>
<ul>
<li>参数<code>--decorate</code>: 显示带tag的记录</li>
<li>参数<code>-p</code>: 显示每个提交引入的补丁</li>
<li>参数<code>--stat</code>: 显示每个提交引入的差值统计</li>
<li>其它参数 <code>--since</code> <code>--before</code> <code>--until</code> <code>--after</code></li>
</ul>
<blockquote>
<p>git log --since --before 根据日期过滤提交记录<br>
如果你要指定一个你感兴趣的日期范围以过滤你的提交，可以执行几个选项 —— 我用 --since 和 --before，但是你也可以用 --until 和 --after。 例如，如果我要看 Git 项目中三周前且在四月十八日之后的所有提交，我可以执行这个（我还用了 --no-merges 选项以隐藏合并提交）<a href="http://gitref.org/zh/inspect/">Git参考手册:检查与比较</a>：</p>
</blockquote>
<pre><code>$ git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges
5469e2d Git 1.7.1-rc2
d43427d Documentation/remote-helpers: Fix typos and improve language
272a36b Fixup: Second argument may be any arbitrary string
b6c8d2d Documentation/remote-helpers: Add invocation section
5ce4f4e Documentation/urls: Rewrite to accomodate transport::address
00b84e9 Documentation/remote-helpers: Rewrite description
03aa87e Documentation: Describe other situations where -z affects git diff
77bc694 rebase-interactive: silence warning when no commits rewritten
636db2c t3301: add tests to use --format=&quot;%N&quot;
</code></pre>
</li>
</ul>
<p>分支</p>
<ul>
<li>
<p><code>git branch</code>列出当前项目的可用分支，并显示当前工作目录当前分支</p>
</li>
<li>
<p>参数<code>&lt;分支名&gt;</code>: 创建分支</p>
</li>
<li>
<p><code>git checkout &lt;分支名&gt;</code> 切换到对应分支</p>
<ul>
<li>参数<code>-b</code> 创建分支并立即切换到新分支</li>
</ul>
</li>
<li>
<p><code>git merge &lt;分支名&gt;</code> 合并指定分支到当前分支</p>
</li>
</ul>
<p>标签</p>
<ul>
<li>
<p><code>git tag</code> 显示当前项目的标签</p>
<ul>
<li>参数<code>&lt;标签名&gt;</code> 给某个历史记录打标签</li>
<li>参数<code>-a</code>: 添加注解</li>
<li>参数<code>&lt;SHA&gt;</code>: 提交id前n位字符，可通过<code>git log</code>获取，n位基于SHA唯一就行（建议5~7位）</li>
</ul>
</li>
</ul>
<p>远程</p>
<ul>
<li>
<p><code>git remote</code> 列出远端别名<br>
-参数<code>-v</code>: 列出远端别名及链接</p>
<blockquote>
<p>一般一个别名会看到两个相同的链接（fetch和push）分别是获取和推送的链接<br>
-<code>add &lt;仓库别名&gt; &lt;仓库链接&gt;</code>: 为项目添加一个新的远端仓库</p>
</blockquote>
<ul>
<li><code>rm &lt;仓库别名&gt;</code>: 为项目删除一个远端仓库</li>
</ul>
<blockquote>
<p>只是本地删掉和远端仓库的链接，不会对远端仓库造成影响</p>
</blockquote>
</li>
<li>
<p><code>git fetch</code> 从远端仓库下载最新的分支与数据</p>
</li>
<li>
<p><code>git pull</code> 从远端仓库下载最新数据，并尝试合并到当前分支</p>
<ul>
<li>参数<code>&lt;仓库别名&gt;</code>: 从哪个仓库拉取更新，默认为origin</li>
</ul>
</li>
</ul>
<blockquote>
<p><code>git pull</code>实际是先<code>git fetch</code>后<code>git merge</code></p>
</blockquote>
<ul>
<li><code>git push</code> 推送更新
<ul>
<li>参数<code>&lt;仓库别名&gt; &lt;分支名&gt;</code>: 推送新分支与数据到某个远端仓库</li>
<li>参数<code>&lt;仓库别名&gt; --all</code>: 推送所有分支</li>
<li>参数<code>&lt;仓库别名&gt; --tagsl</code>: 推送所有标签</li>
</ul>
</li>
</ul>
<h2 id="参考资料">参考资料</h2>
<blockquote>
<p><a href="http://gitref.org/zh">Git 参考手册</a><br>
<a href="http://www.bootcss.com/p/git-guide/">git - 简易指南</a></p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[pip 常用命令]]></title>
        <id>https://me.iblogc.com/post/pip常用命令/</id>
        <link href="https://me.iblogc.com/post/pip常用命令/">
        </link>
        <updated>2015-01-01T15:38:56.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="列出已安装的包">列出已安装的包</h2>
<p><code>pip freeze</code> or <code>pip list</code></p>
]]></summary>
        <content type="html"><![CDATA[<h2 id="列出已安装的包">列出已安装的包</h2>
<p><code>pip freeze</code> or <code>pip list</code></p>
<!--more-->
<h3 id="导出requirementstxt">导出requirements.txt</h3>
<p><code>pip freeze &gt; &lt;目录&gt;/requirements.txt</code></p>
<h2 id="安装包">安装包</h2>
<h3 id="在线安装">在线安装</h3>
<p><code>pip install &lt;包名&gt;</code> 或 <code>pip install -r requirements.txt</code></p>
<pre><code># 安装1.9版本的django
pip install django==1.9
# 安装版本号大于1.9的django，注意有引号
pip install &quot;django&gt;1.9&quot;
pip install &quot;django&gt;=1.9&quot;
pip install &quot;django&lt;1.9&quot;
pip install &quot;django&lt;=1.9&quot;
pip install &quot;django&gt;&lt;1.9&quot;
</code></pre>
<p>requirements.txt内容格式为：</p>
<pre><code>APScheduler==2.1.2
Django==1.5.4
MySQL-Connector-Python==2.0.1
MySQL-python==1.2.3
PIL==1.1.7
South==1.0.2
django-grappelli==2.6.3
django-pagination==1.0.7
</code></pre>
<h3 id="安装本地安装包">安装本地安装包</h3>
<p><code>pip install &lt;目录&gt;/&lt;文件名&gt;</code> 或 <code>pip install --use-wheel --no-index --find-links=wheelhouse/ &lt;包名&gt;</code></p>
<p><em>&lt;包名&gt;前有空格</em></p>
<p>可简写为</p>
<p><code>pip install --no-index -f=&lt;目录&gt;/ &lt;包名&gt;</code></p>
<h3 id="卸载包">卸载包</h3>
<p><code>pip uninstall &lt;包名&gt;</code> 或 <code>pip uninstall -r requirements.txt</code></p>
<h3 id="升级包">升级包</h3>
<p><code>pip install -U &lt;包名&gt;</code></p>
<h3 id="升级pip">升级pip</h3>
<p><code>pip install -U pip</code></p>
<h2 id="显示包所在的目录">显示包所在的目录</h2>
<p><code>pip show -f &lt;包名&gt;</code></p>
<h2 id="搜索包">搜索包</h2>
<p><code>pip search &lt;搜索关键字&gt;</code></p>
<h2 id="查询可升级的包">查询可升级的包</h2>
<p><code>pip list -o</code></p>
<h2 id="下载包而不安装">下载包而不安装</h2>
<p><code>pip install &lt;包名&gt; -d  &lt;目录&gt; </code> 或 <code>pip install -d &lt;目录&gt; -r requirements.txt</code></p>
<h2 id="打包">打包</h2>
<p><code>pip wheel &lt;包名&gt;</code></p>
<h2 id="更换国内pypi镜像">更换国内pypi镜像</h2>
<h3 id="国内pypi镜像">国内pypi镜像</h3>
<ul>
<li>阿里云 <a href="https://mirrors.aliyun.com/pypi/simple/">https://mirrors.aliyun.com/pypi/simple/</a></li>
<li>豆瓣：<a href="https://pypi.douban.com/simple">https://pypi.douban.com/simple</a></li>
<li>中国科学技术大学：<a href="https://mirrors.ustc.edu.cn/pypi/web/simple/">https://mirrors.ustc.edu.cn/pypi/web/simple/</a></li>
<li>清华大学TUNA：<a href="https://pypi.tuna.tsinghua.edu.cn/simple">https://pypi.tuna.tsinghua.edu.cn/simple</a><br>
<a href="https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/">https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/</a></li>
</ul>
<h3 id="指定单次安装源">指定单次安装源</h3>
<p><code>pip install &lt;包名&gt; -i http://pypi.v2ex.com/simple</code></p>
<h3 id="指定全局安装源">指定全局安装源</h3>
<p>在unix和macos，配置文件为：$HOME/.pip/pip.conf<br>
在windows上，配置文件为：%HOME%\pip\pip.ini</p>
<pre><code>[global]
timeout = 6000
index-url = http://pypi.douban.com/simple
</code></pre>
<hr>
<h2 id="参考资料">参考资料</h2>
<blockquote>
<p><a href="https://pip.pypa.io/en/latest/">pip documentation</a></p>
</blockquote>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 Virtualenv 创建独立的 Python 运行环境]]></title>
        <id>https://me.iblogc.com/post/使用virtualenv创建独立的python运行环境/</id>
        <link href="https://me.iblogc.com/post/使用virtualenv创建独立的python运行环境/">
        </link>
        <updated>2015-01-01T12:39:14.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="准备工作">准备工作</h2>
<ul>
<li>python环境</li>
<li>pip</li>
</ul>
<h2 id="安装">安装</h2>
<pre><code>pip install virtualenv
</code></pre>
<p>或</p>
<pre><code>pip install https://github.com/pypa/virtualenv/tarball/develop
</code></pre>
]]></summary>
        <content type="html"><![CDATA[<h2 id="准备工作">准备工作</h2>
<ul>
<li>python环境</li>
<li>pip</li>
</ul>
<h2 id="安装">安装</h2>
<pre><code>pip install virtualenv
</code></pre>
<p>或</p>
<pre><code>pip install https://github.com/pypa/virtualenv/tarball/develop
</code></pre>
<!--more-->
<h2 id="创建虚拟环境">创建虚拟环境</h2>
<pre><code>virtualenv myVE
</code></pre>
<p>指定python解释器</p>
<pre><code> -p PYTHON_EXE, --python=PYTHON_EXE
</code></pre>
<p><em>创建虚拟环境时默认会自动安装setuptools和pip</em></p>
<p>不安装setuptool</p>
<pre><code>--no--setuptools
</code></pre>
<p>不安装pip</p>
<pre><code>--no--pip
</code></pre>
<p><em>更多Options请参考<a href="https://virtualenv.pypa.io/en/latest/reference.html">官方文档</a></em></p>
<h2 id="启动虚拟环境">启动虚拟环境</h2>
<p>Mac OS</p>
<pre><code>cd myVE
source ./bin/activate
</code></pre>
<p>Windows</p>
<pre><code>cd myVE
scripts\activate
</code></pre>
<p>启动成功后可以在开头显示&quot;(myVE)&quot;，说明已经进入刚刚创建的虚拟环境了</p>
<h2 id="退出">退出</h2>
<pre><code>deactivate
</code></pre>
<h2 id="virtualenvwrapper">virtualenvwrapper</h2>
<h3 id="安装-2">安装</h3>
<blockquote>
<p>Virtaulenvwrapper是virtualenv的扩展包，用于更方便管理虚拟环境，它可以做：</p>
<ol>
<li>将所有虚拟环境整合在一个目录下</li>
<li>管理（新增，删除，复制）虚拟环境</li>
<li>切换虚拟环境</li>
</ol>
</blockquote>
<pre><code>pip install virtualenvwrapper
</code></pre>
<p>Windows下还需额外安装virtualenvwrapper-win</p>
<pre><code>pip install virtualenvwrapper-win
</code></pre>
<p>ubuntu需要将下面这句加入到<code>~/.bashrc</code>里面</p>
<pre><code>if [ -f /usr/local/bin/virtualenvwrapper.sh ]; then
    source /usr/local/bin/virtualenvwrapper.sh
fi
</code></pre>
<p>加入后需要重启才能生效，如果想要立即生效，输入命令</p>
<pre><code>source ~/.bashrc
</code></pre>
<h3 id="常用命令">常用命令</h3>
<p><em>部分命令在windows下无效</em></p>
<ul>
<li><code>workon myEnv</code>: 切换虚拟环境</li>
<li><code>mkvirtualenv</code>: 新建工作环境</li>
<li><code>rmvirtualenv</code>: 删除工作环境</li>
<li><code>cdproject</code>: 切换到工程目录</li>
<li><code>workon</code>/<code>lsvirtualenv</code>: 列出所有虚拟环境</li>
<li><code>deactivate</code>: 退出虚拟环境</li>
<li><code>cpvirtualenv [source] [dest]</code> 复制一份虚拟环境。</li>
<li><code>cdvirtualenv [subdir]</code> 把当前工作目录设置为所在的环境目录。</li>
<li><code>cdsitepackages [subdir]</code> 把当前工作目录设置为所在环境的sitepackages路径。</li>
<li><code>add2virtualenv [dir] [dir]</code> 把指定的目录加入当前使用的环境的path中，这常使用于在多个project里面同时使用一个较大的库的情况。</li>
<li><code>toggleglobalsitepackages -q</code> 控制当前的环境是否使用全局的sitepackages目录。</li>
</ul>
<hr>
<h2 id="参考资料">参考资料</h2>
<p>https://virtualenv.pypa.io/en/latest/</p>
<p>http://virtualenvwrapper.readthedocs.org/en/latest/</p>
<p>https://github.com/davidmarble/virtualenvwrapper-win</p>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[电影《小黄人》中文预告片]]></title>
        <id>https://me.iblogc.com/post/电影《小黄人》中文预告片/</id>
        <link href="https://me.iblogc.com/post/电影《小黄人》中文预告片/">
        </link>
        <updated>2014-11-09T14:04:05.000Z</updated>
        <content type="html"><![CDATA[<!--more-->
<p>导演: 凯尔·巴尔达 / 皮埃尔·科芬<br>
编剧: 布莱恩·林奇<br>
主演: 迈克尔·基顿 / 桑德拉·布洛克 / 乔恩·哈姆 / 凯蒂·米克松 / 真田广之 / 珍妮弗·桑德斯 / 皮埃尔·科芬 / 克里斯·雷纳德 / Dave Rosenbaum<br>
类型: 喜剧 / 动画 / 家庭<br>
制片国家/地区: 美国<br>
语言: 英语<br>
上映日期: 2015-07-10(美国)<br>
又名: 小黄人大电影<br>
豆瓣链接: <a href="http://movie.douban.com/subject/11624706/">小黄人</a><br>
预告版:</p>
<iframe height=498 width=510 src="http://player.youku.com/embed/XODE4MzY3MDYw" frameborder=0 allowfullscreen></iframe>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[GitHub Pages/GitCafe Pages 绑定自定义域名]]></title>
        <id>https://me.iblogc.com/post/Github-Pages-Gitcafe-Pages绑定自定义域名/</id>
        <link href="https://me.iblogc.com/post/Github-Pages-Gitcafe-Pages绑定自定义域名/">
        </link>
        <updated>2014-11-09T10:32:11.000Z</updated>
        <summary type="html"><![CDATA[<h2 id="更新记录">更新记录</h2>
<p>2015-01-01 更新 GitCafe-Page IP地址<br>
2014-11-09 初稿</p>
<p>虽然GitHub Pages和GitCafe Pages默认为每个用户分配了一个二级域名（GitHub为<code>username.github.io</code>或<code>username.github.com</code>,GitCafe为<code>username.gitcafe.com</code>），但如果你对这个二级域名不满意也可以申请一个自己的域名进行绑定。下面就说说GitHub和GitCafe的绑定过程。</p>
]]></summary>
        <content type="html"><![CDATA[<h2 id="更新记录">更新记录</h2>
<p>2015-01-01 更新 GitCafe-Page IP地址<br>
2014-11-09 初稿</p>
<p>虽然GitHub Pages和GitCafe Pages默认为每个用户分配了一个二级域名（GitHub为<code>username.github.io</code>或<code>username.github.com</code>,GitCafe为<code>username.gitcafe.com</code>），但如果你对这个二级域名不满意也可以申请一个自己的域名进行绑定。下面就说说GitHub和GitCafe的绑定过程。</p>
<!--more-->
<h2 id="准备工作">准备工作</h2>
<ul>
<li>域名（例：iblogc.com）</li>
<li>一个GitHub Pages/GitCafe Pages</li>
</ul>
<hr>
<h2 id="github">GitHub</h2>
<ul>
<li>在repo目录下创建一个名为<code>CNAME</code>的文件（无后缀）</li>
<li>打开CNAME，在里面写入你要绑定的域名</li>
<li><s>1)如果你绑定的是二级域名，请在域名管理里添加一条CNAME记录，指向username.github.io或username.github.com</s></li>
<li><s>2)如果你绑定的是顶级域名，请在域名管理里添加一条A记录，指向103.245.222.133</s></li>
<li>请在域名管理里添加一条CNAME记录，指向username.github.io</li>
<li>等待生效</li>
</ul>
<hr>
<h2 id="gitcafe">GitCafe</h2>
<ul>
<li>打开你自己的gitcafe pages项目，</li>
<li>进入 项目管理&gt;&gt;自定义域名，在这里添加你要绑定的域名就可以，比如我配置了顶级域名iblogc.com（当然也可以设置二级域名）</li>
<li>QQ截图20141109181543.png)</li>
<li>在域名管理里添加一条CNAME记录，记录值为gitcafe.io，如果您的域名注册商不提供CNAME记录选项，请将A记录值修改为 207.226.141.135(IP地址截止2015-01-01有效，如失效，请以<a href="https://gitcafe.com/GitCafe/Help/wiki/Pages-%E7%9B%B8%E5%85%B3%E5%B8%AE%E5%8A%A9#wiki">官方说明</a>为准)。</li>
<li>等待生效</li>
</ul>
]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[Hello World]]></title>
        <id>https://me.iblogc.com/post/hello-world/</id>
        <link href="https://me.iblogc.com/post/hello-world/">
        </link>
        <updated>2014-09-15T06:04:40.000Z</updated>
        <summary type="html"><![CDATA[<p>Welcome to <a href="http://hexo.io/">Hexo</a>! This is your very first post. Check <a href="http://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="http://hexo.io/docs/troubleshooting.html">trobuleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p>
]]></summary>
        <content type="html"><![CDATA[<p>Welcome to <a href="http://hexo.io/">Hexo</a>! This is your very first post. Check <a href="http://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="http://hexo.io/docs/troubleshooting.html">trobuleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p>
<!-- more -->
<h2 id="quick-start">Quick Start</h2>
<h3 id="create-a-new-post">Create a new post</h3>
<pre><code class="language-bash">$ hexo new &quot;My New Post&quot;
</code></pre>
<p>More info: <a href="http://hexo.io/docs/writing.html">Writing</a></p>
<h3 id="run-server">Run server</h3>
<pre><code class="language-bash">$ hexo server
</code></pre>
<p>More info: <a href="http://hexo.io/docs/server.html">Server</a></p>
<h3 id="generate-static-files">Generate static files</h3>
<pre><code class="language-bash">$ hexo generate
</code></pre>
<p>More info: <a href="http://hexo.io/docs/generating.html">Generating</a></p>
<h3 id="deploy-to-remote-sites">Deploy to remote sites</h3>
<pre><code class="language-bash">$ hexo deploy
</code></pre>
<p>More info: <a href="http://hexo.io/docs/deployment.html">Deployment</a></p>
]]></content>
    </entry>
</feed>