-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.xml
565 lines (329 loc) · 241 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>iblogc's blog</title>
<icon>https://www.gravatar.com/avatar/6b6caae0eb040668a27752c042d71e3a</icon>
<subtitle>Just do it.</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://me.iblogc.com/"/>
<updated>2020-03-24T06:15:27.000Z</updated>
<id>http://me.iblogc.com/</id>
<author>
<name>沙丁鱼🇨🇳</name>
<email>[email protected]</email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>代码托管平台码云(Gitee)到Gitea迁移记</title>
<link href="http://me.iblogc.com/2020/03/01/Gitee2Gitea/"/>
<id>http://me.iblogc.com/2020/03/01/Gitee2Gitea/</id>
<published>2020-03-01T11:42:34.000Z</published>
<updated>2020-03-24T06:15:27.000Z</updated>
<content type="html"><![CDATA[<p>团队的代码托管管理平台之前一直用Gitee的企业版本,但除了代码pull/push操作外,基本不用平台上其它功能,除了要新建一个仓库要打开下网页版,其它时间基本不会访问网页版本,所以经过半天的调研,从GitLab/Gogs/Gitea中选择了Gitea,把迁移过程记录如下。</p><span id="more"></span><h3 id="安装Gitea"><a href="#安装Gitea" class="headerlink" title="安装Gitea"></a>安装Gitea</h3><p>因为服务器上刚好装有docker,按照<a href="https://docs.gitea.io/zh-cn/install-with-docker/">官方文档</a>选择了最简单的docker安装。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">docker pull gitea/gitea:latest</span><br><span class="line">sudo mkdir -p /data/gitea</span><br><span class="line">docker run -d --name=gitea -p 10022:22 -p 10080:3000 -v /data/gitea:/data gitea/gitea:latest</span><br><span class="line">// 重启gitea</span><br><span class="line">docker restart gitea</span><br></pre></td></tr></table></figure><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"><a href="#配置Nginx" class="headerlink" title="配置Nginx"></a>配置Nginx</h3><p>在服务器Nginx上配置反向代理</p><p>vi /etc/nginx/conf.d/gitea.conf</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">upstream gitea {</span><br><span class="line"> server 127.0.0.1:10080<span class="comment">;</span></span><br><span class="line"> keepalive 2000<span class="comment">;</span></span><br><span class="line">}</span><br><span class="line">server {</span><br><span class="line"> listen 80<span class="comment">;</span></span><br><span class="line"> server_name git.i.example.com<span class="comment">;</span></span><br><span class="line"> client_max_body_size 1024M<span class="comment">;</span></span><br><span class="line"></span><br><span class="line"> location / {</span><br><span class="line"> proxy_pass http://gitea/<span class="comment">;</span></span><br><span class="line"> proxy_set_header Host $host:$server_port<span class="comment">;</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>重新加载配置</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nginx -s reload</span><br></pre></td></tr></table></figure><h3 id="域名解析"><a href="#域名解析" class="headerlink" title="域名解析"></a>域名解析</h3><p>git.i.example.com解析到当前服务器ip,并把服务器防火墙入方向的10022 tcp端口打开,以便使用ssh方式clone仓库时使用。</p><h3 id="Gitea初始化"><a href="#Gitea初始化" class="headerlink" title="Gitea初始化"></a>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="仓库迁移"><a href="#仓库迁移" class="headerlink" title="仓库迁移"></a>仓库迁移</h3><p>因为我迁移的是团队项目,所以先通过Gitea提供的API把所有仓库以镜像方式(镜像方式同步过来仓库对成员为只读,并且可以设置间隔时间,默认8小时,定时从原始地址Gitee同步最新代码)同步过来<strong>[操作1]</strong>,然后为每个项目配置好协作者/团队/权限等设置,在这期间,团队成员还是往Gitee上提交代码,待全部设置完成后取消告知团队成员不要往Gitee提交代码,并调用Giea api把所有仓库从Gitee上同步一下最新代码<strong>[操作2]</strong>,然后每个仓库从镜像仓库转为普通仓库,并让团队的所有在自己仓库根目录执行修改本地仓库Git远程仓库地址替换操作<strong>[操作3]</strong></p><p><strong>[操作1]</strong>:登录Gitea后,界面右上角有一个加号,点开了后有一个迁移外部仓库的功能,只要填入外部仓库URL,授权验证信息等信息就可以一键把外部仓库的所有代码(包括所有branch和commit)迁移到Gitea,如果要迁移的仓库比较多,可以使用Gitea提供的Api来操作。对应此迁移操作的api是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">POST /repos/migrate?access_token=<your gitea admin access token></span><br><span class="line"></span><br><span class="line">Request body</span><br><span class="line">{</span><br><span class="line"> description: MigrateRepoForm form for migrating repository</span><br><span class="line"> auth_password: string</span><br><span class="line"> auth_username: string</span><br><span class="line"> clone_addr*: string</span><br><span class="line"> description: string</span><br><span class="line"> issues: boolean</span><br><span class="line"> labels: boolean</span><br><span class="line"> milestones: boolean</span><br><span class="line"> mirror: boolean</span><br><span class="line"> private: boolean</span><br><span class="line"> pull_requests: boolean</span><br><span class="line"> releases: boolean</span><br><span class="line"> repo_name*: string</span><br><span class="line"> uid*: integer($int64)</span><br><span class="line"> wiki: boolean</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong><em>注:</em></strong></p><ol><li><p>access_token 请在有管理员权限的账号的设置>应用中创建;</p></li><li><p>Request body 中的uid即管理后台>账户管理/组织管理中的ID列值;</p></li></ol><p>找了Gitee没找到可以获取账户下所有仓库信息的API,所以只好手写了一个Gitee仓库地址的文件,类似</p><p>vi gitee-url.txt</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">https://gitee.com/example/project_a.git</span><br><span class="line">https://gitee.com/example/project_b.git</span><br></pre></td></tr></table></figure><p>使用shell脚本逐行读取url,并调用Gitea api迁移仓库。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">for line in $(<gitee-url.txt);</span><br><span class="line">do</span><br><span class="line"><span class="meta prompt_">tureture# </span><span class="language-bash">Windows注释下面这行</span></span><br><span class="line"> line=$(echo $line | sed -e 's/\r//g');</span><br><span class="line"> tmp=${line#https://gitee.com/xxx/};</span><br><span class="line"> project_name=${tmp%.git};</span><br><span class="line"> curl -X POST "http://git.i.example.com/api/v1/repos/migrate?access_token=<your gitea admin access token>" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"auth_password\": \"NDY2&F*K!hL75y*z\", \"auth_username\": \"[email protected]\", \"clone_addr\": \"$line\", \"issues\": true, \"labels\": true, \"milestones\": true, \"mirror\": true, \"private\": true, \"pull_requests\": true, \"releases\": true, \"repo_name\": \"$project_name\", \"uid\": 2, \"wiki\": true}";</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p><strong>[操作2]</strong>:从Gitee上同步最新代码</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">for line in $(<gitee-url.txt);</span><br><span class="line">do</span><br><span class="line"> line=$(echo $line | sed -e 's/\r//g');</span><br><span class="line"> tmp=${line#https://gitee.com/xxx/};</span><br><span class="line"> project_name=${tmp%.git};</span><br><span class="line"> curl -X POST "http://git.i.example.com/api/v1/repos/{owner}/$project_name/mirror-sync?access_token=<your gitea admin access token>" -H "accept: application/json"</span><br><span class="line">done</span><br></pre></td></tr></table></figure><p><strong><em>注:</em></strong>owner为项目拥有者用户名/组织名</p><p><strong>[操作3]</strong>:原本地仓库Git远程仓库地址替换</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">// http地址</span><br><span class="line">// 原代码仓库http地址:https://gitee.com/example/project_a.git</span><br><span class="line">// 新代码仓库http地址:http://git.i.example.com/JIANSU/project_a.git</span><br><span class="line">// https://gitee.com/example > http://git.i.example.com/JIANSU</span><br><span class="line">// 本地仓库使用此命令替换,可在包含所有项目的外层文件夹路径下执行批量替换</span><br><span class="line">// Windows删除'.bak'</span><br><span class="line">sed -i '.bak' 's/https:\/\/gitee\.com\/example/http:\/\/git\.i\.example.com\/JIANSU/g' */.git/config</span><br><span class="line"></span><br><span class="line">// ssh地址</span><br><span class="line">// 原代码仓库ssh地址:[email protected]:example/project_a.git</span><br><span class="line">// 新代码仓库地址:ssh://[email protected]:10022/JIANSU/project_a.git</span><br><span class="line">// [email protected]:example > ssh://[email protected]:10022/JIANSU</span><br><span class="line">// 本地仓库使用此命令替换,可在包含所有项目的外层文件夹路径下执行批量替换</span><br><span class="line">// Windows删除'.bak'</span><br><span class="line">sed -i '.bak' 's/git@gitee\.com:example/ssh:\/\/git@git\.i\.example\.com:10022\/JIANSU/g' */.git/config</span><br></pre></td></tr></table></figure><ol><li><p>如果之前是用http地址进行克隆的仓库的话,现在就是在进行pull和push操作时,把账户密码换成Gitea的就可以了;</p></li><li><p>如果以前是用ssh克隆的仓库的话,现在在Gitea的设置>SSH / GPG 密钥里添加一下公钥就可以进行git pull/git push等操作了;</p></li></ol><h3 id="仓库备份"><a href="#仓库备份" class="headerlink" title="仓库备份"></a>仓库备份</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<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python3</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> platform</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">current_dir = os.path.abspath(os.path.dirname(__file__))</span><br><span class="line">access_token = <span class="string">"<your access token>"</span></span><br><span class="line">repos_url = <span class="string">'http://git.i.example.com/api/v1/repos/search?access_token={}&page={}&limit={}'</span></span><br><span class="line">branches_url = <span class="string">'http://git.i.example.com/api/v1/repos/{}/branches?access_token={}'</span></span><br><span class="line">repo_key_url = <span class="string">'http://git.i.example.com/api/v1/repos/{}/{}/keys?access_token={}'</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">repos</span>():</span><br><span class="line"> page = <span class="number">1</span></span><br><span class="line"> limit = <span class="number">50</span></span><br><span class="line"> has_next = <span class="literal">True</span></span><br><span class="line"> <span class="keyword">while</span> has_next:</span><br><span class="line"> r = requests.get(repos_url.<span class="built_in">format</span>(access_token, page, limit))</span><br><span class="line"> <span class="keyword">for</span> repo <span class="keyword">in</span> r.json()[<span class="string">'data'</span>]:</span><br><span class="line"> <span class="keyword">yield</span> repo</span><br><span class="line"> page += <span class="number">1</span></span><br><span class="line"> has_next = <span class="built_in">len</span>(r.json()[<span class="string">'data'</span>]) == limit</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">"""拉取项目所有分支代码到本地"""</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">sync_repo</span>():</span><br><span class="line"> repo_index = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> repo <span class="keyword">in</span> repos():</span><br><span class="line"> repo_index += <span class="number">1</span></span><br><span class="line"> <span class="comment"># 克隆仓库</span></span><br><span class="line"> os.chdir(current_dir)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'克隆第 {} 个仓库 {} '</span>.<span class="built_in">format</span>(repo_index, repo[<span class="string">'name'</span>]))</span><br><span class="line"> os.system(<span class="string">"git clone {}"</span>.<span class="built_in">format</span>(repo[<span class="string">'ssh_url'</span>]))</span><br><span class="line"> os.chdir(os.path.join(current_dir, repo[<span class="string">'name'</span>]))</span><br><span class="line"> <span class="comment"># 更新仓库</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'同步 {} 仓库所有分支'</span>.<span class="built_in">format</span>(repo[<span class="string">'name'</span>]))</span><br><span class="line"> os.system(<span class="string">'git fetch --all'</span>)</span><br><span class="line"> <span class="comment"># if platform.system() == 'Windows':</span></span><br><span class="line"> <span class="comment"># Windows</span></span><br><span class="line"> branches = requests.get(branches_url.<span class="built_in">format</span>(</span><br><span class="line"> repo[<span class="string">'full_name'</span>], access_token)).json()</span><br><span class="line"> <span class="keyword">for</span> branch <span class="keyword">in</span> branches:</span><br><span class="line"> branch_name = branch[<span class="string">'name'</span>]</span><br><span class="line"> os.system(<span class="string">'git branch --track {} origin/{}'</span>.<span class="built_in">format</span>(branch_name, branch_name))</span><br><span class="line"> <span class="comment"># 用reset而不用pull是因为如果分支被强推了pull下来会有合并冲突,用rest就不会有冲突问题</span></span><br><span class="line"> os.system(<span class="string">'git checkout {} && git reset --hard origin/{}'</span>.<span class="built_in">format</span>(branch_name, branch_name))</span><br><span class="line"> <span class="comment"># else:</span></span><br><span class="line"> <span class="comment"># # Linux/macOS</span></span><br><span class="line"> <span class="comment"># # git branch -r | grep -v '\->' | while read remote; do git branch --track ${remote#origin/} $remote; done && git fetch --all && git pull --all</span></span><br><span class="line"> <span class="comment"># # os.system("git branch -r | grep -v '\->' | while read remote; do git branch --track ${remote#origin/} $remote; done && git fetch --all && git pull --all")</span></span><br><span class="line"> <span class="comment"># # # 用reset而不用pull是因为如果分支被强推了pull下来会有合并冲突,用rest就不会有冲突问题</span></span><br><span class="line"> <span class="comment"># os.system("git branch -r | grep -v '\->' | while read remote; do git branch --track ${remote#origin/} $remote; git checkout ${remote#origin/}; git reset --hard $remote; done")</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">"""设置项目部署公钥"""</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">set_pub_key</span>():</span><br><span class="line"> repo_index = <span class="number">0</span></span><br><span class="line"> body = {</span><br><span class="line"> <span class="string">"key"</span>: <span class="string">"ssh-rsa aabbcc"</span>,</span><br><span class="line"> <span class="string">"read_only"</span>: <span class="literal">True</span>,</span><br><span class="line"> <span class="string">"title"</span>: <span class="string">"SandBox"</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> repo <span class="keyword">in</span> repos():</span><br><span class="line"> repo_index += <span class="number">1</span></span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'==={}. {}==='</span>.<span class="built_in">format</span>(repo_index, repo[<span class="string">'name'</span>]))</span><br><span class="line"> r = requests.post(repo_key_url.<span class="built_in">format</span>(</span><br><span class="line"> repo[<span class="string">'owner'</span>][<span class="string">'username'</span>], repo[<span class="string">'name'</span>], access_token), data=body)</span><br><span class="line"> <span class="built_in">print</span>(r.json())</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> sync_repo()</span><br><span class="line"> <span class="comment"># set_pub_key()</span></span><br></pre></td></tr></table></figure></p><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>
<summary type="html">
<p>团队的代码托管管理平台之前一直用Gitee的企业版本,但除了代码pull/push操作外,基本不用平台上其它功能,除了要新建一个仓库要打开下网页版,其它时间基本不会访问网页版本,所以经过半天的调研,从GitLab/Gogs/Gitea中选择了Gitea,把迁移过程记录如下。</p>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Git" scheme="http://me.iblogc.com/tags/git/"/>
<category term="Gitee" scheme="http://me.iblogc.com/tags/gitee/"/>
<category term="GitLab" scheme="http://me.iblogc.com/tags/gitlab/"/>
<category term="Gite" scheme="http://me.iblogc.com/tags/gite/"/>
<category term="Gogs" scheme="http://me.iblogc.com/tags/gogs/"/>
<category term="迁移" scheme="http://me.iblogc.com/tags/%E8%BF%81%E7%A7%BB/"/>
<category term="代码" scheme="http://me.iblogc.com/tags/%E4%BB%A3%E7%A0%81/"/>
<category term="Python" scheme="http://me.iblogc.com/tags/python/"/>
</entry>
<entry>
<title>WebFlux中mongo操作-Transaction</title>
<link href="http://me.iblogc.com/2020/01/17/WebFlux%E4%B8%ADmongo%E6%93%8D%E4%BD%9C-Transaction/"/>
<id>http://me.iblogc.com/2020/01/17/WebFlux中mongo操作-Transaction/</id>
<published>2020-01-17T07:16:46.000Z</published>
<updated>2020-01-17T08:01:36.000Z</updated>
<content type="html"><![CDATA[<p><br><br><span id="more"></span><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping("/test")</span></span><br><span class="line"><span class="keyword">public</span> Mono <span class="title function_">testA</span><span class="params">(<span class="meta">@RequestParam</span> <span class="type">boolean</span> exception)</span> {</span><br><span class="line"> <span class="keyword">return</span> embedService.saveAC(<span class="keyword">new</span> <span class="title class_">ADocument</span>(<span class="string">"张三"</span>), <span class="keyword">new</span> <span class="title class_">CDocument</span>(<span class="string">"李四"</span>), exception);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Mono<Boolean> <span class="title function_">saveAC</span><span class="params">(ADocument aDocument, CDocument cDocument, <span class="type">boolean</span> exception)</span> {</span><br><span class="line"> <span class="keyword">return</span> reactiveMongoTemplate.inTransaction()</span><br><span class="line"> <span class="comment">//所有文档的持久化操作都只能在单独一个execute函数中汇总实现</span></span><br><span class="line"> .execute(action -> action.insert(aDocument)</span><br><span class="line"> .flatMap(a -> {</span><br><span class="line"> cDocument.setName(a.getName() + <span class="string">"copy"</span>);</span><br><span class="line"> <span class="keyword">return</span> action.insert(cDocument)</span><br><span class="line"> .map(d -> {</span><br><span class="line"> <span class="keyword">if</span> (exception) {</span><br><span class="line"> <span class="comment">//测试跨文档的异常回滚</span></span><br><span class="line"> <span class="keyword">throw</span> Exceptions.propagate(<span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">"模拟异常的出现"</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> d;</span><br><span class="line"> });</span><br><span class="line"> })</span><br><span class="line"> )</span><br><span class="line"> <span class="comment">//如果里面是个mono,则用next取出第一个元素就是里面的mono</span></span><br><span class="line"> .next()</span><br><span class="line"> .map(list -> {</span><br><span class="line"> <span class="comment">//需要注意,在execute之外的函数中产生的异常,不会触发事务的回滚。</span></span><br><span class="line"> <span class="comment">// if (exception) {</span></span><br><span class="line"> <span class="comment">// throw Exceptions.propagate(new RuntimeException("模拟异常的出现"));</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="keyword">return</span> Boolean.TRUE;</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>flux的数据库操作,在有事务的前提下不能用flatMap,要用事务不能用flatMap要用concatMap保持有序</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@PostMapping("/test")</span></span><br><span class="line"><span class="keyword">public</span> Mono <span class="title function_">testA</span><span class="params">(<span class="meta">@RequestParam</span> <span class="type">boolean</span> exception)</span> {</span><br><span class="line"> <span class="keyword">return</span> embedService.saveAC(<span class="keyword">new</span> <span class="title class_">ADocument</span>(<span class="string">"张三"</span>), <span class="keyword">new</span> <span class="title class_">CDocument</span>(<span class="string">"李四"</span>), exception);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Mono<Boolean> <span class="title function_">saveAC</span><span class="params">(ADocument aDocument, CDocument cDocument, <span class="type">boolean</span> exception)</span> {</span><br><span class="line"> <span class="keyword">return</span> reactiveMongoTemplate.inTransaction()</span><br><span class="line"> <span class="comment">//所有文档的持久化操作都只能在单独一个execute函数中汇总实现</span></span><br><span class="line"> .execute(action -> Flux.fromIterable(<span class="string">"1"</span>, <span class="string">"2"</span>, <span class="string">"3"</span>)</span><br><span class="line"> <span class="comment">//如果是个flux此处要用concatMap保持有序不能用flatMap</span></span><br><span class="line"> .concatMap(i -> action.insert(aDocument)</span><br><span class="line"> .flatMap(a -> {</span><br><span class="line"> cDocument.setName(a.getName() + <span class="string">"copy"</span>);</span><br><span class="line"> <span class="keyword">return</span> action.insert(cDocument)</span><br><span class="line"> .map(d -> {</span><br><span class="line"> <span class="keyword">if</span> (exception) {</span><br><span class="line"> <span class="comment">//测试跨文档的异常回滚</span></span><br><span class="line"> <span class="keyword">throw</span> Exceptions.propagate(<span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">"模拟异常的出现"</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> d;</span><br><span class="line"> });</span><br><span class="line"> }));</span><br><span class="line"> )</span><br><span class="line"> <span class="comment">//如果里面返回的就是一个flux则不需要使用next</span></span><br><span class="line"> <span class="comment">//.next()</span></span><br><span class="line"> .map(list -> {</span><br><span class="line"> <span class="comment">//需要注意,在execute之外的函数中产生的异常,不会触发事务的回滚。</span></span><br><span class="line"> <span class="comment">// if (exception) {</span></span><br><span class="line"> <span class="comment">// throw Exceptions.propagate(new RuntimeException("模拟异常的出现"));</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="keyword">return</span> Boolean.TRUE;</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><br><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Java" scheme="http://me.iblogc.com/tags/java/"/>
<category term="Reactive" scheme="http://me.iblogc.com/tags/reactive/"/>
<category term="事务" scheme="http://me.iblogc.com/tags/%E4%BA%8B%E5%8A%A1/"/>
<category term="mongo" scheme="http://me.iblogc.com/tags/mongo/"/>
<category term="数据库" scheme="http://me.iblogc.com/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<category term="Flux" scheme="http://me.iblogc.com/tags/flux/"/>
</entry>
<entry>
<title>WebFlux中mongo操作-Aggregation</title>
<link href="http://me.iblogc.com/2020/01/17/WebFlux%E4%B8%ADmongo%E6%93%8D%E4%BD%9C-Aggregation/"/>
<id>http://me.iblogc.com/2020/01/17/WebFlux中mongo操作-Aggregation/</id>
<published>2020-01-17T07:12:53.000Z</published>
<updated>2020-01-17T08:01:32.000Z</updated>
<content type="html"><![CDATA[<p><br><br><span id="more"></span></p><p>switch</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">ConditionalOperators.Switch.<span class="type">CaseOperator</span> <span class="variable">cond</span> <span class="operator">=</span> ConditionalOperators.Switch.CaseOperator.when(</span><br><span class="line"> BooleanOperators.And.and(</span><br><span class="line"> ComparisonOperators.Eq.valueOf(<span class="string">"channelBillStatus1"</span>).equalToValue(<span class="string">"已结算"</span>),</span><br><span class="line"> ComparisonOperators.Eq.valueOf(<span class="string">"channelBillStatus2"</span>).equalToValue(<span class="string">"已结算"</span>)</span><br><span class="line"> )</span><br><span class="line"> ).then(<span class="string">"已结清"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">Aggregation</span> <span class="variable">aggregation</span> <span class="operator">=</span> Aggregation.newAggregation(</span><br><span class="line"> Aggregation.project(<span class="string">"channelBillStatus1"</span>, <span class="string">"channelBillStatus2"</span>)</span><br><span class="line"> .and(ConditionalOperators.switchCases(cond).defaultTo(<span class="string">"未结清"</span>)).as(<span class="string">"channelBillStatus"</span>)</span><br><span class="line"> );</span><br><span class="line"> </span><br><span class="line"> reactiveMongoTemplate.aggregate(aggregation, PlatformBillItem.class, PlatformBillBo.class);</span><br></pre></td></tr></table></figure><p>lookup及id类型转换</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//把_id转成String并赋值给id</span></span><br><span class="line"> Aggregation.project(<span class="string">"internalId"</span>, <span class="string">"name"</span>, <span class="string">"isAvailable"</span>, <span class="string">"isCanAdd"</span>, <span class="string">"fitGender"</span>, <span class="string">"fitAge"</span>, <span class="string">"fitMaritalStatus"</span>, <span class="string">"price"</span>, <span class="string">"sortNo"</span>, <span class="string">"createdAt"</span>)</span><br><span class="line"> turetureture<span class="comment">//如果需要把String转Object使用ConvertOperators.ToObjectId.toObjectId()</span></span><br><span class="line"> .and(ConvertOperators.ToString.toString(<span class="string">"$_id"</span>)).as(<span class="string">"id"</span>);</span><br><span class="line"> <span class="comment">//用当前表的id值去匹配chn_section表的sectionId字段值,并把结果存入chnSections数组</span></span><br><span class="line"> Aggregation.lookup(<span class="string">"chn_section"</span>, <span class="string">"id"</span>, <span class="string">"sectionId"</span>, <span class="string">"chnSections"</span>);</span><br><span class="line"> <span class="comment">//如有需要,把chnSections数组拆出来,chnSections数组有几个元素,当前这条数据就会被拆成多少条,chnSections值会变成元素值而不再是原来的数组</span></span><br><span class="line"> <span class="comment">//如果chnSections数组无值,默认会丢弃这条数据,如果要保留设置preserveNullAndEmptyArrays=true</span></span><br><span class="line"> Aggregation.unwind(<span class="string">"chnSection"</span>, <span class="literal">true</span>);</span><br><span class="line"> <span class="comment">//只输出这些字段</span></span><br><span class="line"> Aggregation.project(<span class="string">"internalId"</span>, <span class="string">"name"</span>, <span class="string">"isAvailable"</span>, <span class="string">"isCanAdd"</span>, <span class="string">"fitGender"</span>, <span class="string">"fitAge"</span>, <span class="string">"fitMaritalStatus"</span>, <span class="string">"price"</span>, <span class="string">"sortNo"</span>, <span class="string">"createdAt"</span>, <span class="string">"chnSections"</span>);</span><br><span class="line"> reactiveMongoTemplate.aggregate(aggregation, PlatformBillItem.class, PlatformBillBo.class);</span><br></pre></td></tr></table></figure><p>如果lookup时,如果要对匹配的数据进行筛选(参考链接:<a href="https://stackoverflow.com/questions/51107626/spring-data-mongodb-lookup-with-pipeline-aggregation)">https://stackoverflow.com/questions/51107626/spring-data-mongodb-lookup-with-pipeline-aggregation)</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//原始mongo</span></span><br><span class="line"><span class="comment">//{</span></span><br><span class="line"><span class="comment">// $lookup:</span></span><br><span class="line"><span class="comment">// {</span></span><br><span class="line"><span class="comment">// from: <collection to join>,</span></span><br><span class="line"><span class="comment">// let: { <var_1>: <expression>, …, <var_n>: <expression> },</span></span><br><span class="line"><span class="comment">// pipeline: [ <pipeline to execute on the collection to join> ],</span></span><br><span class="line"><span class="comment">// as: <output array field></span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"><span class="comment">//}</span></span><br><span class="line"><span class="comment">//自定义一个AggregationOperation类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomProjectAggregationOperation</span> <span class="keyword">implements</span> <span class="title class_">AggregationOperation</span> {</span><br><span class="line"> <span class="keyword">private</span> String jsonOperation;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">CustomProjectAggregationOperation</span><span class="params">(String jsonOperation)</span> {</span><br><span class="line"> <span class="built_in">this</span>.jsonOperation = jsonOperation;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Document <span class="title function_">toDocument</span><span class="params">(AggregationOperationContext aggregationOperationContext)</span> {</span><br><span class="line"> <span class="keyword">return</span> aggregationOperationContext.getMappedObject(Document.parse(jsonOperation));</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">getJsonOperation</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"{"</span> +</span><br><span class="line"> <span class="string">" $lookup: "</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" from: 'chn_set_meal',"</span> +</span><br><span class="line"> <span class="string">" let: {"</span> +</span><br><span class="line"> <span class="string">" id: { $toString: '$_id' }"</span> +</span><br><span class="line"> <span class="string">" },"</span> +</span><br><span class="line"> <span class="string">" pipeline: ["</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" $match: "</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" $expr: "</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" $and: "</span> +</span><br><span class="line"> <span class="string">" ["</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" $eq: ['$setMealId', '$$id']"</span> +</span><br><span class="line"> <span class="string">" },"</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" $eq: ['$cooperationState', '合作中']"</span> +</span><br><span class="line"> <span class="string">" }"</span> +</span><br><span class="line"> <span class="string">" ]"</span> +</span><br><span class="line"> <span class="string">" }"</span> +</span><br><span class="line"> <span class="string">" }"</span> +</span><br><span class="line"> <span class="string">" },"</span> +</span><br><span class="line"> <span class="string">" {"</span> +</span><br><span class="line"> <span class="string">" $project: {"</span> +</span><br><span class="line"> <span class="string">" channelId: 1,"</span> +</span><br><span class="line"> <span class="string">" channelName: 1"</span> +</span><br><span class="line"> <span class="string">" cooperationState: 1"</span> +</span><br><span class="line"> <span class="string">" }"</span> +</span><br><span class="line"> <span class="string">" }"</span> +</span><br><span class="line"> <span class="string">" ],"</span> +</span><br><span class="line"> <span class="string">" as: 'channels'"</span> +</span><br><span class="line"> <span class="string">" }"</span> +</span><br><span class="line"> <span class="string">"}}"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="type">AggregationOperation</span> <span class="variable">aggregationOperation</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CustomProjectAggregationOperation</span>(getJsonOperation());</span><br><span class="line"> <span class="keyword">return</span> reactiveMongoTemplate.aggregate(Aggregation.newAggregation(aggregationOperation), SetMeal.class, SetMealListBo.class);</span><br></pre></td></tr></table></figure><p>group</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//背景:查询交易表,订单和交易一对多</span></span><br><span class="line">Aggregation.group(<span class="string">"orderNo"</span>)</span><br><span class="line"> <span class="comment">//单一组的金额汇总</span></span><br><span class="line"> .sum(<span class="string">"amount"</span>).as(<span class="string">"totalAmount"</span>)</span><br><span class="line"> <span class="comment">//组的最后一个订单号</span></span><br><span class="line"> .last(<span class="string">"orderNo"</span>).as(<span class="string">"orderNo"</span>)</span><br><span class="line"> <span class="comment">//组里数据条数</span></span><br><span class="line"> .count().as(<span class="string">"tradeCount"</span>)</span><br><span class="line"> <span class="comment">//把一组数据里每条数据的状态放到一个statuses数组里</span></span><br><span class="line"> .addToSet(<span class="string">"status"</span>).as(<span class="string">"statuses"</span>)</span><br><span class="line"> <span class="comment">//把一组数据里的一些字段信息重新组装成一个对象放到billItems的对象数组里</span></span><br><span class="line"> .push(<span class="keyword">new</span> <span class="title class_">BasicDBObject</span>(<span class="string">"tradeContent"</span>, <span class="string">"$tradeContent"</span>)</span><br><span class="line"> .append(<span class="string">"tradeNo"</span>, <span class="string">"$tradeNo"</span>)</span><br><span class="line"> .append(<span class="string">"amount"</span>, <span class="string">"$amount"</span>)</span><br><span class="line"> ).as(<span class="string">"billItems"</span>);</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><br><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Java" scheme="http://me.iblogc.com/tags/java/"/>
<category term="Reactive" scheme="http://me.iblogc.com/tags/reactive/"/>
<category term="事务" scheme="http://me.iblogc.com/tags/%E4%BA%8B%E5%8A%A1/"/>
<category term="mongo" scheme="http://me.iblogc.com/tags/mongo/"/>
<category term="数据库" scheme="http://me.iblogc.com/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<category term="Aggregation" scheme="http://me.iblogc.com/tags/aggregation/"/>
</entry>
<entry>
<title>自用软件推荐</title>
<link href="http://me.iblogc.com/2019/10/12/awesome-software/"/>
<id>http://me.iblogc.com/2019/10/12/awesome-software/</id>
<published>2019-10-12T08:40:17.000Z</published>
<updated>2021-05-06T07:24:05.081Z</updated>
<content type="html"><![CDATA[<p>日用软件/效率提升/开发工具/Chorme插件扩展推荐<br><span id="more"></span></p><h4 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h4><h5 id="日常"><a href="#日常" class="headerlink" title="日常"></a>日常</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">iShot</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><a href="http://www.irolan.com/"><strong>Rolan</strong></a>(Windows)</p><p> Windows上的快速启动器 </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><strong>[Shadowsocks/v2rayNG/v2ray/V2rayU]</strong>(macOS/Windows/Linux/Android)</p><p> 科学上网</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><a href="http://www.eagleget.com/"><strong>EagleGet</strong></a>(Windows)</p><p> 下载器</p></li><li><p><a href="https://www.freedownloadmanager.org/zh/"><strong>Free Download Manager</strong></a>(macOS/Windows)</p><p> 下载器</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><a href="https://github.com/hakimel/reveal.js"><strong>reveal.js</strong></a>(Web)</p><p> 程序员的PPT工具</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></ol><h5 id="开发"><a href="#开发" class="headerlink" title="开发"></a>开发</h5><ol start="22"><li><p><a href="https://github.com/cmderdev/cmder"><strong>Cmder</strong></a>(Windows)</p><p> Windows默认命令行替代品 </p></li><li><p><a href="https://insomnia.rest/"><strong>Insomnia</strong></a>(macOS/Windows/Linux)</p><p> 跨平台的REST客户端</p></li><li><p><a href="https://www.getpostman.com/"><strong>Postman</strong></a>(macOS/Windows/Linux/<del>Chrome App</del>)</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="https://hyper.is/"><strong>hyper</strong></a>(macOS/Windows/Linux)</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://github.com/fatedier/frp"><strong>frp</strong></a>(macOS/Windows/Linux)<br> 内网穿透</p></li><li><p><a href="https://www.royalapps.com/ts/mac/features"><strong>MobaXterm</strong></a></p><p>全能终端神器</p></li><li><p><a href="https://www.royalapps.com/ts/mac/features"><strong>Royal TSX</strong></a></p><p>远程管理工具</p></li></ol><h4 id="Chrome"><a href="#Chrome" class="headerlink" title="Chrome"></a>Chrome</h4><h5 id="扩展-应用"><a href="#扩展-应用" class="headerlink" title="扩展/应用"></a>扩展/应用</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/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb"><strong>Google翻译</strong></a></p><p>支付在网页中划词翻译</p></li><li><p><a href="https://chrome.google.com/webstore/detail/lingocloud-web-translatio/jmpepeebcbihafjjadogphmbgiffiajh"><strong>彩云小译</strong></a></p><p> 网页翻译插件</p></li><li><p><strong>LastPass</strong></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><a href="https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb"><strong>Web Server for Chrome</strong></a></p><p> 快速搭建本地Web服务器(当然,你也可以<code>python -m http.server</code>)</p></li><li><p><a href="https://chrome.google.com/webstore/detail/octotree-github-code-tree/bkhaagjahfmjljalopjnoealnfndnagc"><strong>Octotree</strong></a></p><p> 在浏览器左侧树形展示Github代码。</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/toby-for-chrome/hddnkoipeenegfoeaoibdmnaalmgkpip"><strong>Toby for Chrome</strong></a></p><p> 标签页管理/稍后阅读/资料归档分类…</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><strong>惠惠购物助手/<a href="https://chrome.google.com/webstore/detail/%E8%B4%AD%E7%89%A9%E5%85%9A%E8%87%AA%E5%8A%A8%E6%AF%94%E4%BB%B7%E5%B7%A5%E5%85%B7/jgphnjokjhjlcnnajmfjlacjnjkhleah">购物党自动比价工具</a></strong></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://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>
<summary type="html">
<p>日用软件/效率提升/开发工具/Chorme插件扩展推荐<br>
</summary>
<category term="工具" scheme="http://me.iblogc.com/categories/%E5%B7%A5%E5%85%B7/"/>
<category term="效率" scheme="http://me.iblogc.com/tags/%E6%95%88%E7%8E%87/"/>
<category term="软件" scheme="http://me.iblogc.com/tags/%E8%BD%AF%E4%BB%B6/"/>
<category term="推荐" scheme="http://me.iblogc.com/tags/%E6%8E%A8%E8%8D%90/"/>
</entry>
<entry>
<title>(猫)花木兰领养注意事项</title>
<link href="http://me.iblogc.com/2019/09/17/adoption-cat-Hua-Mulan/"/>
<id>http://me.iblogc.com/2019/09/17/adoption-cat-Hua-Mulan/</id>
<published>2019-09-17T03:22:04.000Z</published>
<updated>2020-08-15T03:08:16.000Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p><img alt="" data-src="/media/花木兰/15.jpg"><br><span id="more"></span></p><section class="layout" style="font-size: 16px; color: black; padding: 10px; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">成为猫奴前的准备</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><div class="note warning"> <p>领养前请确认能否接受以下几点,如果否,不建议领养 </p> </div><ol style="margin-top: 8px; margin-bottom: 8px; padding-left: 20px; color: black; list-style-type: decimal;"><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">养猫请<strong style="font-weight: bold; color: black;">不要半途而废</strong>,请不要让它变成流浪猫</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">相对于狗来说,猫掉毛很严重,房间经常要打扫,衣服每天都要用粘毛器粘毛</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">猫不像狗一样会听你的指令,绝大部分听不懂</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">猫每年要驱虫和打狂犬费用几百</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">猫如果生病去看病花费要比人生病花费大很多很多</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">绝育需要花费1~2k</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">猫会比较粘人,一般喜欢在人腿上或身上睡觉</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">猫呆的房间不能开窗</li><br></ol><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">小猫信息</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">生日:2018年09月23日</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">性别:妹子</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">品种:英短银渐层+虎斑,有部分折耳基因</p><br><h3 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; font-size: 20px;"><span>家族:</span></h3><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">爷爷:英短银渐层</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">奶奶:美短虎斑</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">爸爸:美短虎斑</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">妈妈:英短银渐层(有折耳基因)</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">新家</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">1.刚带回家应该先放到猫砂里,拿它的爪子刨几 猫砂,让它知道那里是拉屎撒尿的地方,最好让小猫在里面待一会儿</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">2.如果小猫在其他地方拉屎撒尿了,把排泄物处理掉放到猫砂里去,让后把小猫也放到猫砂里,小猫闻到那气味让它知道应该在猫砂里厕所,然后用的爪子刨猫砂把排泄物盖起来</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">吃</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><ol style="margin-top: 8px; margin-bottom: 8px; padding-left: 20px; color: black; list-style-type: decimal;"><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">主食一定是猫粮,猫粮选购请自己网上查询,这里不提供建议</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">不能喝生水最好倒是温水,凉开水也可以,猫喝的水每天换一次</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">猫粮倒碗里小猫自己会吃,不要太多也不要太少,一天的量就可以,太多了过夜不新鲜猫可能就不喜欢吃了</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">10个月内都可以吃幼猫粮,后面可以换成成猫 粮,除了猫粮,也要给猫些吃湿粮、猫罐头、营养膏和其他猫零食,但不要给给猫吃太多,因为这些食物比较香,如果猫吃多了,可能就会挑食不喜欢吃猫粮了</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">平时也可以买点鸡胸肉,用清水煮熟,然后撕下 来喂猫吃。</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">尽量别给猫吃咸的东西或其它味道的人吃的东西</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">目前小猫已经可以不用喝奶了,不要给猫喝牛奶,牛奶里有乳糖,猫是不能消化乳糖的,如果喝了可能会腹泻,严重会致死</li><br><li style="margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: black;">其他东西给猫吃之前先网上查下能不能给猫吃</li><br></ol><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">用</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">1.可以买个猫抓盘或猫抓板猫喜欢到处抓东西,如果有纸箱子猫也会经常抓</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">2.可以买个猫爬架,让猫玩,猫喜欢爬高高</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">3.猫到夏天可能会掉毛比较多,网上买个猫毛梳子 或噜猫手套时常给猫去去毛</p><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">4.因为猫会掉毛,抱它玩了之后,衣服上经常会粘 上毛,可以淘宝买个粘毛滚筒粘衣服上或是其它地方的猫毛,十分有效</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">玩</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">可以买激光笔啊,逗猫棒之类的猫玩具,让小猫玩</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">洗澡</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">可以自己在家洗(容易被抓伤)或是带去宠物店(100多一次)</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">绝育</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">请在适当的时候带猫去做绝育</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">疫苗</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">猫的狂犬和驱虫每年要打,人的狂犬疫苗自己决定要不要打,不提供建议</p><br><h2 style="margin-top: 40px; margin-bottom: 20px; font-weight: bold; color: black; border-bottom: 2px solid rgb(239, 112, 96); font-size: 1.3em;"><span style="display: inline-block; font-weight: bold; background: rgb(239, 112, 96); color: #ffffff; padding: 3px 10px 1px; border-top-right-radius: 3px; border-top-left-radius: 3px; margin-right: 3px;">其它注意事项</span><span style="display: inline-block; vertical-align: bottom; border-bottom: 36px solid #efebe9; border-right: 20px solid transparent;"> </span></h2><br><p style="font-size: 16px; padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black;">前面说了,猫喜欢爬高高,所以猫能够跳上去或爬上去的窗户阳台要关掉,猫不知道高空危险,会跳楼的,很多猫都是坠楼死掉的</p><br></section><div class="group-picture"><div class="group-picture-container"><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/1.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/2.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/3.jpg"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/4.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/5.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/6.jpg"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/7.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/8.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/9.jpg"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/10.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/11.jpg"></div><div class="group-picture-column" style="width: 33.333333333333336%;"><img alt="" data-src="/media/花木兰/12.jpg"></div></div><div class="group-picture-row"><div class="group-picture-column" style="width: 50%;"><img alt="" data-src="/media/花木兰/13.jpg"></div><div class="group-picture-column" style="width: 50%;"><img alt="" data-src="/media/花木兰/14.jpg"></div></div></div></div></body></html>]]></content>
<summary type="html">
<p><img src="/media/花木兰/15.jpg" alt=""><br>
</summary>
<category term="猫" scheme="http://me.iblogc.com/categories/%E7%8C%AB/"/>
<category term="猫" scheme="http://me.iblogc.com/tags/%E7%8C%AB/"/>
<category term="Cat" scheme="http://me.iblogc.com/tags/cat/"/>
</entry>
<entry>
<title>Git后悔药</title>
<link href="http://me.iblogc.com/2019/07/25/git-reset/"/>
<id>http://me.iblogc.com/2019/07/25/git-reset/</id>
<published>2019-07-25T10:33:09.000Z</published>
<updated>2020-01-19T07:41:07.000Z</updated>
<content type="html"><![CDATA[<p>使用git提交代码过程中有时会手抖提交错误代码,这时就需要用到git的后悔药reset操作。</p><span id="more"></span><div id="sequence-0"></div><h3 id="差异(diff)"><a href="#差异(diff)" class="headerlink" title="差异(diff)"></a>差异(diff)</h3><p>工作区vs暂存区: <code>git diff</code></p><p>暂存区vs本地仓库: <code>git diff —cached</code></p><p>本地仓库vs远程仓库: <code>git diff <分支名> origin/<分支名></code></p><h3 id="撤消(reset)"><a href="#撤消(reset)" class="headerlink" title="撤消(reset)"></a>撤消(reset)</h3><p>撤消工作区修改: <code>git reset —hard</code></p><p>撤消(1)<code>git add</code>: <code>git reset && 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^ && git push -f</code>(先在本地回到上一个版本,然后强推到远端)<script src="https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.27/webfontloader.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/js-sequence-diagrams/1.0.6/sequence-diagram-min.js"></script><textarea id="sequence-0-code" style="display: none">title: git resetparticipant 工作区participant 暂存区participant 本地仓库participant 远程仓库 工作区->暂存区:(1) git add 暂存区->本地仓库:(2) git commit 本地仓库->远程仓库:(3) git push</textarea><textarea id="sequence-0-options" style="display: none">{"theme":"simple"}</textarea><script> var code = document.getElementById("sequence-0-code").value; var options = JSON.parse(decodeURIComponent(document.getElementById("sequence-0-options").value)); var diagram = Diagram.parse(code); diagram.drawSVG("sequence-0", options);</script></p>]]></content>
<summary type="html">
<p>使用git提交代码过程中有时会手抖提交错误代码,这时就需要用到git的后悔药reset操作。</p>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Git" scheme="http://me.iblogc.com/tags/git/"/>
<category term="reset" scheme="http://me.iblogc.com/tags/reset/"/>
<category term="恢复" scheme="http://me.iblogc.com/tags/%E6%81%A2%E5%A4%8D/"/>
<category term="后悔" scheme="http://me.iblogc.com/tags/%E5%90%8E%E6%82%94/"/>
</entry>
<entry>
<title>Git常用命令别名设置</title>
<link href="http://me.iblogc.com/2019/06/28/git-alias/"/>
<id>http://me.iblogc.com/2019/06/28/git-alias/</id>
<published>2019-06-28T09:17:06.000Z</published>
<updated>2020-10-29T17:16:23.000Z</updated>
<content type="html"><![CDATA[<html><head></head><body><p>如果平时使用git使用git命令多于GUI工具,则设置一些常用命令的别名有且于效率提升,以下是我平时使用较多的一些命令的别名设置</p><span id="more"></span><p>Git别名设置</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">git config --global alias.st status</span><br><span class="line">git config --global alias.co checkout</span><br><span class="line">git config --global alias.ci commit</span><br><span class="line">git config --global alias.br branch</span><br><span class="line">git config --global alias.cp cherry-pick</span><br><span class="line">git config --global alias.unstage <span class="string">'reset HEAD'</span></span><br><span class="line"><span class="comment"># 可用git pull -r代替</span></span><br><span class="line">git config --global alias.fr <span class="string">'!f() { git fetch && git rebase $@; }; f'</span>; </span><br><span class="line"><span class="comment"># git提交日志</span></span><br><span class="line">git config --global alias.lg <span class="string">"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' --abbrev-commit"</span></span><br></pre></td></tr></tbody></table></figure><p>删除别名</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global --<span class="built_in">unset</span> alias.xxx</span><br></pre></td></tr></tbody></table></figure><p>以下两个命令设置git alias和zsh alias都失败,暂没找到方法可以设置别名</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看仓库提交者排名前 5</span></span><br><span class="line">git <span class="built_in">log</span> --pretty=<span class="string">'%aN'</span> | <span class="built_in">sort</span> | <span class="built_in">uniq</span> -c | <span class="built_in">sort</span> -k1 -n -r | <span class="built_in">head</span> -n 5</span><br></pre></td></tr></tbody></table></figure><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 统计每个人增删行数</span></span><br><span class="line">git <span class="built_in">log</span> --format=<span class="string">'%aN'</span> | <span class="built_in">sort</span> -u | <span class="keyword">while</span> <span class="built_in">read</span> name; <span class="keyword">do</span> <span class="built_in">echo</span> -en <span class="string">"<span class="variable">$name</span>\t"</span>; git <span class="built_in">log</span> --author=<span class="string">"<span class="variable">$name</span>"</span> --pretty=tformat: --numstat | awk <span class="string">'{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }'</span> -; <span class="keyword">done</span></span><br></pre></td></tr></tbody></table></figure><p>git lg命令效果图<br><img alt="git lg命令效果图" data-src="/media/git-lg效果图.png"></p></body></html>]]></content>
<summary type="html">
<p>如果平时使用git使用git命令多于GUI工具,则设置一些常用命令的别名有且于效率提升,以下是我平时使用较多的一些命令的别名设置</p>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Git" scheme="http://me.iblogc.com/tags/git/"/>
<category term="教程" scheme="http://me.iblogc.com/tags/%E6%95%99%E7%A8%8B/"/>
</entry>
<entry>
<title>Android真机调试常用命令</title>
<link href="http://me.iblogc.com/2019/04/28/android%E7%9C%9F%E6%9C%BA%E8%B0%83%E8%AF%95%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/"/>
<id>http://me.iblogc.com/2019/04/28/android真机调试常用命令/</id>
<published>2019-04-28T12:39:32.000Z</published>
<updated>2020-08-15T03:08:21.000Z</updated>
<content type="html"><![CDATA[<p>使用USB连接Android真机调试时,使用无线连接调试会方便很多,并使用电脑端用adb命令实现截图和录屏,方便调试和问题反馈。<br><span id="more"></span></p><h2 id="无线调试"><a href="#无线调试" class="headerlink" title="无线调试"></a>无线调试</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># 前提条件:手机和电脑处理同一网段</span><br><span class="line"># 第一次手机先使用USB连接电脑执行以下命令让手机上的某一端口处于监听状态</span><br><span class="line">adb tcpip <port></span><br><span class="line"></span><br><span class="line"># 在手机上查看ip地址或使用以下命令查看ip</span><br><span class="line">adb shell ifconfig </span><br><span class="line"># 连接手机(在同一个环境下,一般手机/电脑不重启就会一直连接着)</span><br><span class="line">adb connect <ip> :<port></span><br><span class="line"># 查看连接的设备</span><br><span class="line">adb devices</span><br></pre></td></tr></table></figure><h2 id="截图"><a href="#截图" class="headerlink" title="截图"></a>截图</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 截图并保存到手机sd卡的下</span><br><span class="line">adb shell screencap -p /sdcard/screenshot.png</span><br></pre></td></tr></table></figure><p>便捷脚本(截图并自动复制到电脑剪切板/保存到电脑本地)</p><blockquote><p>因脚本里调用了linux/macOS的命令,所以只适用于macOS系统,windows请自行修改脚本。</p></blockquote><p><code>vi shot.sh</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">#!/bin/bash</span><br><span class="line"># Android截图,定位和预览默认关闭,请取消注释</span><br><span class="line"></span><br><span class="line">dd=`date +%Y-%m-%d-%H-%M-%S`</span><br><span class="line">pwd=`pwd`</span><br><span class="line">adb shell screencap -p /sdcard/screenshot.png</span><br><span class="line">adb pull /sdcard/screenshot.png</span><br><span class="line">adb shell rm /sdcard/screenshot.png</span><br><span class="line">mv screenshot.png $dd.png</span><br><span class="line">echo "截图已保存为当前目录下的"$dd.png</span><br><span class="line"># 修改图片尺寸,长或宽最大不超过960,等比缩放</span><br><span class="line">echo "压缩图片..."</span><br><span class="line">sips -Z 960 $pwd/$dd.png</span><br><span class="line"># 定位到文件</span><br><span class="line">open ./$dd.png -R</span><br><span class="line"># 打开预览</span><br><span class="line">open -a Preview $dd.png</span><br><span class="line"># 复制到剪切板</span><br><span class="line">osascript -e 'on run args' -e 'set the clipboard to POSIX file (first item of args)' -e end $pwd/$dd.png</span><br><span class="line">echo "截图已复制到剪切板"</span><br></pre></td></tr></table></figure><p>授予执行权限<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod a+x shot.sh</span><br></pre></td></tr></table></figure></p><p>使用方法</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./shot.sh</span><br></pre></td></tr></table></figure><p>⌘+v试试</p><p><em>可把命令添加alias别名</em></p><h2 id="录屏"><a href="#录屏" class="headerlink" title="录屏"></a>录屏</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"># 执行录屏并保存到手机sd卡目录下(默认时长180s)</span><br><span class="line"># 可配置参数</span><br><span class="line"># --time-limit: 录制时长,单位秒</span><br><span class="line"># --size: 分辨率,如1280*720,不指定默认使用手机的分辨率</span><br><span class="line"># --bit-rate: 视频的比特率,如6Mbps为6000000</span><br><span class="line"># --verbose: 命令行显示log</span><br><span class="line">adb shell screenrecord /sdcard/demo.mp4</span><br></pre></td></tr></table></figure><p>便捷脚本(录屏并自动复制到电脑剪切板/保存到电脑本地)</p><blockquote><p>因脚本里调用了linux/macOS的命令,所以只适用于macOS系统,windows请自行修改脚本。</p></blockquote><p><code>vi record.sh</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">#!/bin/bash</span><br><span class="line"># Android录屏</span><br><span class="line">dd=`date +%Y-%m-%d-%H-%M-%S`"-$1s"</span><br><span class="line">pwd=`pwd`</span><br><span class="line">adb shell screenrecord --time-limit $1 /sdcard/screenrecord.mp4</span><br><span class="line">adb pull /sdcard/screenrecord.mp4</span><br><span class="line">adb shell rm /sdcard/screenrecord.mp4</span><br><span class="line">mv screenrecord.mp4 $dd.mp4</span><br><span class="line">echo "$1秒视频已保存为当前目录下的"$dd.mp4</span><br><span class="line"># 定位到文件</span><br><span class="line">open ./$dd.mp4 -R</span><br><span class="line"># 复制到剪切板</span><br><span class="line">osascript -e 'on run args' -e 'set the clipboard to POSIX file (first item of args)' -e end $pwd/$dd.mp4</span><br><span class="line">echo "$1秒视频已复制到剪切板"</span><br></pre></td></tr></table></figure><p>授予执行权限<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod a+x record.sh</span><br></pre></td></tr></table></figure></p><p>使用方法<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 3为录制秒数,可修改</span><br><span class="line">./record.sh 3</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p>使用USB连接Android真机调试时,使用无线连接调试会方便很多,并使用电脑端用adb命令实现截图和录屏,方便调试和问题反馈。<br>
</summary>
<category term="程序员" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E5%91%98/"/>
<category term="Android" scheme="http://me.iblogc.com/tags/android/"/>
<category term="macOS" scheme="http://me.iblogc.com/tags/macos/"/>
<category term="命令" scheme="http://me.iblogc.com/tags/%E5%91%BD%E4%BB%A4/"/>
<category term="效率" scheme="http://me.iblogc.com/tags/%E6%95%88%E7%8E%87/"/>
<category term="无线" scheme="http://me.iblogc.com/tags/%E6%97%A0%E7%BA%BF/"/>
<category term="调试" scheme="http://me.iblogc.com/tags/%E8%B0%83%E8%AF%95/"/>
<category term="adb" scheme="http://me.iblogc.com/tags/adb/"/>
<category term="USB" scheme="http://me.iblogc.com/tags/usb/"/>
</entry>
<entry>
<title>JS笔记</title>
<link href="http://me.iblogc.com/2019/04/24/js%E7%AC%94%E8%AE%B0/"/>
<id>http://me.iblogc.com/2019/04/24/js笔记/</id>
<published>2019-04-24T08:35:13.000Z</published>
<updated>2020-08-15T03:06:50.000Z</updated>
<content type="html"><![CDATA[<p>js中要用变量作为key的话使用方括号括住<br>例:<code>this.searchKeyword</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.$http({</span><br><span class="line"> <span class="attr">url</span>: <span class="variable language_">this</span>.<span class="property">searchUrl</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="variable language_">this</span>.<span class="property">remoteRequestMethod</span>,</span><br><span class="line"> <span class="attr">params</span>: <span class="title class_">Object</span>.<span class="title function_">assign</span>({}, <span class="variable language_">this</span>.<span class="property">searchParams</span>, <span class="variable language_">this</span>.<span class="property">pager</span>),</span><br><span class="line"> <span class="attr">data</span>: <span class="title class_">Object</span>.<span class="title function_">assign</span>({ [<span class="variable language_">this</span>.<span class="property">searchKeyword</span>]: query }, <span class="variable language_">this</span>.<span class="property">searchBody</span>, <span class="variable language_">this</span>.<span class="property">pager</span>)</span><br><span class="line"> })</span><br></pre></td></tr></table></figure><br><span id="more"></span><br>全文完🙈</p>]]></content>
<summary type="html">
<p>js中要用变量作为key的话使用方括号括住<br>例:<code>this.searchKeyword</code><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.$http(&#123;</span><br><span class="line"> <span class="attr">url</span>: <span class="variable language_">this</span>.<span class="property">searchUrl</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="variable language_">this</span>.<span class="property">remoteRequestMethod</span>,</span><br><span class="line"> <span class="attr">params</span>: <span class="title class_">Object</span>.<span class="title function_">assign</span>(&#123;&#125;, <span class="variable language_">this</span>.<span class="property">searchParams</span>, <span class="variable language_">this</span>.<span class="property">pager</span>),</span><br><span class="line"> <span class="attr">data</span>: <span class="title class_">Object</span>.<span class="title function_">assign</span>(&#123; [<span class="variable language_">this</span>.<span class="property">searchKeyword</span>]: query &#125;, <span class="variable language_">this</span>.<span class="property">searchBody</span>, <span class="variable language_">this</span>.<span class="property">pager</span>)</span><br><span class="line"> &#125;)</span><br></pre></td></tr></table></figure><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="js" scheme="http://me.iblogc.com/tags/js/"/>
</entry>
<entry>
<title>SpringWebFlux使用笔记</title>
<link href="http://me.iblogc.com/2019/02/28/SpringWebFlux/"/>
<id>http://me.iblogc.com/2019/02/28/SpringWebFlux/</id>
<published>2019-02-28T05:32:59.000Z</published>
<updated>2020-08-15T03:07:21.000Z</updated>
<content type="html"><![CDATA[<p>记录使用SpringWebFlux的一些笔记。</p><span id="more"></span><h2 id="groupBy"><a href="#groupBy" class="headerlink" title="groupBy"></a>groupBy</h2><p>对flux进行分组。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">channelOnlineCityBoFlux.sort((s1, s2) -> Objects.requireNonNull(s1.getInitial()).compareTo(s2.getInitial()))</span><br><span class="line"> .groupBy(city -> city.getInitial().substring(<span class="number">0</span>, <span class="number">1</span>).toUpperCase())</span><br><span class="line"> .sort((s1, s2) -> Objects.requireNonNull(s1.key()).compareTo(s2.key()))</span><br><span class="line"> .flatMap(gf -> gf.collectList()</span><br><span class="line"> .map(cityList -> {</span><br><span class="line"> <span class="type">ChannelOnlineCityGroupByPinYinBo</span> <span class="variable">cityGroupByPinYinBo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ChannelOnlineCityGroupByPinYinBo</span>();</span><br><span class="line"> cityGroupByPinYinBo.setLetter(gf.key());</span><br><span class="line"> cityGroupByPinYinBo.setCities(cityList);</span><br><span class="line"> <span class="keyword">return</span> cityGroupByPinYinBo;</span><br><span class="line"> }));</span><br></pre></td></tr></table></figure></p><h2 id="handle"><a href="#handle" class="headerlink" title="handle"></a>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"><br></iframe>]]></content>
<summary type="html">
<p>记录使用SpringWebFlux的一些笔记。</p>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Java" scheme="http://me.iblogc.com/tags/java/"/>
<category term="Reactive" scheme="http://me.iblogc.com/tags/reactive/"/>
</entry>
<entry>
<title>使用alembic进行数据库版本管理</title>
<link href="http://me.iblogc.com/2018/09/13/%E4%BD%BF%E7%94%A8alembic%E8%BF%9B%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%BA%93%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/"/>
<id>http://me.iblogc.com/2018/09/13/使用alembic进行数据库版本管理/</id>
<published>2018-09-13T10:53:14.000Z</published>
<updated>2020-03-04T05:22:29.000Z</updated>
<content type="html"><![CDATA[<p>转自:<a href="https://www.cnblogs.com/blackmatrix/p/6236573.html,做了部分修改">https://www.cnblogs.com/blackmatrix/p/6236573.html,做了部分修改</a></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>随着项目业务需求的不断变更,数据库的表结构修改难以避免,此时就需要对数据库的修改加以记录和控制,便于项目的版本管理和随意的升级和降级。</p><p>Alembic 就可以很好的解决这个问题。Alembic 是 SQLAlchemy 作者开发的 Python 数据库版本管理工具。<br><span id="more"></span></p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install alembic</span><br></pre></td></tr></table></figure><p>通过 pip 命令安装,如果使用虚拟环境,记得激活虚拟环境后再执行 pip 命令</p><p>同时需要安装的还有 SQLAlchemy 和 PyMysql</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install sqlalchemy</span><br><span class="line">pip install pymysql</span><br></pre></td></tr></table></figure><h2 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h2><p>在使用 alembic 之前,需要进行初始化操作。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic init <YOUR_ALEMBIC_DIR></span><br></pre></td></tr></table></figure><p>YOUR_ALEMBIC_DIR,可以取一个符合项目名称规范的目录名,如</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic init alembic</span><br></pre></td></tr></table></figure><p><strong>此时需要注意,如果之前是在虚拟环境中安装的 alembic,需要激活虚拟环境后,在执行上述命令。</strong></p><p><strong>同时,建议 cd 到项目根目录再执行初始化操作,因为 YOUR_ALEMBIC_DIR 会在当前目录下创建。</strong></p><p>显示类似结果即初始化成功。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Creating directory D:\Project\py_sqlalchemy_demo\alembic ... <span class="keyword">done</span></span><br><span class="line">Creating directory D:\Project\py_sqlalchemy_demo\alembic\versions ... <span class="keyword">done</span></span><br><span class="line">Generating D:\Project\py_sqlalchemy_demo\alembic.ini ... <span class="keyword">done</span></span><br><span class="line">Generating D:\Project\py_sqlalchemy_demo\alembic\env.py ... <span class="keyword">done</span></span><br><span class="line">Generating D:\Project\py_sqlalchemy_demo\alembic\README ... <span class="keyword">done</span></span><br><span class="line">Generating D:\Project\py_sqlalchemy_demo\alembic\script.py.mako ... <span class="keyword">done</span></span><br><span class="line">Please edit configuration/connection/logging settings <span class="keyword">in</span> <span class="string">'D:\\Project\\py_sqlalchemy_demo\\alembic.ini'</span> befor</span><br><span class="line">e proceeding.</span><br></pre></td></tr></table></figure><p>初始化成功后,会在执行初始化命令的目录下,生成一个 alembic.ini 的配置文件,及一个 alembic 目录,目录名就是之前设置的 YOUR_ALEMBIC_DIR。</p><h2 id="修改配置文件"><a href="#修改配置文件" class="headerlink" title="修改配置文件"></a>修改配置文件</h2><p>接下来对 alembic.ini 的信息进行修改。</p><p>主要修改的是配置文件中的数据库连接部分。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sqlalchemy.url = driver://user:<span class="keyword">pass</span>@localhost:port/dbname</span><br></pre></td></tr></table></figure><p>将配置文件中,此部分替换成对应的数据库连接,这个数据库连接的写法是与 SQLAlchemy 创建 engine 时是一样的。</p><p>如我在 demo 中使用的是 SQLAlchemy 与 PyMysql,那数据库连接就是类似如下</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql+pymysql://demo_user:demo123456@<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">3306</span>/demo_db</span><br></pre></td></tr></table></figure><h2 id="修改-env-py"><a href="#修改-env-py" class="headerlink" title="修改 env.py"></a>修改 env.py</h2><p>除修改配置文件外,还需要对 YOUR_ALEMBIC_DIR 目录下的 env.py 文件进行修改。</p><p>在 env.py 中,将 target_metadata 设置成项目的 model,使 alembic 能获取到项目中 model 定义的信息。</p><p>将原先的</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">target_metadata = <span class="literal">None</span></span><br></pre></td></tr></table></figure><p>修改成项目中的 model<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"></span><br><span class="line">sys.path.append(dirname(dirname(abspath(__file__))))</span><br><span class="line"><span class="keyword">from</span> app <span class="keyword">import</span> db</span><br><span class="line">target_metadata = db.metadata</span><br></pre></td></tr></table></figure></p><h2 id="创建新版本"><a href="#创建新版本" class="headerlink" title="创建新版本"></a>创建新版本</h2><p>用 alembic revision -m + 注释 创建数据库版本</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic revision --autogenerate -m <span class="string">"init db"</span></span><br></pre></td></tr></table></figure><p>运行后,类似如下结果,即创建版本成功</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">INFO [alembic.runtime.migration] Context impl MySQLImpl.</span><br><span class="line">INFO [alembic.runtime.migration] Will assume non-transactional DDL.</span><br><span class="line">INFO [alembic.autogenerate.compare] Detected removed table <span class="string">'user'</span></span><br><span class="line">Generating D:\Project\py_sqlalchemy_demo\alembic\versions\7b55b3d83158_create_tables.py ... <span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>每次修改过 SQLAlchemy 的 model,执行此命令即可创建对应的版本。</p><p>执行成功后,会在项目根目录下的 alembic/versions / 下生成的对应版本的 py 文件。命令规则是版本号 + 注释。(这个命名规则是在配置文件中定义的)</p><p>在每次创建新版本后,需要执行将数据库升级到新版本的命令,才能继续更新版本。</p><h2 id="变更数据库"><a href="#变更数据库" class="headerlink" title="变更数据库"></a>变更数据库</h2><p>在每次创建新版本后,需要执行将数据库升级到新版本的命令,才能继续更新版本</p><p><strong>将数据库升级到最新版本</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic upgrade <span class="built_in">head</span></span><br></pre></td></tr></table></figure><p>运行结果类似</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(venv_win) D:\Project\py_sqlalchemy_demo>alembic upgrade <span class="built_in">head</span></span><br><span class="line">INFO [alembic.runtime.migration] Context impl MySQLImpl.</span><br><span class="line">INFO [alembic.runtime.migration] Will assume non-transactional DDL.</span><br><span class="line">INFO [alembic.runtime.migration] Running upgrade 7b55b3d83158 -> b034414f04cd, create tables02</span><br></pre></td></tr></table></figure><p>其中,命令中的 head 和 base 特指最新版本和最初版本。当需要对数据库进行升级时,使用 upgrade,降级使用 downgrade。</p><p><strong>将数据库降级到最初版本</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic downgrade base</span><br></pre></td></tr></table></figure><p><strong>将数据库降级到执行版本</strong>,使用 alembic downgrade + 版本号,不包含注释部分</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic downgrade <version></span><br></pre></td></tr></table></figure><p>如</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic downgrade 7b55b3d83158</span><br></pre></td></tr></table></figure><p>运行结果</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">INFO [alembic.runtime.migration] Context impl MySQLImpl.</span><br><span class="line">INFO [alembic.runtime.migration] Will assume non-transactional DDL.</span><br><span class="line">INFO [alembic.runtime.migration] Running downgrade b034414f04cd -> 7b55b3d83158, create tables02</span><br></pre></td></tr></table></figure><p>升级也是同样的道理,alembic upgrade + 版本号</p><h2 id="离线更新(生成-sql-脚本)"><a href="#离线更新(生成-sql-脚本)" class="headerlink" title="离线更新(生成 sql 脚本)"></a>离线更新(生成 sql 脚本)</h2><p>在某些不适合在线更新的情况,可以采用生成 sql 脚本的形式,进行离线更新:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic upgrade <version> --sql > migration.sql</span><br></pre></td></tr></table></figure><p>如:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic upgrade ae1027a6acf --sql > migration.sql</span><br></pre></td></tr></table></figure><p>从特定起始版本生成 sql 脚本:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic upgrade <vsersion>:<vsersion> --sql > migration.sql</span><br></pre></td></tr></table></figure><p>如:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alembic upgrade 1975ea83b712:ae1027a6acf --sql > migration.sql</span><br></pre></td></tr></table></figure><p>如果是数据库降级操作,把 upgrade 替换为 downgrade。</p><h2 id="查询当前数据库版本号"><a href="#查询当前数据库版本号" class="headerlink" title="查询当前数据库版本号"></a>查询当前数据库版本号</h2><p>在对数据库进行升级或降级后,会在当前操作的数据库中新增一个表;alembic_version。</p><p>表中的 version_num 字段记录了当前的数据库版本号。</p><h2 id="清除所有版本"><a href="#清除所有版本" class="headerlink" title="清除所有版本"></a>清除所有版本</h2><p>如果需要清除所有的版本,将 versions 删除掉,同时删除数据库的 alembic_version 表。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</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>
<summary type="html">
<p>转自:<a href="https://www.cnblogs.com/blackmatrix/p/6236573.html,做了部分修改">https://www.cnblogs.com/blackmatrix/p/6236573.html,做了部分修改</a></p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>随着项目业务需求的不断变更,数据库的表结构修改难以避免,此时就需要对数据库的修改加以记录和控制,便于项目的版本管理和随意的升级和降级。</p>
<p>Alembic 就可以很好的解决这个问题。Alembic 是 SQLAlchemy 作者开发的 Python 数据库版本管理工具。<br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Python" scheme="http://me.iblogc.com/tags/python/"/>
<category term="教程" scheme="http://me.iblogc.com/tags/%E6%95%99%E7%A8%8B/"/>
</entry>
<entry>
<title>Django REST framework单元测试「Unit Testing」</title>
<link href="http://me.iblogc.com/2017/09/05/django-rest-framework%E6%8E%A5%E5%8F%A3%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/"/>
<id>http://me.iblogc.com/2017/09/05/django-rest-framework接口单元测试/</id>
<published>2017-09-05T15:23:41.000Z</published>
<updated>2020-01-17T07:57:22.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span></p><h2 id="settings"><a href="#settings" class="headerlink" title="settings"></a><code>settings</code></h2><p><code>Django</code>运行单元测试时,会以<code>settings</code>里的数据库配置里的<code>NAME</code>新建一个以<code>test_</code>开关的临时数据库,并在测试结束后删除,默认的测试数据库会以当前的<code>migrations</code>文件来创建数据表并进行迁移,但如果<code>migrations</code>文件很多,每次运行时间将很久,所以可以跳过迁移,直接以当前<code>Model</code>结果来创建表以提升测试效率,如果想进一步加快测试时创建数据库的速度,可以使用<code>SQLite</code>数据库引擎,当使用<code>SQLite</code>数据库引擎时,测试将默认使用内存数据库。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">TESTING = <span class="built_in">len</span>(sys.argv) > <span class="number">1</span> <span class="keyword">and</span> sys.argv[<span class="number">1</span>] == <span class="string">'test'</span></span><br><span class="line"><span class="keyword">if</span> TESTING:</span><br><span class="line"> <span class="comment"># 当使用SQLite数据库引擎时,测试将默认使用内存数据库</span></span><br><span class="line"> DATABASES[<span class="string">'default'</span>] = {</span><br><span class="line"> <span class="string">'ENGINE'</span>: <span class="string">'django.db.backends.sqlite3'</span>,</span><br><span class="line"> }</span><br><span class="line"> <span class="comment"># 单元测试时, 跳过migrate, 极 的提升测试运 效率</span></span><br><span class="line"> <span class="comment"># 具体可以查看</span></span><br><span class="line"> <span class="comment"># https://simpleisbetterthancomplex.com/tips/2016/08/19/django-tip-12-disabl ing-migrations-to-speed-up-unit-tests.html</span></span><br><span class="line"> <span class="comment"># https://stackoverflow.com/questions/36487961/django-unit-testing-taking-a- very-long-time-to-create-test-database</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">DisableMigrations</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__contains__</span>(<span class="params">self, item</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__getitem__</span>(<span class="params">self, item</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"notmigrations"</span></span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> MIGRATION_MODULES = DisableMigrations()</span><br></pre></td></tr></table></figure></p><h2 id="示例代码"><a href="#示例代码" class="headerlink" title="示例代码"></a>示例代码</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> absolute_import</span><br><span class="line"><span class="keyword">from</span> __future__ <span class="keyword">import</span> unicode_literals</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> rest_framework <span class="keyword">import</span> status</span><br><span class="line"><span class="keyword">from</span> rest_framework.test <span class="keyword">import</span> APITestCase</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> apps.account.models <span class="keyword">import</span> User</span><br><span class="line"><span class="keyword">from</span> apps.account.tests.test_utils <span class="keyword">import</span> TestCaseUtils</span><br><span class="line"></span><br><span class="line">__author__ = <span class="string">'jeff'</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserAPITests</span>(APITestCase, TestCaseUtils):</span><br><span class="line"> <span class="comment"># 初始数据加载,可使用manage.py dumpdata [app_label app_label app_label.Model]生成</span></span><br><span class="line"> <span class="comment"># xml/yaml/json格式的数据</span></span><br><span class="line"> <span class="comment"># 一般放在每个应用的fixtures目录下, 只需要填写json文件名即可,django会自动查找</span></span><br><span class="line"> <span class="comment"># 此测试类运行结束后,会自动从数据库里销毁这份数据</span></span><br><span class="line"> <span class="comment"># fixtures = ['user.json']</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">setUp</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="comment"># 在类里每个测试方法执行前会运行</span></span><br><span class="line"> <span class="comment"># 在此方法执行前,django会运行以下操作</span></span><br><span class="line"> <span class="comment"># 1. 重置数据库,数据库恢复到执行migrate后的状态</span></span><br><span class="line"> <span class="comment"># 2. 加载fixtures数据</span></span><br><span class="line"> <span class="comment"># 所以每个测试方法里对数据库的操作都是独立的,不会相互影响</span></span><br><span class="line"> kwargs = <span class="built_in">dict</span>(mobile_phone=<span class="string">'15999999999'</span>, password=<span class="string">'111111'</span>)</span><br><span class="line"> self.user = User.app_user_objects.create(**kwargs)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">tearDown</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="comment"># 在类里每个方法结束执行后会运行</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"><span class="meta"> @classmethod</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">setUpClass</span>(<span class="params">cls</span>):</span><br><span class="line"> <span class="comment"># 在类初始化时执行,必须调用super</span></span><br><span class="line"> <span class="built_in">super</span>(UserAPITests, cls).setUpClass()</span><br><span class="line"> cls.token = <span class="string">''</span></span><br><span class="line"></span><br><span class="line"><span class="meta"> @classmethod</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">tearDownClass</span>(<span class="params">cls</span>):</span><br><span class="line"> <span class="comment"># 在整个测试类运行结束时执行,必须调用super</span></span><br><span class="line"> <span class="built_in">super</span>(UserAPITests, cls).tearDownClass()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">test_app_user_login_success</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="string">"""APP用户登录接口成功情况"""</span></span><br><span class="line"> <span class="comment"># path使用硬编码,不要使用reverse反解析url,以便在修改url之后能及时发现接口地址变化,并通知接口使用人员</span></span><br><span class="line"> path = <span class="string">'/api/api-token-auth/'</span></span><br><span class="line"> data = {<span class="string">'mobile_phone'</span>: <span class="string">'15999999999'</span>, <span class="string">'password'</span>: <span class="string">'111111'</span>}</span><br><span class="line"> response = self.client.post(path, data)</span><br><span class="line"> <span class="comment"># response.data是字典对象</span></span><br><span class="line"> <span class="comment"># response.content是json字符串对象</span></span><br><span class="line"> self.assertEquals(response.status_code,</span><br><span class="line"> status.HTTP_200_OK,</span><br><span class="line"> <span class="string">'登录接口返回状态码错误: 错误信息: {}'</span>.<span class="built_in">format</span>(response.content))</span><br><span class="line"> self.assertIn(<span class="string">'token'</span>, response.data, <span class="string">'登录成功后无token返回'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">test_app_user_login_with_error_pwd</span>(<span class="params">self</span>):</span><br><span class="line"> path = <span class="string">'/api/api-token-auth/'</span></span><br><span class="line"> data = {<span class="string">'mobile_phone'</span>: <span class="string">'15999999999'</span>, <span class="string">'password'</span>: <span class="string">'123456'</span>}</span><br><span class="line"> response = self.client.post(path, data)</span><br><span class="line"> self.assertEquals(response.status_code, status.HTTP_400_BAD_REQUEST)</span><br><span class="line"> self.assertJSONEqual(<span class="string">'{"errors":["用户名或密码错误。"]}'</span>, response.content)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">test_get_app_user_profile_success</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="string">"""成功获取app用户个人信息接口"""</span></span><br><span class="line"> path = <span class="string">'/api/account/user/profile/'</span></span><br><span class="line"> headers = self.get_headers(user=self.user)</span><br><span class="line"> response = self.client.get(path, **headers)</span><br><span class="line"> <span class="comment"># 校验一些关键数据即可</span></span><br><span class="line"> <span class="comment"># 如果是创建新数据,不仅要校验返回的状态码和数据,</span></span><br><span class="line"> <span class="comment"># 还需要到使用Django ORM去数据库查询数据是否创建成功</span></span><br><span class="line"> self.assertEqual(response.status_code, status.HTTP_200_OK)</span><br><span class="line"> self.assertEqual(<span class="number">6</span>, <span class="built_in">len</span>(response.data))</span><br><span class="line"> self.assertIn(<span class="string">'url'</span>, response.data)</span><br><span class="line"> self.assertIn(<span class="string">'mobile_phone'</span>, response.data)</span><br><span class="line"> self.assertIn(<span class="string">'avatar'</span>, response.data)</span><br><span class="line"> self.assertIn(<span class="string">'company_name'</span>, response.data)</span><br><span class="line"> self.assertIn(<span class="string">'username'</span>, response.data)</span><br><span class="line"> self.assertIn(<span class="string">'is_inviter'</span>, response.data)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">test_get_app_user_profile_without_token</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="string">"""不传token请求获取用户信息接口"""</span></span><br><span class="line"> path = <span class="string">'/api/account/user/profile/'</span></span><br><span class="line"> response = self.client.get(path)</span><br><span class="line"> self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)</span><br></pre></td></tr></table></figure><h2 id="断言"><a href="#断言" class="headerlink" title="断言"></a>断言</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 来自unittest.case.TestCase</span></span><br><span class="line">assertFalse(expr, msg=<span class="literal">None</span>)</span><br><span class="line">assertTrue(expr, msg=<span class="literal">None</span>)</span><br><span class="line">assertEqual(first, second, msg=<span class="literal">None</span>)</span><br><span class="line">assertNotEqual(first, second, msg=<span class="literal">None</span>)</span><br><span class="line">assertAlmostEqual(first, second, places=<span class="literal">None</span>, msg=<span class="literal">None</span>, delta=<span class="literal">None</span>)</span><br><span class="line">assertNotAlmostEqual(first, second, places=<span class="literal">None</span>, msg=<span class="literal">None</span>, delta=<span class="literal">None</span>)</span><br><span class="line">assertSequenceEqual(seq1, seq2, msg=<span class="literal">None</span>, seq_type=<span class="literal">None</span>)</span><br><span class="line">assertListEqual(list1, list2, msg=<span class="literal">None</span>)</span><br><span class="line">assertTupleEqual(tuple1, tuple2, msg=<span class="literal">None</span>)</span><br><span class="line">assertSetEqual(set1, set2, msg=<span class="literal">None</span>)</span><br><span class="line">assertIn(member, container, msg=<span class="literal">None</span>)</span><br><span class="line">assertNotIn(member, container, msg=<span class="literal">None</span>)</span><br><span class="line">assertIs(expr1, expr2, msg=<span class="literal">None</span>)</span><br><span class="line">assertIsNot(expr1, expr2, msg=<span class="literal">None</span>)</span><br><span class="line">assertDictEqual(d1, d2, msg=<span class="literal">None</span>)</span><br><span class="line">assertDictContainsSubset(expected, actual, msg=<span class="literal">None</span>)</span><br><span class="line">assertItemsEqual(expected_seq, actual_seq, msg=<span class="literal">None</span>)</span><br><span class="line">assertMultiLineEqual(first, second, msg=<span class="literal">None</span>)</span><br><span class="line">assertLess(a, b, msg=<span class="literal">None</span>)</span><br><span class="line">assertLessEqual(a, b, msg=<span class="literal">None</span>)</span><br><span class="line">assertGreater(a, b, msg=<span class="literal">None</span>)</span><br><span class="line">assertGreaterEqual(a, b, msg=<span class="literal">None</span>)</span><br><span class="line">assertIsNone(obj, msg=<span class="literal">None</span>)</span><br><span class="line">assertIsInstance(obj, cls, msg=<span class="literal">None</span>)</span><br><span class="line">assertNotIsInstance(obj, cls, msg=<span class="literal">None</span>)</span><br><span class="line">assertRaisesRegexp(expected_exception, expected_regexp,</span><br><span class="line"> callable_obj=<span class="literal">None</span>, *args, **kwargs)</span><br><span class="line">assertRegexpMatches(text, expected_regexp, msg=<span class="literal">None</span>)</span><br><span class="line">assertNotRegexpMatches(text, unexpected_regexp, msg=<span class="literal">None</span>)</span><br></pre></td></tr></table></figure><h2 id="测试接口地址"><a href="#测试接口地址" class="headerlink" title="测试接口地址"></a>测试接口地址</h2><p>测试接口地址建议使用硬编码,不要使用<code>reverse</code>反解析url,原因是接口地址尽量避免改变,如果必须修改,需要以很明显的方式来提醒开发人员以便开发人员通知接口使用人员。</p><h2 id="测试数据准备"><a href="#测试数据准备" class="headerlink" title="测试数据准备"></a>测试数据准备</h2><p>有如下两种方法准备测试数据</p><ol><li>简单的数据可以在<code>setUp()</code>里来创建;</li><li>复杂数据可以使用fixtures来写,并在赋值给测试类的<code>fixtures</code>属性;<br>fixtures数据示例<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"model"</span><span class="punctuation">:</span> <span class="string">"myapp.person"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"pk"</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"fields"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"first_name"</span><span class="punctuation">:</span> <span class="string">"John"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"last_name"</span><span class="punctuation">:</span> <span class="string">"Lennon"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span><span class="punctuation">,</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"model"</span><span class="punctuation">:</span> <span class="string">"myapp.person"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"pk"</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"fields"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"first_name"</span><span class="punctuation">:</span> <span class="string">"Paul"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"last_name"</span><span class="punctuation">:</span> <span class="string">"McCartney"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure></li></ol><h2 id="测试覆盖率(coverage)"><a href="#测试覆盖率(coverage)" class="headerlink" title="测试覆盖率(coverage)"></a>测试覆盖率(coverage)</h2><p>在<code>Pycharm</code>里可以通用右键项目,选择<code>Run 'Test:' with Coverage</code>来查看测试的覆盖率。也可以通过其它第三方包查看测试覆盖率,具体请自己查询。</p>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Django" scheme="http://me.iblogc.com/tags/django/"/>
<category term="restful api" scheme="http://me.iblogc.com/tags/restful-api/"/>
<category term="接口" scheme="http://me.iblogc.com/tags/%E6%8E%A5%E5%8F%A3/"/>
<category term="单元测试" scheme="http://me.iblogc.com/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/"/>
<category term="测试" scheme="http://me.iblogc.com/tags/%E6%B5%8B%E8%AF%95/"/>
</entry>
<entry>
<title>在macOS上更改Jenkins的默认用户,解决权限问题</title>
<link href="http://me.iblogc.com/2017/08/24/%E5%9C%A8macos%E4%B8%8A%E6%9B%B4%E6%94%B9jenkins%E9%BB%98%E8%AE%A4%E7%94%A8%E6%88%B7%E8%A7%A3%E5%86%B3%E6%9D%83%E9%99%90%E9%97%AE%E9%A2%98/"/>
<id>http://me.iblogc.com/2017/08/24/在macos上更改jenkins默认用户解决权限问题/</id>
<published>2017-08-24T06:29:14.000Z</published>
<updated>2020-08-15T03:08:14.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span><br>在MacOS上使用<code>dmg</code>安装包安装完Jenkins之后,发了Jenkins自动在系统里新建了一个名为<code>jenkins</code>的用户。默认的,Jenkins程序里的自动化构建操作都是以这个用户身份来进行的,所以有时会出现一些权限问题,解决方法就是修改Jenkins配置文件,把Jenkins运行的默认账户改成平时用的账户。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">停止Jenkins</span></span><br><span class="line">sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">修改Group和User</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><用户名>填写你的MacOS用户名,不知道的可以在命令行使用<span class="built_in">whoami</span>查看,不需要尖括号</span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sudo vim +1 +/daemon +’s/daemon/staff/’ +/daemon +’s/daemon/<用户名> +wq org.jenkins-ci.plist</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">可能相应文件夹的权限</span></span><br><span class="line">sudo chown -R <用户名>:staff /Users/Shared/Jenkins/</span><br><span class="line">sudo chown -R <用户名>:staff /var/log/jenkins/</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">启动Jenkins</span></span><br><span class="line">sudo launchctl load /Library/LaunchDaemons/org.jenkins-ci.plist</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="macOS" scheme="http://me.iblogc.com/tags/macos/"/>
<category term="Jenkins" scheme="http://me.iblogc.com/tags/jenkins/"/>
<category term="权限" scheme="http://me.iblogc.com/tags/%E6%9D%83%E9%99%90/"/>
<category term="用户" scheme="http://me.iblogc.com/tags/%E7%94%A8%E6%88%B7/"/>
</entry>
<entry>
<title>内网穿透工具frp客户端自定义子域名访问配置</title>
<link href="http://me.iblogc.com/2017/08/16/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E5%B7%A5%E5%85%B7frp%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AD%90%E5%9F%9F%E5%90%8D%E8%AE%BF%E9%97%AE%E9%85%8D%E7%BD%AE/"/>
<id>http://me.iblogc.com/2017/08/16/内网穿透工具frp客户端自定义子域名访问配置/</id>
<published>2017-08-16T08:36:56.000Z</published>
<updated>2019-04-09T03:26:20.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span></p><h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>A: 公网电脑<br>B: 内网电脑</p><h2 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h2><p>从<a href="[Releases · fatedier/frp · GitHub](https://github.com/fatedier/frp/releases">releases</a>)下载系统对应的压缩包,Mac可使用<code>darwin amd64</code>的包,在公网电脑和本地电脑各放一份。</p><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>公网电脑上<code>frps.ini</code><br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">[common]</span><br><span class="line"># 用于接收 frpc 连接的端口</span><br><span class="line">bind_port = 7000</span><br><span class="line"># 通过此端口访问http服务</span><br><span class="line">vhost_http_port = 8080</span><br><span class="line"># 日志文件输出位置</span><br><span class="line">log_file = ./frps.log</span><br><span class="line"># 日志等级</span><br><span class="line">log_level = info</span><br><span class="line"># 域名</span><br><span class="line">subdomain_host = example.com</span><br><span class="line"># frp管理后台端口</span><br><span class="line">dashboard_port = 7500</span><br><span class="line"># frp管理后台用户名</span><br><span class="line">dashboard_user = admin</span><br><span class="line"># frp管理后台密码</span><br><span class="line">dashboard_pwd = admin</span><br></pre></td></tr></table></figure></p><p>本地电脑上<code>frpc.ini</code><br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[common]</span><br><span class="line"># 公网电脑IP</span><br><span class="line">server_addr = 111.111.111.111</span><br><span class="line"># frp连接的端口</span><br><span class="line">server_port = 7000</span><br><span class="line"></span><br><span class="line">[web]</span><br><span class="line">type = http</span><br><span class="line"># 本地http服务端口</span><br><span class="line">local_port = 8080</span><br><span class="line"># 子域名前缀, 子域名前缀里不要使用下划线"_",不然可能会出现莫名其妙的400错误可以用"-"代替。</span><br><span class="line">subdomain = iblogc</span><br></pre></td></tr></table></figure></p><p>配置域名<code>example.com</code>的A记录的泛解析<br><code>*.example.com</code>指向公网电脑IP<code>111.111.111.111</code></p><h2 id="运行"><a href="#运行" class="headerlink" title="运行"></a>运行</h2><ol><li>在内网电脑B上<code>8080</code>端口运行<code>http</code>服务</li><li>在公网电脑上运行(Windows电脑上运行请去掉<code>./</code>)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./frps -c ./frps.ini</span><br></pre></td></tr></table></figure></li><li>在本地电脑上运行(Windows电脑上运行请去掉<code>./</code>)<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./frpc -c ./frpc.ini</span><br></pre></td></tr></table></figure></li></ol><h2 id="成功"><a href="#成功" class="headerlink" title="成功"></a>成功</h2><p>在任何一台能联网的机器上访问 <code>http://iblogc.example.com:8080</code> 即可访问内网电脑B上的http服务。<br>在任务一台能联网的机器上访问<code>111.111.111.111:7500</code>即可访问frp的管理后台。</p><h2 id="frps服务端与nginx可共用80端口"><a href="#frps服务端与nginx可共用80端口" class="headerlink" title="frps服务端与nginx可共用80端口"></a>frps服务端与nginx可共用80端口</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen 80;</span><br><span class="line"> server_name *.example.com;</span><br><span class="line"> location / {</span><br><span class="line"> proxy_pass http://127.0.0.1:8080;</span><br><span class="line"> proxy_redirect http://$host/ http://$http_host/;</span><br><span class="line"> proxy_set_header X-Real-IP $remote_addr;</span><br><span class="line"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;</span><br><span class="line"> proxy_set_header Host $host;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="教程" scheme="http://me.iblogc.com/tags/%E6%95%99%E7%A8%8B/"/>
<category term="内网穿透" scheme="http://me.iblogc.com/tags/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/"/>
</entry>
<entry>
<title>MacOS远程连接Windows</title>
<link href="http://me.iblogc.com/2017/08/08/mac%E8%BF%9C%E7%A8%8B%E8%BF%9E%E6%8E%A5windows/"/>
<id>http://me.iblogc.com/2017/08/08/mac远程连接windows/</id>
<published>2017-08-08T01:40:28.000Z</published>
<updated>2017-08-16T08:58:43.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span><br>MacOS连接Windows时,除了在Windows上打开远程桌面功能外,还需要修改Windows的组策略才能成功连接。</p><ol><li>开始-运行-gpedit.msc,进入组策略编辑器。</li><li>在左侧边栏中展开,计算机配置-管理模板-Windows组件-远程桌面服务-远程桌面会话主机-安全,修改以下两项。<ul><li>远程(RDP)连接要求使用指定的安全层,改为启用,安全层选择RDP。</li><li>要求使用网络级别的身份验证对远程连接的用户进行身份验证,改为禁用。</li></ul></li><li>关闭组策略编辑器,重试远程,如果不行重启Windows再重试远程即可。</li></ol>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="远程" scheme="http://me.iblogc.com/tags/%E8%BF%9C%E7%A8%8B/"/>
</entry>
<entry>
<title>Django Rest framework入门笔记及跳坑记录</title>
<link href="http://me.iblogc.com/2016/12/17/django-rest-framework%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0%E5%8F%8A%E8%B7%B3%E5%9D%91%E8%AE%B0%E5%BD%95/"/>
<id>http://me.iblogc.com/2016/12/17/django-rest-framework入门笔记及跳坑记录/</id>
<published>2016-12-17T07:03:10.000Z</published>
<updated>2020-08-15T03:05:28.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span></p><h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2><p>2016-01-26 初稿</p><h2 id="序列化时嵌套显示外键关联字段"><a href="#序列化时嵌套显示外键关联字段" class="headerlink" title="序列化时嵌套显示外键关联字段"></a>序列化时嵌套显示外键关联字段</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><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Hospital</span>(models.Model):</span><br><span class="line"> name = models.CharField()</span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HospitalPic</span>(models.Model):</span><br><span class="line"> hospital = models.ForeignKey(Hospital)</span><br></pre></td></tr></table></figure><p>serializers.py</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> rest_framework <span class="keyword">import</span> serializers</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HospitalSerializer</span>(serializers.HyperlinkedModelSerializer):</span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line"> model = Hospital </span><br><span class="line"> fields = <span class="string">'__all__'</span></span><br></pre></td></tr></table></figure></li></ul><p> class HospitalPicSerializer(serializers.HyperlinkedModelSerializer):<br> hospital = HospitalSerializer()</p><pre><code>class Meta: model = HospitalPic fields = '__all__'</code></pre> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">***反向关系嵌套***</span><br><span class="line">在`Hospital`序列化结果里嵌套显示`HospitalPic`</span><br><span class="line">serializers.py</span><br><span class="line"> ```python</span><br><span class="line"> from rest_framework import serializers</span><br><span class="line"> </span><br><span class="line"> class HospitalPicSerializer(serializers.HyperlinkedModelSerializer): </span><br><span class="line"> class Meta:</span><br><span class="line"> model = HospitalPic</span><br><span class="line"> fields = '__all__'</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> class HospitalSerializer(serializers.HyperlinkedModelSerializer):</span><br><span class="line"> hospitalpic_set = HospitalPicSerializer(many=Ture)</span><br><span class="line"> class Meta:</span><br><span class="line"> model = Hospital</span><br><span class="line"> fields = '__all__'</span><br></pre></td></tr></table></figure><h2 id="在序列化对象里添加关联表的字段内容"><a href="#在序列化对象里添加关联表的字段内容" class="headerlink" title="在序列化对象里添加关联表的字段内容"></a>在序列化对象里添加关联表的字段内容</h2><p>定义一个<code>serializer Field</code>,并添加参数<code>source</code>指向外键对对应的字段(<code>source</code>值其实是从当前序列化的实例的属性)<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">my_address= serializers.ReadOnlyField(source=<span class="string">'address.full_address'</span>)</span><br></pre></td></tr></table></figure></p><h2 id="在序列化对象里添加自定义内容"><a href="#在序列化对象里添加自定义内容" class="headerlink" title="在序列化对象里添加自定义内容"></a>在序列化对象里添加自定义内容</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib.auth.models <span class="keyword">import</span> User</span><br><span class="line"><span class="keyword">from</span> django.utils.timezone <span class="keyword">import</span> now</span><br><span class="line"><span class="keyword">from</span> rest_framework <span class="keyword">import</span> serializers</span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UserSerializer</span>(serializers.ModelSerializer):</span><br><span class="line"> days_since_joined = serializers.SerializerMethodField()</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line"> model = User</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get_days_since_joined</span>(<span class="params">self, obj</span>):</span><br><span class="line"> <span class="keyword">return</span> (now() - obj.date_joined).days</span><br></pre></td></tr></table></figure><h2 id="使用ViewSet,并不有设置queryset,而是重写了get-queryset时,需要在router里增加base-name参数(base-name为router为ViewSet注册url时自动添加的name前缀,如果未设置则从ViewSet的queryset里取,使用ViewSet自动生成的url-name为-lt-base-name-gt-list-lt-base-name-gt-detail-等)"><a href="#使用ViewSet,并不有设置queryset,而是重写了get-queryset时,需要在router里增加base-name参数(base-name为router为ViewSet注册url时自动添加的name前缀,如果未设置则从ViewSet的queryset里取,使用ViewSet自动生成的url-name为-lt-base-name-gt-list-lt-base-name-gt-detail-等)" class="headerlink" title="使用ViewSet,并不有设置queryset,而是重写了get_queryset时,需要在router里增加base_name参数(base_name为router为ViewSet注册url时自动添加的name前缀,如果未设置则从ViewSet的queryset里取,使用ViewSet自动生成的url name为<base_name>-list <base_name>-detail 等)"></a>使用<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为<base_name>-list <base_name>-detail 等)</h2><p>views.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ContactViewSet</span>(viewsets.ModelViewSet):</span><br><span class="line"> serializer_class = ContactSerializer</span><br><span class="line"> permission_classes = (permissions.IsAuthenticated,)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get_queryset</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> self.request.user.contact_set.<span class="built_in">all</span>()</span><br></pre></td></tr></table></figure></p><p>urls.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">router.register(<span class="string">r'contact'</span>, ContactViewSet, base_name=<span class="string">'contact'</span>)</span><br></pre></td></tr></table></figure><br>未设置<code>base_name</code>会报下面错误<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.</span><br></pre></td></tr></table></figure></p><h2 id="给api接口的url添加了命名空间namespace"><a href="#给api接口的url添加了命名空间namespace" class="headerlink" title="给api接口的url添加了命名空间namespace"></a>给api接口的url添加了命名空间<code>namespace</code></h2><p>urls.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">url(<span class="string">r'^api/'</span>, include(router.urls, namespace=<span class="string">'api'</span>)),</span><br></pre></td></tr></table></figure><br>需要对<code>HyperlinkedRelatedField</code>字段的参数进行修改<br>serializers.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">HospitalPicSerializer</span>(serializers.HyperlinkedModelSerializer):</span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line"> model = HospitalPic</span><br><span class="line"> fields = <span class="string">'__all__'</span></span><br><span class="line"> extra_kwargs = {</span><br><span class="line"> <span class="string">'url'</span>: {<span class="string">'view_name'</span>: <span class="string">'api:hospitalpic-detail'</span>},</span><br><span class="line"> <span class="string">'hospital'</span>: {<span class="string">'view_name'</span>: <span class="string">'api:hospital-detail'</span>}</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></p><p>不然会出现以下错误<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Could <span class="keyword">not</span> resolve URL <span class="keyword">for</span> hyperlinked relationship using view name <span class="string">"user-detail"</span>. You may have failed to include the related model <span class="keyword">in</span> your API, <span class="keyword">or</span> incorrectly configured the `lookup_field` attribute on this field.</span><br></pre></td></tr></table></figure><br>不过话说我们全api的url加<code>namespace</code>一般是为了版本控制,所以有一种简单的方法,只要在settings.py添加基于<code>namespace</code>的版本控制,这样就不需要修改<code>HyperlinkedRelatedField</code>字段的<code>view_name</code>了<br>urls.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">url(<span class="string">r'^api/v1/'</span>, include(router.urls, namespace=<span class="string">'v1'</span>)),</span><br><span class="line">url(<span class="string">r'^api/v2/'</span>, include(router.urls, namespace=<span class="string">'v2'</span>)),</span><br></pre></td></tr></table></figure><br>settings.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">REST_FRAMEWORK = {</span><br><span class="line"> ……</span><br><span class="line"> <span class="string">'DEFAULT_VERSIONING_CLASS'</span>: <span class="string">'rest_framework.versioning.NamespaceVersioning'</span>,</span><br><span class="line"> ……</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="要drf的错误提示为中文,需要设置"><a href="#要drf的错误提示为中文,需要设置" class="headerlink" title="要drf的错误提示为中文,需要设置"></a>要drf的错误提示为中文,需要设置</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LANGUAGE_CODE = <span class="string">'zh-CN'</span></span><br></pre></td></tr></table></figure><p>如果设置为<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LANGUAGE_CODE = <span class="string">'zh-Hans'</span></span><br></pre></td></tr></table></figure><br>虽然django默认表单错误会输出中文,但drf还是输出英文</p><h2 id="django的validators可以直接在drf中使用,不需要做任何修改"><a href="#django的validators可以直接在drf中使用,不需要做任何修改" class="headerlink" title="django的validators可以直接在drf中使用,不需要做任何修改"></a>django的<code>validators</code>可以直接在drf中使用,不需要做任何修改</h2><h2 id="当字段里的属性editable-False时,ModelSerializer里该字段会抛弃model里显式和隐式(unique)的所有validators"><a href="#当字段里的属性editable-False时,ModelSerializer里该字段会抛弃model里显式和隐式(unique)的所有validators" class="headerlink" title="当字段里的属性editable=False时,ModelSerializer里该字段会抛弃model里显式和隐式(unique)的所有validators"></a>当字段里的属性<code>editable=False</code>时,<code>ModelSerializer</code>里该字段会抛弃<code>model</code>里显式和隐式(unique)的所有<code>validators</code></h2><h2 id="Serializer里write-only写在field里和写在extra-kwargs里是有区别的,"><a href="#Serializer里write-only写在field里和写在extra-kwargs里是有区别的," class="headerlink" title="Serializer里write_only写在field里和写在extra_kwargs里是有区别的,"></a><code>Serializer</code>里<code>write_only</code>写在<code>field</code>里和写在<code>extra_kwargs</code>里是有区别的,</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">UserRegisterSerializer</span>(serializers.ModelSerializer):</span><br><span class="line"> <span class="string">"""用户注册Serializer"""</span></span><br><span class="line"> </span><br><span class="line"> code = serializers.CharField(min_length=<span class="number">4</span>, max_length=<span class="number">6</span>, label=_(<span class="string">'验证码'</span>),</span><br><span class="line"> help_text=_(<span class="string">'验证码'</span>), write_only=<span class="literal">True</span>)</span><br><span class="line"> re_password = serializers.CharField(label=_(<span class="string">'重复密码'</span>), help_text=_(<span class="string">'重复密码'</span>),</span><br><span class="line"> validators=validators.password_validators(),</span><br><span class="line"> write_only=<span class="literal">True</span>)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line"> model = User</span><br><span class="line"> fields = (<span class="string">'mobile_phone'</span>, <span class="string">'code'</span>, <span class="string">'password'</span>, <span class="string">'re_password'</span>)</span><br><span class="line"> extra_kwargs = {<span class="string">'password'</span>:</span><br><span class="line"> {<span class="string">'write_only'</span>: <span class="literal">True</span>}</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">validate</span>(<span class="params">self, attrs</span>):</span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> Check that the start is before the stop.</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="keyword">if</span> attrs[<span class="string">'password'</span>] != attrs[<span class="string">'re_password'</span>]:</span><br><span class="line"> <span class="keyword">raise</span> serializers.ValidationError(_(<span class="string">'密码不一致'</span>))</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 校验验证码</span></span><br><span class="line"> verify_result = Sms(attrs[<span class="string">'mobile_phone'</span>]).verify_sms_code(</span><br><span class="line"> attrs.pop(<span class="string">'code'</span>))</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> verify_result:</span><br><span class="line"> error = verify_result.get(<span class="string">'error'</span>)</span><br><span class="line"> <span class="keyword">raise</span> ParseError(error)</span><br><span class="line"> <span class="keyword">return</span> attrs</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">create</span>(<span class="params">self, validated_data</span>):</span><br><span class="line"> user = User(</span><br><span class="line"> username=validated_data[<span class="string">'mobile_phone'</span>],</span><br><span class="line"> mobile_phone=validated_data[<span class="string">'mobile_phone'</span>],</span><br><span class="line"> )</span><br><span class="line"> user.set_password(validated_data[<span class="string">'password'</span>])</span><br><span class="line"> user.save()</span><br><span class="line"> <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure><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>参数里,不然会报以下错误<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">AttributeError: Got AttributeError when attempting to get a value for field `code` on serializer `UserRegisterSerializer`.</span><br><span class="line">The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.</span><br><span class="line">Original exception text was: 'User' object has no attribute 'code'.</span><br></pre></td></tr></table></figure></p><h2 id="如果使用django-rest-swagger报以下错误"><a href="#如果使用django-rest-swagger报以下错误" class="headerlink" title="如果使用django-rest-swagger报以下错误"></a>如果使用<code>django-rest-swagger</code>报以下错误</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Can't read from server. It may not have the appropriate access-control-origin settings.</span><br></pre></td></tr></table></figure><p>注释掉设置里的<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 'base_path': '127.0.0.1:8000/docs',</span></span><br></pre></td></tr></table></figure></p><h2 id="serializer-data和serializer-validated-data"><a href="#serializer-data和serializer-validated-data" class="headerlink" title="serializer.data和serializer.validated_data"></a><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里获取原始请求信息"><a href="#在serializer里获取原始请求信息" class="headerlink" title="在serializer里获取原始请求信息"></a>在<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字段"><a href="#自定义serializer字段" class="headerlink" title="自定义serializer字段"></a>自定义<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>方法指定<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">QiNiuField</span>(serializers.Field):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get_attribute</span>(<span class="params">self, instance</span>):</span><br><span class="line"> <span class="comment"># (序列化时)从模型实例中取一个值给这个字段处理,也可以使用`source`参数指定</span></span><br><span class="line"> <span class="keyword">return</span> instance.key</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get_value</span>(<span class="params">self, dictionary</span>):</span><br><span class="line"> <span class="comment"># (反序列化时)从传入数据中提取一个值给这个字段处理</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">super</span>(QiNiuField, self).get_value(dictionary)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">to_representation</span>(<span class="params">self, value</span>):</span><br><span class="line"> <span class="comment"># (序列化时)处理出来的数据用来序列化显示</span></span><br><span class="line"> <span class="keyword">return</span> value.url</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">to_internal_value</span>(<span class="params">self, data</span>):</span><br><span class="line"> <span class="comment"># (反序列化时)处理接收到的数据</span></span><br><span class="line"> <span class="keyword">return</span> data[<span class="string">'key'</span>]</span><br></pre></td></tr></table></figure></p><h2 id="嵌套序列化,传参问题"><a href="#嵌套序列化,传参问题" class="headerlink" title="嵌套序列化,传参问题"></a>嵌套序列化,传参问题</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>"user.email":"foobar"</code>.</p>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="Django" scheme="http://me.iblogc.com/tags/django/"/>
<category term="restful" scheme="http://me.iblogc.com/tags/restful/"/>
<category term="api" scheme="http://me.iblogc.com/tags/api/"/>
<category term="问题" scheme="http://me.iblogc.com/tags/%E9%97%AE%E9%A2%98/"/>
</entry>
<entry>
<title>Django Rest framework使用问题及解决方法</title>
<link href="http://me.iblogc.com/2016/12/17/django-rest-framework%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98%E5%8F%8A%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/"/>
<id>http://me.iblogc.com/2016/12/17/django-rest-framework使用问题及解决方法/</id>
<published>2016-12-17T06:58:04.000Z</published>
<updated>2020-08-15T03:05:35.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span></p><h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2><p>2016-01-29 初稿</p><h2 id="问题1"><a href="#问题1" class="headerlink" title="问题1"></a>问题1</h2><p><code>ViewSet</code>没有写<code>serializer_class</code>属性,而是重写了<code>get_serializer_class()</code>方法,出现<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cannot use OrderingFilter on a view which does not have either a 'serializer_class' or 'ordering_fields' attribute.</span><br></pre></td></tr></table></figure><br>原因:因为启用了<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"><a href="#问题2" class="headerlink" title="问题2"></a>问题2</h2><p><code>ViewSet</code>没有写<code>queryset</code>属性,而是重写了<code>get_queryset()</code>方法,出现<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.</span><br></pre></td></tr></table></figure><br>解决方法:需要在<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为<base_name>-list <base_name>-detail 等)<br>urls.py<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">router.register(r'users', UserViewSet, base_name='user')</span><br></pre></td></tr></table></figure></p><h2 id="问题3"><a href="#问题3" class="headerlink" title="问题3"></a>问题3</h2><p>给url设置了<code>namespace</code><br>urls.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">url(<span class="string">r'^api/'</span>, include(router.urls, namespace=<span class="string">'api'</span>)),</span><br></pre></td></tr></table></figure><br>访问部分接口出现<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.</span><br></pre></td></tr></table></figure><br>解决方法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<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ContactSerializer</span>(serializers.HyperlinkedModelSerializer):</span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line"> model = Contact</span><br><span class="line"> fields = <span class="string">'__all__'</span></span><br><span class="line"> extra_kwargs = {</span><br><span class="line"> <span class="string">'url'</span>: {<span class="string">'view_name'</span>: <span class="string">'api:contact-detail'</span>},</span><br><span class="line"> <span class="string">'user'</span>:{<span class="string">'view_name'</span>:<span class="string">'api:user-detail'</span>}</span><br><span class="line"> } </span><br></pre></td></tr></table></figure><br>解决方法2:启动drf基于<code>NameSpace</code>的版本控制<br>settings.py<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">REST_FRAMEWORK = {</span><br><span class="line"> ……</span><br><span class="line"> <span class="string">'DEFAULT_VERSIONING_CLASS'</span>: <span class="string">'rest_framework.versioning.NamespaceVersioning'</span>,</span><br><span class="line"> ……</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="Django" scheme="http://me.iblogc.com/tags/django/"/>
<category term="restful" scheme="http://me.iblogc.com/tags/restful/"/>
<category term="api" scheme="http://me.iblogc.com/tags/api/"/>
<category term="问题" scheme="http://me.iblogc.com/tags/%E9%97%AE%E9%A2%98/"/>
</entry>
<entry>
<title>Django Rest framework里的API请求频率控制</title>
<link href="http://me.iblogc.com/2016/12/17/django-rest-framework%E9%87%8C%E7%9A%84api%E8%AF%B7%E6%B1%82%E9%A2%91%E7%8E%87%E6%8E%A7%E5%88%B6/"/>
<id>http://me.iblogc.com/2016/12/17/django-rest-framework里的api请求频率控制/</id>
<published>2016-12-17T06:48:19.000Z</published>
<updated>2020-08-15T03:05:17.000Z</updated>
<content type="html"><![CDATA[<p><br /><br><span id="more"></span></p><h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2><p>2016-08-25 初稿</p><p><code>Django Rest framework</code>有自带的频率控制配置</p><h2 id="全局设置"><a href="#全局设置" class="headerlink" title="全局设置"></a>全局设置</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">REST_FRAMEWORK = {</span><br><span class="line"> <span class="string">'DEFAULT_THROTTLE_CLASSES'</span>: (</span><br><span class="line"> <span class="comment"># 开启匿名用户接口请求频率限制</span></span><br><span class="line"> <span class="string">'rest_framework.throttling.AnonRateThrottle'</span>,</span><br><span class="line"> <span class="comment"># 开启授权用户接口请求频率限制</span></span><br><span class="line"> <span class="string">'rest_framework.throttling.UserRateThrottle'</span></span><br><span class="line"> ),</span><br><span class="line"> <span class="string">'DEFAULT_THROTTLE_RATES'</span>: {</span><br><span class="line"> <span class="comment"># 频率限制有second, minute, hour, day</span></span><br><span class="line"> <span class="comment"># 匿名用户请求频率</span></span><br><span class="line"> <span class="string">'anon'</span>: <span class="string">'100/day'</span>,</span><br><span class="line"> <span class="comment"># 授权用户请求频率</span></span><br><span class="line"> <span class="string">'user'</span>: <span class="string">'1000/day'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="类视图单独配置"><a href="#类视图单独配置" class="headerlink" title="类视图单独配置"></a>类视图单独配置</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> rest_framework.response <span class="keyword">import</span> Response</span><br><span class="line"><span class="keyword">from</span> rest_framework.throttling <span class="keyword">import</span> UserRateThrottle</span><br><span class="line"><span class="keyword">from</span> rest_framework.views <span class="keyword">import</span> APIView</span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ExampleView</span>(<span class="title class_ inherited__">APIView</span>):</span><br><span class="line"> throttle_classes = (UserRateThrottle,)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self, request, <span class="built_in">format</span>=<span class="literal">None</span></span>):</span><br><span class="line"> content = {</span><br><span class="line"> <span class="string">'status'</span>: <span class="string">'request was permitted'</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Response(content)</span><br></pre></td></tr></table></figure><h2 id="方法视图配置"><a href="#方法视图配置" class="headerlink" title="方法视图配置"></a>方法视图配置</h2><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@api_view(<span class="params">[<span class="string">'GET'</span>]</span>)</span></span><br><span class="line"><span class="meta">@throttle_classes(<span class="params">[UserRateThrottle]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">example_view</span>(<span class="params">request, <span class="built_in">format</span>=<span class="literal">None</span></span>):</span><br><span class="line"> content = {</span><br><span class="line"> <span class="string">'status'</span>: <span class="string">'request was permitted'</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Response(content)</span><br></pre></td></tr></table></figure><h2 id="自定义"><a href="#自定义" class="headerlink" title="自定义"></a>自定义</h2><p>方法一:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BurstRateThrottle</span>(<span class="title class_ inherited__">UserRateThrottle</span>):</span><br><span class="line"> scope = <span class="string">'burst'</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SustainedRateThrottle</span>(<span class="title class_ inherited__">UserRateThrottle</span>):</span><br><span class="line"> scope = <span class="string">'sustained'</span></span><br><span class="line">...<span class="keyword">and</span> the following settings.</span><br></pre></td></tr></table></figure></p><p><code>settings.py</code><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">REST_FRAMEWORK = {</span><br><span class="line"> <span class="string">'DEFAULT_THROTTLE_CLASSES'</span>: (</span><br><span class="line"> <span class="string">'example.throttles.BurstRateThrottle'</span>,</span><br><span class="line"> <span class="string">'example.throttles.SustainedRateThrottle'</span></span><br><span class="line"> ),</span><br><span class="line"> <span class="string">'DEFAULT_THROTTLE_RATES'</span>: {</span><br><span class="line"> <span class="string">'burst'</span>: <span class="string">'60/min'</span>,</span><br><span class="line"> <span class="string">'sustained'</span>: <span class="string">'1000/day'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>然后在视图里设置<code>throttle_classes</code>即可。</p><p>方法二:<br><code>settings.py</code><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">REST_FRAMEWORK = {</span><br><span class="line"> <span class="string">'DEFAULT_THROTTLE_CLASSES'</span>: (</span><br><span class="line"> <span class="string">'rest_framework.throttling.ScopedRateThrottle'</span>,</span><br><span class="line"> ),</span><br><span class="line"> <span class="string">'DEFAULT_THROTTLE_RATES'</span>: {</span><br><span class="line"> <span class="string">'contacts'</span>: <span class="string">'1000/day'</span>,</span><br><span class="line"> <span class="string">'uploads'</span>: <span class="string">'20/day'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>然后在类视图中设置<code>throttle_scope</code><br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ContactListView</span>(<span class="title class_ inherited__">APIView</span>):</span><br><span class="line"> throttle_scope = <span class="string">'contacts'</span></span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ContactDetailView</span>(<span class="title class_ inherited__">APIView</span>):</span><br><span class="line"> throttle_scope = <span class="string">'contacts'</span></span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"><span class="keyword">class</span> <span class="title class_">UploadView</span>(<span class="title class_ inherited__">APIView</span>):</span><br><span class="line"> throttle_scope = <span class="string">'uploads'</span></span><br><span class="line"> ...</span><br></pre></td></tr></table></figure></p><p><strong>1. 匿名用户频率如果设置大于授权用户频率,则以授权用户频率为准。</strong><br><strong>2. 频率限制是针对单个接口的频率,而不是所有接口的频率。</strong></p>]]></content>
<summary type="html">
<p><br /><br>
</summary>
<category term="Django" scheme="http://me.iblogc.com/tags/django/"/>
<category term="restful" scheme="http://me.iblogc.com/tags/restful/"/>
<category term="api, version" scheme="http://me.iblogc.com/tags/api%EF%BC%8C-version/"/>
</entry>
<entry>
<title>在CentOS7上用MySQL+Nginx+Gunicorn+Supervisor部署Django</title>
<link href="http://me.iblogc.com/2016/12/08/%E5%9C%A8centos7%E4%BD%BF%E7%94%A8mysql-nginx-gunicorn+supervisor%E9%83%A8%E7%BD%B2django/"/>
<id>http://me.iblogc.com/2016/12/08/在centos7使用mysql-nginx-gunicorn+supervisor部署django/</id>
<published>2016-12-08T15:19:59.000Z</published>
<updated>2020-08-15T03:02:43.000Z</updated>
<content type="html"><![CDATA[<p>本文记录下在CentOS下部署Django项目的步骤。<br><span id="more"></span></p><h2 id="MySQL"><a href="#MySQL" class="headerlink" title="MySQL"></a>MySQL</h2><h3 id="安装mysql和mysql-devel"><a href="#安装mysql和mysql-devel" class="headerlink" title="安装mysql和mysql-devel"></a>安装mysql和mysql-devel</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">yum install mysql</span><br><span class="line">yum install mysql-devel</span><br></pre></td></tr></table></figure><h3 id="安装mysql-server"><a href="#安装mysql-server" class="headerlink" title="安装mysql-server"></a>安装mysql-server</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm</span><br><span class="line">rpm -ivh mysql-community-release-el7-5.noarch.rpm</span><br><span class="line">yum install mysql-community-server</span><br></pre></td></tr></table></figure><h3 id="重启mysql服务"><a href="#重启mysql服务" class="headerlink" title="重启mysql服务"></a>重启mysql服务</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service mysqld restart</span><br></pre></td></tr></table></figure><h3 id="设置root密码"><a href="#设置root密码" class="headerlink" title="设置root密码"></a>设置root密码</h3><p>初次安装mysql需要设置root密码<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mysql -uroot</span><br><span class="line">set password for 'root'@'localhost' =password('password');</span><br></pre></td></tr></table></figure></p><h3 id="配置mysql"><a href="#配置mysql" class="headerlink" title="配置mysql"></a>配置mysql</h3><p>在<code>/etc/my.cnf</code>文件中[mysql]和[mysql]中添加以下内容<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[mysql]</span><br><span class="line">default-character-set=utf8</span><br><span class="line"></span><br><span class="line">[mysqld]</span><br><span class="line">character-set-server=utf8</span><br></pre></td></tr></table></figure><br>字符编码保持和<code>/usr/share/mysql/charsets/Index.xml</code>中的一致。</p><h3 id="远程连接设置"><a href="#远程连接设置" class="headerlink" title="远程连接设置"></a>远程连接设置</h3><p>把在所有数据库的所有表的所有权限赋值给位于所有IP地址的root用户。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql> grant all privileges on *.* to root@'%'identified by 'password';</span><br></pre></td></tr></table></figure><br>如果是新用户而不是root,则要先新建用户<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql>create user 'username'@'%' identified by 'password';</span><br></pre></td></tr></table></figure><br>此时就可以进行远程连接了。</p><h2 id="Virtualenv"><a href="#Virtualenv" class="headerlink" title="Virtualenv"></a>Virtualenv</h2><p>安装epel扩展源<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install epel-release</span><br></pre></td></tr></table></figure><br>安装pip<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install python-pip</span><br></pre></td></tr></table></figure><br>安装virtualenv和virtualenvwrapper<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install virtualenv virtualenvwrapper</span><br></pre></td></tr></table></figure><br>编辑<code>~/.bashrc</code>文件,结尾添加以下内容<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export WORKON_HOME=~/.virtualenvs</span><br><span class="line">source /usr/bin/virtualenvwrapper.sh</span><br></pre></td></tr></table></figure><br>然后执行以下命令使配置生效<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><br>创建env<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkvirtualenv explame</span><br></pre></td></tr></table></figure><br>使用pip安装项目需要的包</p><h2 id="WSGI"><a href="#WSGI" class="headerlink" title="WSGI"></a>WSGI</h2><p>在项目目录下新建<code>nginx_wsgi.py</code>文件<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">touch nginx_wsgi.py</span><br></pre></td></tr></table></figure><br>添加如下内容<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">import sys</span><br><span class="line">import site</span><br><span class="line">import os</span><br><span class="line"> </span><br><span class="line"># site-packages</span><br><span class="line">site.addsitedir('/home/nginxuser/.virtualenvs/example/lib/python2.7/site-packages')</span><br><span class="line"># Add the project directory</span><br><span class="line"># sys.path.append('/home/nginxuser/nginxuser')</span><br><span class="line">PROJECT_DIR = '/home/nginxuser/projects/example'</span><br><span class="line">sys.path.insert(0, PROJECT_DIR)</span><br><span class="line">os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings.prod'</span><br><span class="line"># Activate your virtual env</span><br><span class="line">activate_env = os.path.expanduser("/home/nginxuser/.virtualenvs/example/bin/activate_this.py")</span><br><span class="line">execfile(activate_env, dict(__file__=activate_env))</span><br><span class="line"> </span><br><span class="line"># after activite env</span><br><span class="line">from django.core.wsgi import get_wsgi_application</span><br><span class="line"> </span><br><span class="line">application = get_wsgi_application()</span><br></pre></td></tr></table></figure></p><h2 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h2><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install nginx</span><br></pre></td></tr></table></figure><h3 id="检查配置是否有错"><a href="#检查配置是否有错" class="headerlink" title="检查配置是否有错"></a>检查配置是否有错</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nginx -t -c /etc/nginx/nginx.conf</span><br></pre></td></tr></table></figure><h3 id="启动nginx"><a href="#启动nginx" class="headerlink" title="启动nginx"></a>启动nginx</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service nginx start</span><br></pre></td></tr></table></figure><h3 id="设置开机自启"><a href="#设置开机自启" class="headerlink" title="设置开机自启"></a>设置开机自启</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl enable nginx</span><br></pre></td></tr></table></figure><h3 id="创建用户"><a href="#创建用户" class="headerlink" title="创建用户"></a>创建用户</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">useradd nginxuser</span><br><span class="line">passwd nginxuser</span><br></pre></td></tr></table></figure><h3 id="修改nginx主配置"><a href="#修改nginx主配置" class="headerlink" title="修改nginx主配置"></a>修改nginx主配置</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/nginx/nginx.conf</span><br></pre></td></tr></table></figure><p>非注释首行<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">user nginx</span><br></pre></td></tr></table></figure><br>改为<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">user nginxuser</span><br></pre></td></tr></table></figure><br>不然可能会出现网站静态文件访问报403问题。</p><h3 id="新建网站运行配置"><a href="#新建网站运行配置" class="headerlink" title="新建网站运行配置"></a>新建网站运行配置</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/nginx/conf.d/example.conf</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">server { </span><br><span class="line"> listen 80; </span><br><span class="line"> server_name example.com; </span><br><span class="line"> charset utf-8; </span><br><span class="line"> client_max_body_size 75M; </span><br><span class="line"> access_log /home/nginxuser/projects/example/nginxlogs/access.log;</span><br><span class="line"> error_log /home/nginxuser/projects/example/nginxlogs/error.log; </span><br><span class="line"> </span><br><span class="line"> location /static { </span><br><span class="line"> alias /home/nginxuser/projects/explame/static; </span><br><span class="line"> } </span><br><span class="line"> </span><br><span class="line"> location / { </span><br><span class="line"> proxy_pass http://127.0.0.1:8000; </span><br><span class="line"> proxy_set_header Host $host; </span><br><span class="line"> proxy_set_header X-Real-IP $remote_addr; </span><br><span class="line"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; </span><br><span class="line"> } </span><br><span class="line">} jk</span><br></pre></td></tr></table></figure><h2 id="Gunicorn"><a href="#Gunicorn" class="headerlink" title="Gunicorn"></a>Gunicorn</h2><h3 id="安装-1"><a href="#安装-1" class="headerlink" title="安装"></a>安装</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install gunicorn</span><br></pre></td></tr></table></figure><p>项目根目录下添加gunicorn运行配置文件gunicorn.conf.py<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">import multiprocessing</span><br><span class="line">bind = "127.0.0.1:8000"</span><br><span class="line">workers = 2</span><br><span class="line">errorlog = "/home/nginxuser/example/gunicorn.error.log"</span><br><span class="line">#loglevel = "debug"</span><br><span class="line">proc_name = "gunicorn_example"</span><br></pre></td></tr></table></figure></p><h3 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo gunicorn example.nginx_wsgi:application -c /home/nginxuser/projects/example/gunicorn.conf.py</span><br></pre></td></tr></table></figure><p>后台运行<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo nohup gunicorn example.nginx_wsgi:application -c /home/nginxuser/projects/example/gunicorn.conf.py&</span><br></pre></td></tr></table></figure><br>如果运行报错先使用以下命令检查下nginx配置是否有错<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nginx -t -c /etc/nginx/nginx.conf</span><br></pre></td></tr></table></figure></p><h2 id="Supervisor"><a href="#Supervisor" class="headerlink" title="Supervisor"></a>Supervisor</h2><h3 id="安装-2"><a href="#安装-2" class="headerlink" title="安装"></a>安装</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install supervisor</span><br></pre></td></tr></table></figure><h3 id="创建管理进程配置文件"><a href="#创建管理进程配置文件" class="headerlink" title="创建管理进程配置文件"></a>创建管理进程配置文件</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/supervisord.d/example.ini</span><br></pre></td></tr></table></figure><p>(需要注意:用 supervisord 管理时,gunicorn 的 daemon 选项需要设置为 False)<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">[program:example]</span><br><span class="line">directory = /home/nginxuser/projects/example ; 程序的启动目录</span><br><span class="line">command = gunicorn example.nginx_wsgi:application -c /home/nginxuser/projects/example/gunicorn.conf.py ; 启动命令,可以看出与手动在命令行启动的命令是一样的</span><br><span class="line">autostart = true ; 在 supervisord 启动的时候也自动启动</span><br><span class="line">startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了</span><br><span class="line">autorestart = true ; 程序异常退出后自动重启</span><br><span class="line">startretries = 3 ; 启动失败自动重试次数,默认是 3</span><br><span class="line">user = nginx ; 用哪个用户启动</span><br><span class="line">redirect_stderr = true ; 把 stderr 重定向到 stdout,默认 false</span><br><span class="line">stdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MB</span><br><span class="line">stdout_logfile_backups = 20 ; stdout 日志文件备份数</span><br><span class="line">; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)</span><br><span class="line">stdout_logfile = /data/logs/usercenter_stdout.log</span><br><span class="line"></span><br><span class="line">; 可以通过 environment 来添加需要的环境变量,一种常见的用法是修改 PYTHONPATH</span><br><span class="line">; environment=PYTHONPATH=$PYTHONPATH:/path/to/somewhere</span><br></pre></td></tr></table></figure><br><strong>冒号后面要有空格</strong></p><h3 id="启动-1"><a href="#启动-1" class="headerlink" title="启动"></a>启动</h3><p>使用<code>-c</code>指定配置文件。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">supervisord -c /etc/supervisord.conf</span><br></pre></td></tr></table></figure><br>如果启动时遇到以下报错信息<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">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.</span><br><span class="line">For help, use /use/bin/supervisord -h</span><br></pre></td></tr></table></figure><br>可以使用以下命令解决<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo unlink /var/run/supervisor/supervisor.sock</span><br></pre></td></tr></table></figure></p><h3 id="命令行客户端工具supervisorctl"><a href="#命令行客户端工具supervisorctl" class="headerlink" title="命令行客户端工具supervisorctl"></a>命令行客户端工具supervisorctl</h3><p>启动时需要使用和<code>supervisorctl</code>使用一样的配置文件。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">supervisorctl -c /etc/supervisord.conf</span><br></pre></td></tr></table></figure><br>启动后进入<code>supervisorctl</code>的shell,在此shell里可以执行以下命令<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">status # 查看程序状态</span><br><span class="line">start example # 启动example程序</span><br><span class="line">stop example # 关闭example程序</span><br><span class="line">restart example # 重启example程序</span><br><span class="line">reread # 读取有更新(增加)的配置文件,不会启动新添加的程序</span><br><span class="line">update # 重启配置文件修改过的程序</span><br></pre></td></tr></table></figure><br>也可以不进shell执行以上命令<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">supervisorctl status # 查看程序状态</span><br><span class="line">supervisorctl start example # 启动example程序</span><br><span class="line">supervisorctl stop example # 关闭example程序</span><br><span class="line">supervisorctl restart example # 重启example程序</span><br><span class="line">supervisorctl reread # 读取有更新(增加)的配置文件,不会启动新添加的程序</span><br><span class="line">supervisorctl update # 重启配置文件修改过的程序</span><br></pre></td></tr></table></figure></p><h3 id="开启web管理界面"><a href="#开启web管理界面" class="headerlink" title="开启web管理界面"></a>开启web管理界面</h3><p>如果要开启web管理界面,打开<code>/etc/supervisord.conf</code>把下面几行取消注释即可<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">:[inet_http_server] ; inet (TCP) server disabled by default</span><br><span class="line">:port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface)</span><br><span class="line">:username=user ; (default is no username (open server))</span><br><span class="line">:password=123 ; (default is no password (open server))</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p>本文记录下在CentOS下部署Django项目的步骤。<br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="Django" scheme="http://me.iblogc.com/tags/django/"/>
<category term="centOS" scheme="http://me.iblogc.com/tags/centos/"/>
<category term="Nginx" scheme="http://me.iblogc.com/tags/nginx/"/>
<category term="Gunicorn" scheme="http://me.iblogc.com/tags/gunicorn/"/>
<category term="MySQL" scheme="http://me.iblogc.com/tags/mysql/"/>
<category term="Supervisor" scheme="http://me.iblogc.com/tags/supervisor/"/>
</entry>
<entry>
<title>npm入门命令</title>
<link href="http://me.iblogc.com/2016/07/06/npm%E5%85%A5%E9%97%A8%E5%91%BD%E4%BB%A4/"/>
<id>http://me.iblogc.com/2016/07/06/npm入门命令/</id>
<published>2016-07-06T15:46:37.000Z</published>
<updated>2020-08-15T03:07:04.000Z</updated>
<content type="html"><![CDATA[<p><br/><br><span id="more"></span></p><h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2><p>2016-07-06 初稿</p><h2 id="基础命令"><a href="#基础命令" class="headerlink" title="基础命令"></a>基础命令</h2><p>显示npm版号<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">npm -v</span><br><span class="line"># 或</span><br><span class="line">npm version</span><br></pre></td></tr></table></figure></p><p>安装模块<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># 带-g为全局安装</span><br><span class="line"># 本地安装:package会被下载到当前所在目录,也只能在当前目录下使用。</span><br><span class="line"># 全局安装:package会被下载到到特定的系统目录下,安装的package能够在所有目录下使用。</span><br><span class="line">npm install <package> -g</span><br><span class="line"># 简写</span><br><span class="line">npm i <package> -g</span><br></pre></td></tr></table></figure></p><p>升级全局安装的指定模块<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm update <package> -g</span><br></pre></td></tr></table></figure></p><p>升级当前目录下的指定模块<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm update <package></span><br></pre></td></tr></table></figure></p><p>升级当前目录下全部模块<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm update</span><br></pre></td></tr></table></figure></p><p>升级node自身<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># 安装一个叫n的模块</span><br><span class="line">npm install -g n</span><br><span class="line"># 升级到最新稳定版</span><br><span class="line">n stable</span><br><span class="line"># 升级到指定版本</span><br><span class="line">n v0.10.26</span><br></pre></td></tr></table></figure></p><p>卸载移除指定模块<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm uninstall <package></span><br><span class="line"># 别名:remove, rm, r, un, unlink</span><br></pre></td></tr></table></figure></p><p>显示已安装模块<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm list</span><br></pre></td></tr></table></figure></p><p>显示模块详细信息<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm show <package></span><br></pre></td></tr></table></figure></p><p>查看全局包安装路径<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm root -g</span><br></pre></td></tr></table></figure></p><p>查看当前包安装路径<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm root</span><br></pre></td></tr></table></figure></p><p>查看npm配置<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config list</span><br></pre></td></tr></table></figure><br>查看帮助<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm help</span><br></pre></td></tr></table></figure></p><p>查看相关命令的帮助文档<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm help <command></span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p><br/><br>
</summary>
<category term="程序猿" scheme="http://me.iblogc.com/categories/%E7%A8%8B%E5%BA%8F%E7%8C%BF/"/>
<category term="npm" scheme="http://me.iblogc.com/tags/npm/"/>
<category term="Nodejs" scheme="http://me.iblogc.com/tags/nodejs/"/>
</entry>
</feed>