-
Notifications
You must be signed in to change notification settings - Fork 1
/
search.xml
2388 lines (2385 loc) · 498 KB
/
search.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
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Apache Rewrite</title>
<url>/2015/05/18/Apache-Rewrite/</url>
<content><![CDATA[<p>本文是对今天使用Apache的Rewrite技术做一个简单的记录。</p>
<blockquote>
<p>Apache的rewrite模块,提供了一个基于规则的重写(rewrite,也许译为重构更为合适)引擎,来实时重写发送到Apache的请求URL。因功能极其强大,被称为URL重写的“瑞士军刀”。</p>
</blockquote>
<p>这个模块使用一个基于正则表达式解析器开发的重写引擎,根据web管理员定义的规则来实时(on the fly)重写请求URL。它支持任意数目的重写规则,以及附加到一条规则上的任意数目的规则条件,从而提供了一套非常灵活和功能强大的URL处理机制。 URL处理操作的实施与否,依赖于各种各样的条件检查,如检查服务器变量、环境变量、HTTP头字段、时间戳的值,甚至外部数据库的检索结果。这个模块可 以在服务器范围内(http.conf)、目录范围内(.htaccess)或请求串(query-string)的一部分处理有关的URL。重写的结果 URL,可以指向一个站内的处理程序、指向站外的重定向或者一个站内的代理。与灵活和功能强大相随的是设置的复杂。<br><span id="more"></span></p>
<h2 id="更新历史"><a href="#更新历史" class="headerlink" title="更新历史"></a>更新历史</h2><p>2015年05月18日 - 初稿</p>
<h2 id="开启模块"><a href="#开启模块" class="headerlink" title="开启模块"></a>开启模块</h2><p>在<code>http.conf</code>中找到<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># LoadModule rewrite_module modules/mod_rewrite.so</span><br></pre></td></tr></table></figure><br>取消注释</p>
<h2 id="定义规则"><a href="#定义规则" class="headerlink" title="定义规则"></a>定义规则</h2><p>在<code>http.conf</code>中加入下列代码(如果启用了<code>httpd-vhosts.conf</code>,请在<code>httpd-vhosts.conf</code>里做配置)<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><IfModule rewrite_module></span><br><span class="line"> RewriteEngine on</span><br><span class="line"> RewriteCond %{HTTP_HOST} ^www.a.com [NC]</span><br><span class="line"> RewriteRule ^/(.*) http://www.b.com/$1 [R=301,l]</span><br><span class="line"><IfModule></span><br></pre></td></tr></table></figure></p>
<p><code>RewriteCond</code>义重写发生的条件,在一条RewriteRule指令前面可能会有一条或多条RewriteCond指令,只有当自身的模板(pattern)匹配成功且这些条件也满足时规则才被应用于当前URL处理,上面代码的<br><code>NC</code>:不区分大小写<br><code>RewriteRule</code>满足<code>^/(.*)</code>此规则的所有URL都重定向到<code>http://www.b.com/$1</code>,<code>$1</code>使用前面<code>(.*)</code>匹配后的字符填充</p>
<p>所以前面的规则就是的最终效果是访问<code>www.a.com</code>的所以页面都会被重定向到<code>www.b.com</code>相应路径下的页面</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote>
<p><a href="http://blog.chinaunix.net/uid-20639775-id-154471.html">http://blog.chinaunix.net/uid-20639775-id-154471.html</a><br><a href="http://man.lupaworld.com/content/manage/Apache2.2_chinese_manual/mod/mod_rewrite.html">http://man.lupaworld.com/content/manage/Apache2.2_chinese_manual/mod/mod_rewrite.html</a><br><a href="http://httpd.apache.org/docs/current/mod/mod_rewrite.html">http://httpd.apache.org/docs/current/mod/mod_rewrite.html</a></p>
</blockquote>
]]></content>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Apache</tag>
<tag>重定向</tag>
</tags>
</entry>
<entry>
<title>代码托管平台码云(Gitee)到Gitea迁移记</title>
<url>/2020/03/01/Gitee2Gitea/</url>
<content><![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="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="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="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="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="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="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="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="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="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>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Git</tag>
<tag>Gitee</tag>
<tag>GitLab</tag>
<tag>Gite</tag>
<tag>Gogs</tag>
<tag>迁移</tag>
<tag>代码</tag>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>GitHub Pages/GitCafe Pages绑定自定义域名</title>
<url>/2014/11/09/Github-Pages-Gitcafe-Pages%E7%BB%91%E5%AE%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E5%9F%9F%E5%90%8D/</url>
<content><![CDATA[<h2 id="更新记录"><a href="#更新记录" class="headerlink" title="更新记录"></a>更新记录</h2><p>2015-01-01 更新 GitCafe-Page IP地址<br>2014-11-09 初稿</p>
<p>虽然GitHub Pages和GitCafe Pages默认为每个用户分配了一个二级域名(GitHub为<code>username.github.io</code>或<code>username.github.com</code>,GitCafe为<code>username.gitcafe.com</code>),但如果你对这个二级域名不满意也可以申请一个自己的域名进行绑定。下面就说说GitHub和GitCafe的绑定过程。<br><span id="more"></span></p>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><ul>
<li>域名(例:iblogc.com)</li>
<li>一个GitHub Pages/GitCafe Pages</li>
</ul>
<hr>
<h2 id="GitHub"><a href="#GitHub" class="headerlink" title="GitHub"></a>GitHub</h2><ul>
<li>在repo目录下创建一个名为<code>CNAME</code>的文件(无后缀)</li>
<li>打开CNAME,在里面写入你要绑定的域名<ul>
<li><del>1)如果你绑定的是二级域名,请在域名管理里添加一条CNAME记录,指向username.github.io或username.github.com</del></li>
<li><del>2)如果你绑定的是顶级域名,请在域名管理里添加一条A记录,指向103.245.222.133</del></li>
<li>请在域名管理里添加一条CNAME记录,指向username.github.io</li>
</ul>
</li>
<li>等待生效 </li>
</ul>
<hr>
<h2 id="GitCafe"><a href="#GitCafe" class="headerlink" title="GitCafe"></a>GitCafe</h2><ul>
<li>打开你自己的gitcafe pages项目,</li>
<li>进入 项目管理>>自定义域名,在这里添加你要绑定的域名就可以,比如我配置了顶级域名iblogc.com(当然也可以设置二级域名)</li>
<li>QQ截图20141109181543.png)</li>
<li>在域名管理里添加一条CNAME记录,记录值为gitcafe.io,如果您的域名注册商不提供CNAME记录选项,请将A记录值修改为 207.226.141.135(IP地址截止2015-01-01有效,如失效,请以<a href="https://gitcafe.com/GitCafe/Help/wiki/Pages-%E7%9B%B8%E5%85%B3%E5%B8%AE%E5%8A%A9#wiki">官方说明</a>为准)。</li>
<li>等待生效</li>
</ul>
]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>域名</tag>
<tag>GitHub</tag>
<tag>GitCafe</tag>
</tags>
</entry>
<entry>
<title>Git命令使用指南</title>
<url>/2015/01/16/Git%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</url>
<content><![CDATA[<html><head></head><body><p><img alt="Git工作流图示" data-src="/media/Git工作流图示.jpg"></p>
<p>Git是软件开发人员在开发中常用的一种工具,是开发之利器。</p>
<blockquote>
<p>Git是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理。<br><span id="more"></span></p>
</blockquote>
<h2 id="工作流图示"><a href="#工作流图示" class="headerlink" title="工作流图示"></a>工作流图示</h2><p><img alt="工作流图示" data-src="/media/git-reset_drbfhd.png"></p>
<h2 id="命令"><a href="#命令" class="headerlink" title="命令"></a>命令</h2><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><ul>
<li><p><code>git config --global user.name 'Your Name'</code> 设置git提交显示的名字</p>
</li>
<li><p><code>git config --global user.email [email protected]</code> 设置git提交显示的邮箱</p>
</li>
<li><p><code>git config --global alias.unstage "reset HEAD"</code> 替换命令 <code>git reset HEAD</code>命令改为 <code>git unstage</code></p>
</li>
<li><p><code>ssh-keygen -t rsa -C [email protected]</code> 生成SSH Key</p>
</li>
<li><p><code>git config --global core.editor emacs</code> 设置文件编辑器</p>
</li>
<li><p><code>git config --global merge.tool vimdiff</code> 设置差异分析工具</p>
</li>
<li><p><code>git config --list</code> 查看配置信息</p>
</li>
</ul>
<h3 id="简洁版"><a href="#简洁版" class="headerlink" title="简洁版"></a>简洁版</h3><p>初始化仓库<br><code>git init</code></p>
<p>添加远程仓库<br><code>git remote add <自定义名字> <远程仓库url></code></p>
<p>给某个仓库名再添加另一个远程仓库url(可实现一次提交到两个远程仓库)<br><code>git remote set-url --add <自定义名字> <远程仓库url></code> </p>
<p>更新项目<br><code>git pull</code></p>
<p>合并分支到当前分支<br><code>git merge <分支名></code></p>
<p>创建标签<br><code>git tag <标签名字> <提交id前10位字符></code> <em>可通过<code>git log</code>获取</em></p>
<p>获取log<br><code>git log</code></p>
<p>切换分支<br><code>git checkout <分支名></code></p>
<p>创建分支并切换过去<br><code>git checkout -b <分支名></code></p>
<p>删除分支<br><code>git branch -D <分支名></code></p>
<p>推送<br><code>git push origin <分支名/标签名></code></p>
<p>强制推送更新<br><code>git push -f origin <分支名/标签名></code></p>
<p>推送所有分支<br><code>git push origin --all</code></p>
<p>推送所有标签<br><code>git push origin --tags</code></p>
<p>撤消本地改动(新文件和提交到缓存区的改动,不受影响)<br><code>git checkout -- <目录><文件名></code></p>
<p>撤消本地所有提交与改动<br><strong><em>假如你想要丢弃你所有的本地改动与提交,可以到服务器上获取最新的版本并将你本地主分支指向到它</em></strong><br><code>git fetch origin</code><br><code>git reset --hard origin/master</code></p>
<p>其它命令</p>
<ul>
<li><code>gitk</code> 获取当前分支图形个界面<ul>
<li>参数<code><分支名></code>: 获取某分支图形界面</li>
<li>参数<code>=--all</code>: 获取所有分支图形个界面</li>
<li><code>cat <目录><文件名></code> 查看文件内容</li>
</ul>
</li>
</ul>
<hr>
<h3 id="详细版"><a href="#详细版" class="headerlink" title="详细版"></a>详细版</h3><p>初始</p>
<ul>
<li><p><code>git init</code> 初始化仓库</p>
</li>
<li><p><code>ls</code> 显示目录下文件及文件夹(不包含隐藏文件即名字前带点的)</p>
<ul>
<li>参数<code>-a</code>显示目录下所有文件及文件夹</li>
</ul>
</li>
<li><p><code>git clone <url></code> 克隆项目</p>
</li>
</ul>
<p>提交</p>
<ul>
<li><p><code>git add <目录><文件名></code> 添加文件到版本库,<em>可以多个文件一起添加,中间用空格隔开</em></p>
</li>
<li><p><code>git add *</code> 或 <code>git add .</code> 添加所有文件到版本库</p>
</li>
</ul>
<p><img alt="status示例图" data-src="http://iblogc.qiniudn.com/iblogcd60500d5-addf-4022-ae4f-c1a57d1f5dd1112.png"></p>
<ul>
<li><p><code>git status</code> 查看项目当前状态,详细信息</p>
<ul>
<li>参数<code>-s</code>: 显示简洁版</li>
</ul>
<blockquote>
<p>绿色表示已经提交的缓存区,红色表示在工作区未提交到缓存区的<br>A新增 M修改 D删除 U冲突 R重命名?<br>push会把绿色部分提交,红色部分不提交<br>已有记录文件做过改动和新文件,需要<code>git add</code></p>
</blockquote>
</li>
<li><p><code>git diff</code> 查看整个项目里的文件改动情况(工作区和缓存区比较)</p>
<ul>
<li>参数<code><目录><文件名></code>: 查看单个文件改动情况(工作区和缓存区比较)<br>-参数<code><标签名></code>: 查看自当前标签发布之后项目的改动情况</li>
<li>参数<code>--cached</code>: 查看整个项目里的文件改动情况(缓存区和本地仓库比较)</li>
<li>参数 <code>HEAD</code>: 查看整个项目里的文件改动情况(工作区和本地仓库比较)</li>
<li>参数<code>--stat</code>: 显示摘要,而非完整diff</li>
</ul>
</li>
<li><p><code>git commit</code>: 提交到缓存</p>
<ul>
<li>参数<code>-m</code>: 后面空格接提交信息</li>
<li>参数<code>-a</code>: 为所有已有记录文件执行<code>git add</code>(新添加文件还是需要手动<code>git add</code>)</li>
</ul>
</li>
<li><p><code>git reset HEAD</code> 取消缓存已缓存的内容</p>
<ul>
<li>参数<code><目录><文件名></code>: 单个文件取消缓存已缓存内容</li>
</ul>
</li>
<li><p><code>git rm <目录><文件名></code>: 将文件从缓存区和硬盘上移除</p>
<ul>
<li>参数<code>--cached</code>: 删除缓存中的文件,保留硬盘上的文件</li>
</ul>
</li>
<li><p><code>git mv</code> 不推荐用</p>
</li>
<li><p><code>git log</code> 显示当前分支提交记录</p>
<ul>
<li>参数<code>--author=<authorname></code>: 只寻找某个特定作者的提交</li>
<li>参数<code>--oneline</code>: 显示简洁版<ul>
<li>参数<code>--oneline -<数字N></code>: 显示简洁版,显示最近N次提交的记录</li>
</ul>
</li>
<li>参数<code>--graph</code>: 显示拓扑图(查看历史中什么时候出现了分支、合并)</li>
<li>参数<code>--grep=<关键字></code>: 根据提交注释关键字过滤提交记录<blockquote>
<p>Git 会对所有的 –grep 和 –author 参数作逻辑或。 如果你用 –grep 和 –author 时,想看的是某人写作的并且有某个特殊的注释内容的提交记录, 你需要加上 –all-match 选项。 在这些例子中,我会用上 –format 选项,这样我们就可以看到每个提交的作者是谁了。详细参考:<a href="http://gitref.org/zh/inspect/">Git参考手册:检查与比较</a></p>
</blockquote>
</li>
<li>参数<code><分支名></code>:显示指定分支“可及”的提交记录</li>
<li>参数<code><分支名1> ^<分支名1></code>: 查看在分支1不在分支2中的提交记录<blockquote>
<p>分支可以是本地的也可以是远端的</p>
</blockquote>
</li>
<li>参数<code>--decorate</code>: 显示带tag的记录</li>
<li>参数<code>-p</code>: 显示每个提交引入的补丁</li>
<li>参数<code>--stat</code>: 显示每个提交引入的差值统计</li>
<li>其它参数 <code>--since</code> <code>--before</code> <code>--until</code> <code>--after</code><blockquote>
<p>git log –since –before 根据日期过滤提交记录<br>如果你要指定一个你感兴趣的日期范围以过滤你的提交,可以执行几个选项 —— 我用 –since 和 –before,但是你也可以用 –until 和 –after。 例如,如果我要看 Git 项目中三周前且在四月十八日之后的所有提交,我可以执行这个(我还用了 –no-merges 选项以隐藏合并提交)<a href="http://gitref.org/zh/inspect/">Git参考手册:检查与比较</a>:</p>
</blockquote>
</li>
</ul>
</li>
</ul>
<pre><code>$ git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges
5469e2d Git 1.7.1-rc2
d43427d Documentation/remote-helpers: Fix typos and improve language
272a36b Fixup: Second argument may be any arbitrary string
b6c8d2d Documentation/remote-helpers: Add invocation section
5ce4f4e Documentation/urls: Rewrite to accomodate transport::address
00b84e9 Documentation/remote-helpers: Rewrite description
03aa87e Documentation: Describe other situations where -z affects git diff
77bc694 rebase-interactive: silence warning when no commits rewritten
636db2c t3301: add tests to use --format="%N"
</code></pre><p>分支</p>
<ul>
<li><p><code>git branch</code>列出当前项目的可用分支,并显示当前工作目录当前分支</p>
</li>
<li><p>参数<code><分支名></code>: 创建分支</p>
</li>
<li><p><code>git checkout <分支名></code> 切换到对应分支</p>
<ul>
<li>参数<code>-b</code> 创建分支并立即切换到新分支</li>
</ul>
</li>
<li><p><code>git merge <分支名></code> 合并指定分支到当前分支</p>
</li>
</ul>
<p>标签</p>
<ul>
<li><p><code>git tag</code> 显示当前项目的标签</p>
<ul>
<li>参数<code><标签名></code> 给某个历史记录打标签</li>
<li>参数<code>-a</code>: 添加注解</li>
<li>参数<code><SHA></code>: 提交id前n位字符,可通过<code>git log</code>获取,n位基于SHA唯一就行(建议5~7位)</li>
</ul>
</li>
</ul>
<p>远程</p>
<ul>
<li><p><code>git remote</code> 列出远端别名<br>-参数<code>-v</code>: 列出远端别名及链接</p>
<blockquote>
<p>一般一个别名会看到两个相同的链接(fetch和push)分别是获取和推送的链接<br>-<code>add <仓库别名> <仓库链接></code>: 为项目添加一个新的远端仓库</p>
<ul>
<li><code>rm <仓库别名></code>: 为项目删除一个远端仓库<br>只是本地删掉和远端仓库的链接,不会对远端仓库造成影响</li>
</ul>
</blockquote>
</li>
<li><p><code>git fetch</code> 从远端仓库下载最新的分支与数据</p>
</li>
<li><p><code>git pull</code> 从远端仓库下载最新数据,并尝试合并到当前分支</p>
<ul>
<li>参数<code><仓库别名></code>: 从哪个仓库拉取更新,默认为origin<blockquote>
<p><code>git pull</code>实际是先<code>git fetch</code>后<code>git merge</code></p>
</blockquote>
</li>
</ul>
</li>
<li><p><code>git push</code> 推送更新</p>
<ul>
<li>参数<code><仓库别名> <分支名></code>: 推送新分支与数据到某个远端仓库</li>
<li>参数<code><仓库别名> --all</code>: 推送所有分支</li>
<li>参数<code><仓库别名> --tagsl</code>: 推送所有标签</li>
</ul>
</li>
</ul>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><blockquote>
<p><a href="http://gitref.org/zh">Git 参考手册</a><br><a href="http://www.bootcss.com/p/git-guide/">git - 简易指南</a></p>
</blockquote>
</body></html>]]></content>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Git</tag>
<tag>教程</tag>
</tags>
</entry>
<entry>
<title>SpringWebFlux使用笔记</title>
<url>/2019/02/28/SpringWebFlux/</url>
<content><![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="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>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Reactive</tag>
</tags>
</entry>
<entry>
<title>UC和QQ浏览器私有META</title>
<url>/2015/05/06/UC%E5%92%8CQQ%E6%B5%8F%E8%A7%88%E5%99%A8%E7%A7%81%E6%9C%89META/</url>
<content><![CDATA[<h2 id="什么是META"><a href="#什么是META" class="headerlink" title="什么是META"></a>什么是META</h2><blockquote>
<p>META是HTML语言中的一个标签,也称作元标记。<META> 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词。<meta> 标签位于文档的头部,不包含任何内容,<meta> 标签的属性定义了与文档相关联的名称/值对。<br><span id="more"></span></p>
</blockquote>
<h2 id="UC的私有META"><a href="#UC的私有META" class="headerlink" title="UC的私有META"></a>UC的私有META</h2><h3 id="屏幕方向"><a href="#屏幕方向" class="headerlink" title="屏幕方向"></a>屏幕方向</h3><p>强制横屏/强制竖屏<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><meta name="screen-orientation" content="landscape/portrait"></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="code"><pre><span class="line"><meta name="full-screen" content="yes"></span><br></pre></td></tr></table></figure>
<h3 id="xhtml适应屏幕排版"><a href="#xhtml适应屏幕排版" class="headerlink" title="xhtml适应屏幕排版"></a>xhtml适应屏幕排版</h3><p>默认值为uc-fitscreen=no,即不启用此功能,此时浏览器的缩放行为与标准一致。当设置为uc-fitscreen=yes,则当进行缩放操作时,仅放大图片和文字等页面元素,但不放大屏幕宽度,从而避免了左右滚动条的产生。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><meta name="viewport" content="uc-fitscreen=yes"/></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="排版模式"><a href="#排版模式" class="headerlink" title="排版模式"></a>排版模式</h3><p>Uc浏览器提供两种排版模式,分别是适屏模式及标准模式,其中适屏模式简化了一些页面的处理,使得页面内容更适合进行页面阅读、节省流量及响应更快,而标准模式则能按照标准规范对页面进行排版及渲染。通过新定义的标签及js api接口,可以让网页设计者执行决定采用何种排版方式向用户展现页面。<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><meta name="layoutmode" content="fitscreen/standard" /></span><br></pre></td></tr></table></figure></p>
<h3 id="夜间模式"><a href="#夜间模式" class="headerlink" title="夜间模式"></a>夜间模式</h3><p>允许进入夜间模式/禁止进入夜间模式<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><meta name="nightmode" content="enable/disable"/></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="code"><pre><span class="line"><meta name="imagemode" content="force"/></span><br></pre></td></tr></table></figure>
<h3 id="应用模式"><a href="#应用模式" class="headerlink" title="应用模式"></a>应用模式</h3><p>默认将全屏,禁止长按菜单,禁止手势,标准排版<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><meta name="browsermode" content="application"/></span><br></pre></td></tr></table></figure></p>
<h2 id="QQ浏览器的私有META"><a href="#QQ浏览器的私有META" class="headerlink" title="QQ浏览器的私有META"></a>QQ浏览器的私有META</h2><h3 id="屏幕方向-1"><a href="#屏幕方向-1" class="headerlink" title="屏幕方向"></a>屏幕方向</h3><p>强制横屏/强制竖屏/自动(默认)<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"><meta name="x5-orientation" content="landscape/portrait/auto"/></span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<h3 id="全屏-1"><a href="#全屏-1" class="headerlink" title="全屏"></a>全屏</h3><p>强制全屏/跟随浏览器(默认)<br><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"x5-fullscreen"</span> <span class="attr">content</span>=<span class="string">"true/auto"</span>/></span></span><br></pre></td></tr></table></figure></p>
<h3 id="页面模式"><a href="#页面模式" class="headerlink" title="页面模式"></a>页面模式</h3><p>普通浏览模式(默认)/网页应用模式(定制工具栏,全屏显示)<br><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"x5-page-mode"</span> <span class="attr">content</span>=<span class="string">"default/app"</span>/></span></span><br></pre></td></tr></table></figure></p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><blockquote>
<p><a href="http://www.uc.cn/business/developer/">http://www.uc.cn/business/developer/</a><br><a href="http://open.mb.qq.com/doc?id=1201#_1">http://open.mb.qq.com/doc?id=1201#_1</a></p>
</blockquote>
]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>meta</tag>
<tag>浏览器</tag>
<tag>前端</tag>
</tags>
</entry>
<entry>
<title>WebFlux中mongo操作-Aggregation</title>
<url>/2020/01/17/WebFlux%E4%B8%ADmongo%E6%93%8D%E4%BD%9C-Aggregation/</url>
<content><![CDATA[<p><br><br><span id="more"></span></p>
<p>switch</p>
<figure class="highlight java"><table><tr><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="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="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="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>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Reactive</tag>
<tag>事务</tag>
<tag>mongo</tag>
<tag>数据库</tag>
<tag>Aggregation</tag>
</tags>
</entry>
<entry>
<title>WebFlux中mongo操作-Transaction</title>
<url>/2020/01/17/WebFlux%E4%B8%ADmongo%E6%93%8D%E4%BD%9C-Transaction/</url>
<content><![CDATA[<p><br><br><span id="more"></span><br><figure class="highlight java"><table><tr><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="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>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Reactive</tag>
<tag>事务</tag>
<tag>mongo</tag>
<tag>数据库</tag>
<tag>Flux</tag>
</tags>
</entry>
<entry>
<title>(猫)花木兰领养注意事项</title>
<url>/2019/09/17/adoption-cat-Hua-Mulan/</url>
<content><![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>
<categories>
<category>猫</category>
</categories>
<tags>
<tag>猫</tag>
<tag>Cat</tag>
</tags>
</entry>
<entry>
<title>Android真机调试常用命令</title>
<url>/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/</url>
<content><![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="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="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="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="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="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="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="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="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="code"><pre><span class="line"># 3为录制秒数,可修改</span><br><span class="line">./record.sh 3</span><br></pre></td></tr></table></figure></p>
]]></content>
<categories>
<category>程序员</category>
</categories>
<tags>
<tag>Android</tag>
<tag>macOS</tag>
<tag>命令</tag>
<tag>效率</tag>
<tag>无线</tag>
<tag>调试</tag>
<tag>adb</tag>
<tag>USB</tag>
</tags>
</entry>
<entry>
<title>自用软件推荐</title>
<url>/2019/10/12/awesome-software/</url>
<content><![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>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>效率</tag>
<tag>软件</tag>
<tag>推荐</tag>
</tags>
</entry>
<entry>
<title>在Android Chrome以app形式打开网站</title>
<url>/2015/05/11/chrome-add-to-homescreen/</url>
<content><![CDATA[<html><head></head><body><p>前面一篇文章我讲到了在UC网站可以以app形式打开,其实在Android Chrome浏览器上也支持此功能。<br><span id="more"></span></p>
<h2 id="添加配置文件"><a href="#添加配置文件" class="headerlink" title="添加配置文件"></a>添加配置文件</h2><p>在网站根目录添加<code>manifest.json</code>,并进行相应配置<br></p><figure class="highlight json"><table><tbody><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"iblogc"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"icons"</span><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">"src"</span><span class="punctuation">:</span> <span class="string">"launcher-icon-0-75x.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizes"</span><span class="punctuation">:</span> <span class="string">"36x36"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"image/png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"density"</span><span class="punctuation">:</span> <span class="string">"0.75"</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">"src"</span><span class="punctuation">:</span> <span class="string">"launcher-icon-1x.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizes"</span><span class="punctuation">:</span> <span class="string">"48x48"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"image/png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"density"</span><span class="punctuation">:</span> <span class="string">"1.0"</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">"src"</span><span class="punctuation">:</span> <span class="string">"launcher-icon-1-5x.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizes"</span><span class="punctuation">:</span> <span class="string">"72x72"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"image/png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"density"</span><span class="punctuation">:</span> <span class="string">"1.5"</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">"src"</span><span class="punctuation">:</span> <span class="string">"launcher-icon-2x.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizes"</span><span class="punctuation">:</span> <span class="string">"96x96"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"image/png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"density"</span><span class="punctuation">:</span> <span class="string">"2.0"</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">"src"</span><span class="punctuation">:</span> <span class="string">"launcher-icon-3x.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizes"</span><span class="punctuation">:</span> <span class="string">"144x144"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"image/png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"density"</span><span class="punctuation">:</span> <span class="string">"3.0"</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">"src"</span><span class="punctuation">:</span> <span class="string">"launcher-icon-4x.png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"sizes"</span><span class="punctuation">:</span> <span class="string">"192x192"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"type"</span><span class="punctuation">:</span> <span class="string">"image/png"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"density"</span><span class="punctuation">:</span> <span class="string">"4.0"</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="attr">"start_url"</span><span class="punctuation">:</span> <span class="string">"index.html"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"display"</span><span class="punctuation">:</span> <span class="string">"standalone"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"orientation"</span><span class="punctuation">:</span> <span class="string">"portrait"</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure><p></p>
<h2 id="在网站公用头部引入配置文件"><a href="#在网站公用头部引入配置文件" class="headerlink" title="在网站公用头部引入配置文件"></a>在网站公用头部引入配置文件</h2><figure class="highlight plaintext"><table><tbody><tr><td class="code"><pre><span class="line"><link rel="manifest" href="manifest.json"></span><br></pre></td></tr></tbody></table></figure>
<h2 id="查看效果"><a href="#查看效果" class="headerlink" title="查看效果"></a>查看效果</h2><p>在Android使用Chrome打开网站,点击memu,选择“添加到主屏幕”选项,点击就可以添加到主屏幕了,步骤及显示效果截图如下:<br><img alt="chrome-add-to-homescreen-01" data-src="/media/chrome-add-to-homescreen-01.png"><br><img alt="chrome-add-to-homescreen-02" data-src="/media/chrome-add-to-homescreen-02.png"><br>PS:地址栏是不是不见了,看着像app而不是网页<br><img alt="chrome-add-to-homescreen-03" data-src="/media/chrome-add-to-homescreen-03.png"><br><img alt="chrome-add-to-homescreen-04" data-src="/media/chrome-add-to-homescreen-04.png"></p>
<h2 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h2><p>ios的safari也有此功能,因手头无ios设备测试不了,所以内容不写了,大家可以参考此文章<a href="http://www.prower.cn/technic/2314">http://www.prower.cn/technic/2314</a></p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><blockquote>
<p><a href="https://developer.chrome.com/multidevice/android/installtohomescreen">https://developer.chrome.com/multidevice/android/installtohomescreen</a></p>
</blockquote>
</body></html>]]></content>
<categories>
<category>网站</category>
</categories>
<tags>
<tag>Android</tag>
<tag>Chrome</tag>
</tags>
</entry>
<entry>
<title>Chrome扩展/插件/应用推荐</title>
<url>/2016/06/01/chrome%E6%89%A9%E5%B1%95-%E6%8F%92%E4%BB%B6-%E5%BA%94%E7%94%A8%E6%8E%A8%E8%8D%90/</url>
<content><![CDATA[<p><br/><br>本文过期,请前行住<a href="/2019/10/12/awesome-software/">自用软件推荐</a>查看最新内容<br><span id="more"></span></p>
<h2 id="插件-扩展"><a href="#插件-扩展" class="headerlink" title="插件/扩展"></a>插件/扩展</h2><ol>
<li><p><strong>Adblock Plus</strong><br>广告拦截器,谁用谁知道。</p>
</li>
<li><p><strong>Checker Plus for Gmail™</strong><br>Gmail/Inbox插件,不用打开网页处理邮件。</p>
</li>
<li><p><strong>Wappalyzer</strong><br>探测当前网页正在使用的开源软件或者js类库,web开发者必备神器。</p>
</li>
<li><p><strong>crxMouse Chrome Gestures</strong><br>鼠标手势。</p>
</li>
<li><p><strong>Ghostery</strong><br>了解谁在跟踪您的网页浏览操作,并可禁用跟踪行为。</p>
</li>
<li><p><strong>Google Keep Chrome 扩展程序</strong><br>将网页快速的保存到Google Keep中。</p>
</li>
<li><p><strong>Google翻译</strong><br>支付在网页中划词翻译。</p>
</li>
<li><p><strong>划词翻译</strong><br>划词翻译,支持谷歌、百度、有道、必应四大翻译和朗读引擎,访问Google比较因难的的可以用这个。</p>
</li>
<li><p><strong>LastPass</strong><br>免费的密码管理管理器。</p>
</li>
<li><p><strong>MindMap Tab</strong><br>在新标签页中快速编辑思维导图。</p>
</li>
<li><p><strong>Octotree</strong><br>在浏览器左侧树形展示Github代码。</p>
</li>
<li><p><strong>Prism Pretty</strong><br>美化代码(html、css、js、json……)</p>
</li>
<li><p><strong>Proxy SwitchyOmega</strong><br>你懂的。</p>
</li>
<li><p><strong>Pushbullet</strong><br>快速的往其它设备发送消息。</p>
</li>
<li><p><strong>Save to Pocket</strong><br>快速保存网页到Pocket。</p>
</li>
<li><p><strong>Search by Image (by Google)</strong><br>浏览器右键增加Google的以图搜图功能。</p>
</li>
<li><p><strong>v2ex plus</strong><br>优雅便捷的 V2EX 扩展。</p>
</li>
<li><p><strong>Web Timer</strong><br>每个网站停留时间统计。</p>
</li>
<li><p><strong>为知笔记网页剪辑器</strong><br>配合为知笔记,快速保存网页内容到为知笔记。</p>
</li>
<li><p><strong>二维码(生成及识别)</strong><br>生成或识别二维码。</p>
</li>
<li><p><strong>惠惠购物助手</strong><br>在主流电商网站页面上提供商品的历史价格,及在同款商品在其它平台的价格比较。</p>
</li>
<li><p><strong>新浪微博图床</strong><br>微博是个好图床。</p>
</li>
<li><p><strong>网页截图:注释&批注</strong><br>捕获整个页面或任何部分,矩形,圆形,箭头,线条和文字,模糊敏感信息,一键上传分享。</p>
</li>
</ol>
<h2 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h2><ol>
<li><p><strong>Postman</strong><br>功能强大的接口调试工具。</p>
</li>
<li><p><strong>Google Keep - 记事和清单</strong><br>不解释。</p>
</li>
<li><p><strong>Pocket</strong><br>不解释。</p>
</li>
<li><p><strong>JSON Editor</strong><br>图形化json编辑工具。</p>
</li>
</ol>
]]></content>
<categories>
<category>优化辅助</category>
</categories>
<tags>
<tag>Chrome</tag>
<tag>工具</tag>
<tag>扩展</tag>
<tag>插件</tag>
</tags>
</entry>
<entry>
<title>curl常用命令</title>
<url>/2015/12/17/curl%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</url>
<content><![CDATA[<p>curl是利用URL语法在命令行方式下工作的开源文件传输工具。它被广泛应用在Unix、多种Linux发行版中,并且有DOS和Win32、Win64下的移植版本。<br><span id="more"></span></p>
<h3 id="访问url并输出结果"><a href="#访问url并输出结果" class="headerlink" title="访问url并输出结果"></a>访问url并输出结果</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl http://www.example.com</span><br></pre></td></tr></table></figure>
<h3 id="传递参数"><a href="#传递参数" class="headerlink" title="传递参数"></a>传递参数</h3><p>默认curl使用GET方式请求数据,这种方式下直接通过URL传递数据<br>可以通过 –data/-d 方式指定使用POST方式传递数据<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># GET</span><br><span class="line">curl -u username https://api.github.com/user?access_token=XXXXXXXXXX</span><br><span class="line"></span><br><span class="line"># POST</span><br><span class="line">curl -u username -d "param1=value1&param2=value" https://api.github.com</span><br><span class="line"></span><br><span class="line"># 也可以指定一个文件,将该文件中的内容当作数据传递给服务器端</span><br><span class="line">curl -d @filename https://github.api.com/authorizations</span><br></pre></td></tr></table></figure><br>注:默认情况下,通过POST方式传递过去的数据中若有特殊字符,首先需要将特殊字符转义在传递给服务器端,如value值中包含有空格,则需要先将空格转换成%20,如:<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -d "value%201" http://hostname.com</span><br></pre></td></tr></table></figure><br>在新版本的CURL中,提供了新的选项 –data-urlencode,通过该选项提供的参数会自动转义特殊字符。<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl --data-urlencode "value 1" http://hostname.com</span><br></pre></td></tr></table></figure></p>
<h3 id="指定请求方式"><a href="#指定请求方式" class="headerlink" title="指定请求方式"></a>指定请求方式</h3><p>除了使用GET和POST协议外,还可以通过 -X 选项指定其它协议,如:<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -I -X DELETE https://api.github.com</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="code"><pre><span class="line">curl -H 'Accept-Language: zh' http://cnn.com</span><br></pre></td></tr></table></figure>
<p><code>-H</code>或被多次指定<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -H 'Host: 157.166.226.25'-H 'Accept-Language: zh'-H 'Cookie: ID=1234' http://cnn.com</span><br></pre></td></tr></table></figure><br>对于”User-Agent”, “Cookie”, “Host”这类标准的HTTP头部字段,通常会有另外一种设置方法。curl命令提供了特定的选项来对这些头部字段进行设置:<br>-A (or –user-agent): 设置 “User-Agent” 字段.<br>-b (or –cookie): 设置 “Cookie” 字段.<br>-e (or –referer): 设置 “Referer” 字段.<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -H "User-Agent: my browser" http://cnn.com</span><br><span class="line">curl -A "my browser" http://cnn.com</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="code"><pre><span class="line">curl -I http://www.baidu.com</span><br></pre></td></tr></table></figure>
<h3 id="提交表单"><a href="#提交表单" class="headerlink" title="提交表单"></a>提交表单</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl --form "[email protected]" http://hostname/resource</span><br></pre></td></tr></table></figure>
<h3 id="访问url并奖结果保存到本地文件中"><a href="#访问url并奖结果保存到本地文件中" class="headerlink" title="访问url并奖结果保存到本地文件中"></a>访问url并奖结果保存到本地文件中</h3><p><code>-o</code>: 将文件保存为命令行中指定的文件名到本地<br><code>-O</code>: 使用url中默认的文件名保存文件到本地<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -o index.html http://www.example.com</span><br><span class="line"># 或</span><br><span class="line">curl http://www.baidu.com > index.html</span><br><span class="line"># 在windows上没成功</span><br><span class="line">curl -O http://www.example.com</span><br></pre></td></tr></table></figure></p>
<h3 id="忽略证书错误"><a href="#忽略证书错误" class="headerlink" title="忽略证书错误"></a>忽略证书错误</h3><p>工作中,经常需要用自签的假证书搭建开发环境。cURL在遇到证书错误时罢工,使用 -k 参数就可以让它不做证书校验。<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -k https://www.example.com</span><br></pre></td></tr></table></figure></p>
<h3 id="获取重定向后的页面"><a href="#获取重定向后的页面" class="headerlink" title="获取重定向后的页面"></a>获取重定向后的页面</h3><p>如果url重定向的话,curl默认是不会去获取重定向后的url页面的,使用<code>-L</code>可进行强制重定向<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -L http://www.example.com</span><br></pre></td></tr></table></figure></p>
<h3 id="发送压缩的请求"><a href="#发送压缩的请求" class="headerlink" title="发送压缩的请求"></a>发送压缩的请求</h3><p>cURL提供了一个 –compress 参数,可以用来发送支持压缩的请求。但使用了–compress之后,虽然传输过程是压缩的,cURL的输出还是解压之后的,难以看到效果。</p>
<p>自己写一个 Accept-Encoding 字段在头信息中。<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -H "Accept-Encoding: gzip" http://www.kuqin.com/</span><br></pre></td></tr></table></figure></p>
<p>如果直接运行上面的命令,会得到一堆乱码,因为cURL输出的内容,是压缩后的数据。不妨在后面接一个gunzip试试。<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 使用gunzip解压</span><br><span class="line">curl -H "Accept-Encoding: gzip" http://www.kuqin.com/ | gunzip</span><br></pre></td></tr></table></figure></p>
<p>使用gunzip解压之后,信息又被还原了。</p>
<h3 id="断点续传"><a href="#断点续传" class="headerlink" title="断点续传"></a>断点续传</h3><p>通过使用-C选项可对大文件使用断点续传功能<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 未下载完成即中断该进程</span><br><span class="line">curl -o a.zip http://www.example.com/bigfile.zip</span><br><span class="line"></span><br><span class="line"># 后面可以通过-C来继续下载</span><br><span class="line">curl -C -o a.html http://www.example.com/bigfile.zip</span><br></pre></td></tr></table></figure></p>
<h3 id="下载限速"><a href="#下载限速" class="headerlink" title="下载限速"></a>下载限速</h3><p>使用-limit-rate进行限速<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 限速为100k/s</span><br><span class="line">curl --limit-rate 1000k -o a.zip http://www.example.com/bigfile.zip</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="code"><pre><span class="line"># 若文件的修改时间在2011/12/11之后,则下载</span><br><span class="line">curl -z 21-Dec-11 http://www.example.com/bigfile.zip</span><br></pre></td></tr></table></figure>
<h3 id="授权"><a href="#授权" class="headerlink" title="授权"></a>授权</h3><p>在访问需要授权的页面时,可通过<code>-u</code>来提供用户名和密码进行授权<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -u username:password http://www.example.com</span><br></pre></td></tr></table></figure></p>
<h3 id="ftp操作"><a href="#ftp操作" class="headerlink" title="ftp操作"></a>ftp操作</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 列出指定目录下的所有文件</span><br><span class="line">curl -u ftpuser:ftppw -O ftp://ftp_server/public_html/</span><br><span class="line"></span><br><span class="line"># 下载文件</span><br><span class="line">curl -u ftpuser:ftppw -O</span><br><span class="line">ftp://ftp_server/public_hmtl/bigfile.zip</span><br><span class="line"></span><br><span class="line"># 上传文件</span><br><span class="line">curl -u ftpuser:ftppw -T myfile.txt ftp://ftp_server/public_html/</span><br><span class="line"></span><br><span class="line"># 上传多个文件</span><br><span class="line">curl -u ftpuser:ftppw -T "{myfile1.txt, myfile2.txt}" ftp://ftp_server/public_html/</span><br><span class="line"></span><br><span class="line"># 从标准输入获取内容保存到服务器的指定文件中</span><br><span class="line">curl -u ftpuser:ftppw -T - ftp://ftp_server/public_html/1.txt</span><br></pre></td></tr></table></figure>
<h3 id="设置代理"><a href="#设置代理" class="headerlink" title="设置代理"></a>设置代理</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -x proxyserver.com:1080 http://www.example.com</span><br></pre></td></tr></table></figure>
<h3 id="保存与使用网站的cookie信息"><a href="#保存与使用网站的cookie信息" class="headerlink" title="保存与使用网站的cookie信息"></a>保存与使用网站的cookie信息</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 将网站的cookies信息保存到example_cookies文件中</span><br><span class="line">curl -D example_cookies http://www.example.com</span><br><span class="line"></span><br><span class="line"># 使用cookies信息访问url</span><br><span class="line">curl -b example_cookies http://www.example.com/user/</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>教程</tag>
<tag>curl</tag>
</tags>
</entry>
<entry>
<title>Django REST framework之版本号version</title>
<url>/2016/01/28/django-rest-framework%E4%B9%8B%E7%89%88%E6%9C%AC%E5%8F%B7version/</url>
<content><![CDATA[<p><br /><br><span id="more"></span><br>drf支持以下形式传输版本号</p>
<ul>
<li>header<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GET /bookings/ HTTP/1.1</span><br><span class="line">Host: example.com</span><br><span class="line">Accept: application/json; version=1.0</span><br></pre></td></tr></table></figure></li>
<li>URL Path <figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GET /v1/bookings/ HTTP/1.1</span><br><span class="line">Host: example.com</span><br><span class="line">Accept: application/json</span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">urlpatterns = [</span><br><span class="line"> url(</span><br><span class="line"> r'^(?P<version>(v1|v2))/bookings/$',</span><br><span class="line"> bookings_list,</span><br><span class="line"> name='bookings-list'</span><br><span class="line"> ),</span><br><span class="line">]</span><br></pre></td></tr></table></figure></li>
<li>Namespace<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GET /v1/something/ HTTP/1.1</span><br><span class="line">Host: example.com</span><br><span class="line">Accept: application/json</span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">urlpatterns = [</span><br><span class="line"> url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),</span><br><span class="line"> url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))</span><br><span class="line">]</span><br></pre></td></tr></table></figure></li>
<li>Host Name<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GET /bookings/ HTTP/1.1</span><br><span class="line">Host: v1.example.com</span><br><span class="line">Accept: application/json</span><br></pre></td></tr></table></figure></li>
<li>Query Parameter<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">GET /something/?version=0.1 HTTP/1.1</span><br><span class="line">Host: example.com</span><br><span class="line">Accept: application/json</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>drf默认是关闭版本控制功能,如需要开启,可在<code>settings.py</code>里添加对应的设置<br><figure class="highlight python"><table><tr><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.AcceptHeaderVersioning'</span>,</span><br><span class="line"> <span class="comment"># 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',</span></span><br><span class="line"> <span class="comment"># 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',</span></span><br><span class="line"> <span class="comment"># 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.HostNameVersioning',</span></span><br><span class="line"> <span class="comment"># 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',</span></span><br><span class="line"> ……</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>当然,你也可以为每个视图单独添加,不过不建议这么做<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ProfileList</span>(<span class="title class_ inherited__">APIView</span>):</span><br><span class="line"> versioning_class = versioning.QueryParameterVersioning</span><br></pre></td></tr></table></figure><br>开启版本控制之后,就可以从<code>request</code>取得版本号<code>request.version</code>(当然你<code>settings.py</code>里配置的是什么方式,就用什么方式传版本号,这样就才可以从<code>request</code>里获取到版本号)<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_serializer_class</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">if</span> self.request.version == <span class="string">'v1'</span>:</span><br><span class="line"> <span class="keyword">return</span> AccountSerializerVersion1</span><br><span class="line"> <span class="keyword">return</span> AccountSerializer</span><br></pre></td></tr></table></figure></p>
<p>启动版本控制后,url逆向解析方法需要传入<code>request</code>参数<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> rest_framework.reverse <span class="keyword">import</span> reverse</span><br><span class="line"> </span><br><span class="line">reverse(<span class="string">'bookings-list'</span>, request=request)</span><br></pre></td></tr></table></figure></p>
<p>如果是使用Namespace时的版本控制,因为配置了<code>DEFAULT_VERSIONING_CLASS</code>,所以设置view_name时不需要添加<code>v1:</code>前缀,见django rest framework入门笔记.md</p>
<p>最后在设置里添加以下全局设置来控制能访问的版本<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="string">'DEFAULT_VERSION'</span>: <span class="literal">None</span>, <span class="comment">#默认版本,request里没有版本信息时,使用的版本,默认为None</span></span><br><span class="line"><span class="string">'ALLOWED_VERSIONS'</span>: [<span class="literal">None</span>, <span class="string">'v1'</span>, <span class="string">'v2'</span>], <span class="comment">#允许访问的版本,如果访问的版本不在列表中,则会抛出异常</span></span><br></pre></td></tr></table></figure><br>也可以为每个视图单独设置<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> rest_framework.versioning <span class="keyword">import</span> URLPathVersioning</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_">ExampleVersioning</span>(<span class="title class_ inherited__">URLPathVersioning</span>):</span><br><span class="line"> default_version = ...</span><br><span class="line"> allowed_versions = ...</span><br><span class="line"> version_param = ...</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"> versioning_class = ExampleVersioning</span><br></pre></td></tr></table></figure></p>
]]></content>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Django</tag>
<tag>restful</tag>
<tag>api</tag>
<tag>version</tag>
</tags>
</entry>
<entry>
<title>Django Rest framework使用问题及解决方法</title>
<url>/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/</url>
<content><![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="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="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="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="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="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="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="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>
<tags>
<tag>Django</tag>
<tag>restful</tag>
<tag>api</tag>
<tag>问题</tag>
</tags>
</entry>
<entry>
<title>Django Rest framework入门笔记及跳坑记录</title>
<url>/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/</url>
<content><![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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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>
<tags>
<tag>Django</tag>
<tag>restful</tag>
<tag>api</tag>
<tag>问题</tag>
</tags>
</entry>
<entry>
<title>Django REST framework单元测试「Unit Testing」</title>
<url>/2017/09/05/django-rest-framework%E6%8E%A5%E5%8F%A3%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/</url>
<content><![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="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="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="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="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>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Django</tag>
<tag>restful api</tag>
<tag>接口</tag>
<tag>单元测试</tag>
<tag>测试</tag>
</tags>
</entry>
<entry>
<title>Django Rest framework里的API请求频率控制</title>
<url>/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/</url>
<content><![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="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="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="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="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="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="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="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>
<tags>
<tag>Django</tag>
<tag>restful</tag>
<tag>api, version</tag>
</tags>
</entry>
<entry>
<title>Django内置的flatpages应用</title>
<url>/2015/09/08/django%E5%86%85%E7%BD%AE%E7%9A%84flatpages%E5%BA%94%E7%94%A8/</url>
<content><![CDATA[<p>不知道大家平时写网站时类似「关于页面」,「用户协议」这类页面是如何处理的。这里列出了这类页面的两个特点:</p>
<ol>
<li>页面数据简单(不会有很多动态数据展示);</li>
<li>有更新但频率不高;</li>
</ol>
<p>如果要为这类页面展示建立多个数据表,显然是很浪费的行为,但如果直接写成静态页面文件,更新又比较很麻烦,这时候就可以使用<code>flatpages</code>来解决这类问题了。<br><!--more--></p>
<p><code>django.contrib.flatpages</code>是<code>Django</code>的内置app,用于添加更新的一些简单的页面,具体设置,请继续查看以下步骤。</p>
<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>首先确保<code>INSTALLED_APPS</code>中已经存在<code>django.contrib.sites</code>,因为<code>django.contrib.flatpages</code>依赖于此包。<br><code>settings.py</code><br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">INSTALLED_APPS = (</span><br><span class="line"> <span class="comment"># ...</span></span><br><span class="line"> <span class="string">'django.contrib.sites'</span>,</span><br><span class="line"> <span class="string">'django.contrib.flatpages'</span>,</span><br><span class="line">)</span><br><span class="line"><span class="comment"># ...</span></span><br><span class="line"><span class="comment"># 如果没有设置`SITE_ID`值,则需要设置,这里直接设置为1</span></span><br><span class="line">SITE_ID = <span class="number">1</span></span><br></pre></td></tr></table></figure><br>执行<code>python manage.py migrate</code>建表</p>
<h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>路由配置可先以有多种形式<br><code>urls.py</code><br>第一种(需放在最后,推荐)<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">from django.contrib.flatpages import views</span><br><span class="line">urlpatterns += [</span><br><span class="line"> url(r'^(?P<url>.*/)$', views.flatpage),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><br>第二种(每个页面都需要写一个url,推荐)<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">from django.contrib.flatpages import views</span><br><span class="line">urlpatterns = [</span><br><span class="line"> url(r'^about-us/$', views.flatpage, {'url': '/about-us/'}, name='about'),</span><br><span class="line"> url(r'^license/$', views.flatpage, {'url': '/license/'}, name='license'),</span><br><span class="line">]</span><br></pre></td></tr></table></figure><br>或者,如果你不想配置路由,还有一种更简单的方法,直接在<code>settings.py</code>的里添加中间件<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">MIDDLEWARE_CLASSES = (</span><br><span class="line"> # ...</span><br><span class="line"> 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',</span><br><span class="line">)</span><br></pre></td></tr></table></figure><br><em>为确保配置生效,保险的方法是把<code>django.contrib.flatpages.middleware.FlatpageFallbackMiddleware</code>放在最后一行</em></p>
<h2 id="管理flatpages"><a href="#管理flatpages" class="headerlink" title="管理flatpages"></a>管理<code>flatpages</code></h2><p>默认的你可以登录超级管理员后台(如果开启),找到<code>Flat pages</code>,进去点击添加,可以看到可配置的选项有,<code>URL</code> <code>Title</code> <code>Content</code> <code>Site</code> <code>Enable comments</code> <code>Registration required</code> <code>Template name</code></p>
<h3 id="数据项说明"><a href="#数据项说明" class="headerlink" title="数据项说明"></a>数据项说明</h3><ul>
<li><code>URL</code>: 页面所处的 URL,不包括域名,但是包含前导斜杠 (例如 /about/contact/ )</li>
<li><code>Title</code>: 页面的标题,框架不对它作任何特殊处理。由你通过模板来显示它</li>
<li><code>Content</code>: 页面的内容 (即 HTML 页面),框架不会对它作任何特别处理。由你负责使用模板来显示</li>
<li><code>Site</code>: 页面放置的站点,该项设置集成了 Django 多站点框架</li>
<li><code>Enable comments</code>: 是否允许该简单页面使用评论,框架不对此做任何特别处理。你可在模板中检查该值并根据需要显示评论窗体</li>
<li><code>Registration required</code>: 是否注册用户才能查看此简单页面,该设置项集成了 Djangos 验证/用户框架,该框架于第十二章详述。</li>
<li><code>Template name</code>: 用来解析该简单页面的模板名称,这是一个可选项,如果未指定模板或该模板不存在,系统会退而使用默认模板 <code>flatpages/default.html</code>(我在<code>Django1.8.4</code>里死活没找到,只好自己写好一个扔进去)</li>
</ul>
<p>当添加相应的数据后,剩下工作就交给<code>flatpages</code>吧,如果你是使用中间件形式的,则<code>flatpages</code>会在配置完所有<code>urls.py</code>后,没有找到配置到对应的<code>URL</code>,才会到<code>flatpages</code>中查找,如果还是找不到,则会引发<code>Http404</code>异常,即<code>FlatpageFallbackMiddleware</code>只在<code>404</code>时会被激活,而不会在<code>500</code>或其它错误响应时被激活。</p>
<p>如果你需要自己定制,则可以针对<code>django/contrib/flatpages/models.py</code>自己写增删改方法就可以。<br><code>models.py</code><br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">FlatPage</span>(models.Model):</span><br><span class="line"> url = models.CharField(_(<span class="string">'URL'</span>), max_length=<span class="number">100</span>, db_index=<span class="literal">True</span>)</span><br><span class="line"> title = models.CharField(_(<span class="string">'title'</span>), max_length=<span class="number">200</span>)</span><br><span class="line"> content = models.TextField(_(<span class="string">'content'</span>), blank=<span class="literal">True</span>)</span><br><span class="line"> enable_comments = models.BooleanField(_(<span class="string">'enable comments'</span>), default=<span class="literal">False</span>)</span><br><span class="line"> template_name = models.CharField(_(<span class="string">'template name'</span>), max_length=<span class="number">70</span>, blank=<span class="literal">True</span>,</span><br><span class="line"> help_text=_(</span><br><span class="line"> <span class="string">"Example: 'flatpages/contact_page.html'. If this isn't provided, "</span></span><br><span class="line"> <span class="string">"the system will use 'flatpages/default.html'."</span></span><br><span class="line"> ),</span><br><span class="line"> )</span><br><span class="line"> registration_required = models.BooleanField(_(<span class="string">'registration required'</span>),</span><br><span class="line"> help_text=_(<span class="string">"If this is checked, only logged-in users will be able to view the page."</span>),</span><br><span class="line"> default=<span class="literal">False</span>)</span><br><span class="line"> sites = models.ManyToManyField(Site)</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"> db_table = <span class="string">'django_flatpage'</span></span><br><span class="line"> verbose_name = _(<span class="string">'flat page'</span>)</span><br><span class="line"> verbose_name_plural = _(<span class="string">'flat pages'</span>)</span><br><span class="line"> ordering = (<span class="string">'url'</span>,)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__str__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"%s -- %s"</span> % (self.url, self.title)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get_absolute_url</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="comment"># Handle script prefix manually because we bypass reverse()</span></span><br><span class="line"> <span class="keyword">return</span> iri_to_uri(get_script_prefix().rstrip(<span class="string">'/'</span>) + self.url)</span><br></pre></td></tr></table></figure></p>
<h2 id="模板"><a href="#模板" class="headerlink" title="模板"></a>模板</h2><p>默认模板路径为<code>flatpages/default.html</code><br><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">title</span>></span>{{ flatpage.title }}<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line">{{ flatpage.content }}</span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>在实际应用中,我们不太可能会使用默认的模板,你可能需要自己写一个漂亮模板,比如有一个头部和底部,头部可能还需要添加<code>requeset.user</code>显示用户信息等。</p>
</blockquote>
<h2 id="高级应用"><a href="#高级应用" class="headerlink" title="高级应用"></a>高级应用</h2><p>获取<code>flatpages</code>实例列表<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">{% load flatpages %}</span><br><span class="line">{% get_flatpages as flatpages %}</span><br></pre></td></tr></table></figure></p>
<p>获取当前用户能打开的<code>flatpages</code>实例列表<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">{% load flatpages %}</span><br><span class="line">{% get_flatpages for request.user as about_pages %}</span><br></pre></td></tr></table></figure></p>
<p>获取链接以<code>/about/</code>为开头的<code>flatpages</code>实例列表<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">{% load flatpages %}</span><br><span class="line">{% get_flatpages '/about/' as about_pages %}</span><br></pre></td></tr></table></figure></p>
<p>上面两种也可以组合使用<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">{% load flatpages %}</span><br><span class="line">{% get_flatpages '/about/' for someuser as about_pages %}</span><br></pre></td></tr></table></figure></p>
<h2 id="生成sitemaps-xml"><a href="#生成sitemaps-xml" class="headerlink" title="生成sitemaps.xml"></a>生成<code>sitemaps.xml</code></h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">from django.conf.urls import url</span><br><span class="line">from django.contrib.flatpages.sitemaps import FlatPageSitemap</span><br><span class="line">from django.contrib.sitemaps.views import sitemap</span><br><span class="line"> </span><br><span class="line">urlpatterns = [</span><br><span class="line"> # ...</span><br><span class="line"> </span><br><span class="line"> # the sitemap</span><br><span class="line"> url(r'^sitemap\.xml$', sitemap,</span><br><span class="line"> {'sitemaps': {'flatpages': FlatPageSitemap}},</span><br><span class="line"> name='django.contrib.sitemaps.views.sitemap'),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<h2 id="容易踩的坑"><a href="#容易踩的坑" class="headerlink" title="容易踩的坑"></a>容易踩的坑</h2><p>最好把<code>settings.py</code>里的<code>APPEND_SLASH</code>设置为<code>Ture</code>, 这样不管是<code>/about-us</code>还是<code>/about-us/</code>都可以访问到。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://docs.djangoproject.com/en/1.8/ref/contrib/flatpages/">https://docs.djangoproject.com/en/1.8/ref/contrib/flatpages/</a></li>
<li><a href="http://djangobook.py3k.cn/2.0/chapter16/">http://djangobook.py3k.cn/2.0/chapter16/</a></li>
</ul>
]]></content>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Django</tag>
</tags>
</entry>
<entry>
<title>Django字段选项related_name和related_query_name</title>
<url>/2015/10/20/django%E5%AD%97%E6%AE%B5%E9%80%89%E9%A1%B9related-name%E5%92%8Crelated-query-name/</url>
<content><![CDATA[<p><code>data</code><br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">sqlite> select * from author;</span><br><span class="line">id name age</span><br><span class="line">1 jim 12</span><br><span class="line">2 tom 11</span><br><span class="line">sqlite> select * from book;</span><br><span class="line">id name author_id</span><br><span class="line">1 learn java 1</span><br><span class="line">2 learn python 1</span><br><span class="line">3 learn c++ 2</span><br></pre></td></tr></table></figure><br><span id="more"></span><br><code>models.py</code><br><figure class="highlight python"><table><tr><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> unicode_literals</span><br><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="comment"># Create your models here.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Author</span>(models.Model):</span><br><span class="line"> name = models.CharField(verbose_name=<span class="string">'姓名'</span>, max_length=<span class="number">50</span>)</span><br><span class="line"> age = models.IntegerField(verbose_name=<span class="string">'年龄'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Book</span>(models.Model):</span><br><span class="line"> name = models.CharField(verbose_name=<span class="string">'书名'</span>, max_length=<span class="number">100</span>)</span><br><span class="line"> author = models.ForeignKey(Author, verbose_name=<span class="string">'作者'</span>)</span><br></pre></td></tr></table></figure><br>执行语句<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">>>> Author.objects.filter(book__name='learn java')</span><br><span class="line">[<Author: jim>]</span><br><span class="line">>>> author = Author.objects.get(pk=1)</span><br><span class="line">>>> author.book_set.all()</span><br><span class="line">[<Book: learn java>, <Book: learn python>]</span><br></pre></td></tr></table></figure></p>
<p>假如把类<code>Book</code>改成这样<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">class Book(models.Model):</span><br><span class="line"> name = models.CharField(verbose_name='书名', max_length=100)</span><br><span class="line"> author = models.ForeignKey(Author, verbose_name='作者', related_name='bs', related_query_name='b')</span><br></pre></td></tr></table></figure><br>那么上面查询代码就应该写成这样<br><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">>>> Author.objects.filter(b__name='learn java')</span><br><span class="line">[<Author: jim>]</span><br><span class="line">>>> author = Author.objects.get(pk=1)</span><br><span class="line">>>> author.bs.all()</span><br><span class="line">[<Book: learn java>, <Book: learn python>]</span><br></pre></td></tr></table></figure></p>
<blockquote>
<p>如果<code>book</code>表里有两个字段都外键关联<code>author</code>表,这时<code>related_name</code>就非常有用了。</p>
</blockquote>
]]></content>
<categories>
<category>程序猿</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Django</tag>