-
Notifications
You must be signed in to change notification settings - Fork 1
/
feed.xml
1818 lines (1408 loc) · 223 KB
/
feed.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"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.6.2">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2020-02-26T12:30:51+00:00</updated><id>/</id><title type="html">Bisguzar - Hobisel Pythoncu</title><entry><title type="html">Github Actions ve Jekyll</title><link href="/jekyll-github-actions" rel="alternate" type="text/html" title="Github Actions ve Jekyll" /><published>2020-02-24T00:00:00+00:00</published><updated>2020-02-24T00:00:00+00:00</updated><id>/jekyll-github-actions</id><content type="html" xml:base="/jekyll-github-actions"><p>Şu anda okuduğunuz bu yazının barındığı platforma (bloga) yıllık alan adı ücreti dışında bir ücret ödemiyorum. Çünkü zaten çok nadir yazdığım ve ziyaretçisinin de az olduğu bir platforma neden ücret ödeyeyim ki diye düşündüm. Getirisi (maddi olarak) olmayan şeyin götürüsü (yine maddi) de olmamalı. Bu yazıda da <em>Jekyll</em> ve <em>Github Actions</em> ile bu blogun yayına çıkma serüveninden bahsedeceğim.</p>
<h1 id="statik-site-üreticileri">Statik Site Üreticileri</h1>
<p><em>Static page generators</em> diye tabir edilen bir yazılım ürünleri grubu var. Bu araçlar sizin verdiğiniz kaynak dosyaları derleyip HTML dosyaları üretiyorlar. Bu yazıyı okuduğunuza göre sizin de bildiğiniz ürünlerden biri olan <em>Wordpress</em> ise dinamik bir araçtır. Yani admin paneline girip yazınızda bir şeyi değiştirebilirsiniz, ve bu direkt yazıya yansıyacaktır. Ancak statik üreticileri kullanarak bir şeyler elde ediyorsanız siz kaynağı düzenlemelisiniz ve bu üretici yazılımla kaynaktan bir derlenmiş sonuç elde etmelisiniz. Yani bir şeyleri elle yapmanız gerekiyor (şimdilik).</p>
<p>Peki bu bize ne sunuyor? Aslında cevap çok basit, sitenizi sunmak için çok çok daha az kaynağa ihtiyacınız oluyor. Çünkü zaten siz önceden her şeyi yapıp nihai ürünü elde etmiş oluyorsunuz. Geriye sadece bunu sunmak kalıyor.</p>
<p>İşte bu noktada da sunucu maliyeti ortaya çıkıyor. Neyse ki <em>Github</em>‘un <em>Github Pages</em> isimli bir çözümü var, statik dosyalarımızı (html, css, js) Github depomuzdan direkt yayınlayabiliyoruz. Yani evet, sunucuya ücret ödemiyoruz. Ayrıca kaynağımızı Github yayınlıyor, yani güzel bir sağlayıcımız var diyebiliriz. Ayrıca kaynak zaten açık olduğu için bu yazıdaki bir yanlışlığı direkt düzeltme şansınız bile oluyor. Hatta bu <a href="https://github.com/bisguzar/bisguzar.github.io/blob/master/source/_posts/2020-02-24-jekyll-github-actions.md">yazının kaynağına giderek</a> bu yazı için bir pull request oluşturursanız (bir şeyi düzeltmeniz ya da eksik bir şeyi tamamlamanızı dilerim) direkt bu blog üzerinde yazı güncellenecek. Dinamik olmayan dinamik bir blog burası.</p>
<p>Bu blog <em>Jekyll</em> adında bir statik site üreticisi tarafından derleniyor. Tema olarak da <em>Ghost</em> isimli bir CMS’in (Content Management System, Wordpress benzeri bir çözüm diyebiliriz) varsayılan temasının uyarlaması olan <em>Jasper</em>‘i kullanıyorum. Jekyll de Jasper de açık kaynaklı.</p>
<p>Bu yazıda sadece serüvenden bahsedeceğim. Bahsettiğim ürünler (jekyll, Github pages, ghost vb.) hakkında internette çok fazla kaynak zaten bulunuyor.</p>
<h1 id="yayına-alma">Yayına Alma</h1>
<p>Github Pages bize birkaç farklı yöntem sunuyor aslında sitemizi yayınlamak için. Mesela <em>gh-pages</em> adında bir branch oluşturup dosyalarımızı orada tutmamızı sağlayabiliyor. Kendisi de oradan yayına alıyor. Ya da aynı şekilde <em>master</em> branch’ındaki <strong>docs/</strong> klasörüne bakabiliyor. Ya da direkt <em>master</em> branchına bakıp ne gördüyse ordan yayına alabiliyor. Ben son bahsettiğim yöntemi kullanıyorum. Bunun çok basit bir sebebi var.</p>
<p>Aslında Github jekyll’i kendi içinde zaten destekliyor. Yani jekyll’i kendisi derleyip yayına alabiliyor ve bunun için sizin nerdeyse hiçbir şey yapmanıza gerek yok. Bu konu hakkında da internette yeterince kaynak var. E o zaman bu yazı neden var? Çünkü benim senaryomda Github’un direkt desteklemesi ve kendisinin derleyip yayına alması yetersiz kaldı. Ben bazı ek Jekyll eklentileri kullanmak durumunda kaldım. Ve Github, Jekyll eklentilerini desteklemiyor :(. Ve ortaya çıkardığı sitede bazı sayfalar yanlış üretilebiliyor ya da hiç üretilmiyordu. Dolayısıyla benim bu üretme işlemini kendim yapmam gerekiyordu ama her yazı yayınladığımda bilgisayarımda derlemek de işkence olabiliyor. Çünkü farklı bir cihazdan bir şey yayınlamam gerekse önce Ruby’yi indirmem gerekecek (Jekyll’in geliştirildiği programlama dili) daha sonra bağımlı olduğu paketleri kurmam gerekecek.</p>
<p>Github’un Jekyll’i tanımaması ve kendi kendine derlemeye çalışmaması için ana dizine <strong>.nojekyll</strong> adında bir dosya oluşturmamız gerekiyor. Bunu oluşturduğumuz zaman da sadece üçüncü seçenekte bahsettiğimiz gibi kullanmamıza izin veriyor Github Pages’i.</p>
<p>Tam bu işleri otomatize etmek için aslında CI/CD toollarını kullanıyoruz. Önce Travis’i kullanıyordum. Daha sonra Github, Actions isimli çözümünü piyasaya sürünce ona geçirmeyi düşündüm altyapıyı. Altyapı diyorsam da üç-beş komuttan ibaret her şey. Epey vakit geçtikten sonra bunu gerçekleştirdim. Github Actions hakkında Türkçe kaynaklar çok zengin değil ama Github’un kendi dökümanının giriş kısmını okusanız bile bu yazıyı yayına çıkarmak için hazırladığım Action’u anlayabilirsiniz. Actions’u bir eylem gerçekleştiğinde (mesela yeni bir yazı yazdığınızda) belli komutları (bizim önceden tanımladığımız) çalıştıran bir bilgisayar gibi düşünebilirsiniz.</p>
<p>Bize yazı yazdığımız zaman blogumuzu Jekyll ile üretecek ve daha sonrasında da bunu yine depoya gönderecek bir action lazım. Çünkü Github Pages yine depodan okuyacak ve yayınlayacak.</p>
<p>Burada parça parça inceleyeceğimiz action dosyamızın tamamına ve şu anda çalışan <a href="https://github.com/bisguzar/bisguzar.github.io/blob/master/.github/workflows/main.yml">güncel versiyonuna ulaşmak için tıklayınız</a>. (İleride action’u güncellersem burayı güncelleyeceğimin garantisini veremem…)</p>
<p>Action dosyalarımız YAML formatında, dolayısıyla okumak kolay. Adım adım incelemeye başlayalım.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="na">name</span><span class="pi">:</span> <span class="s">Build Blog</span>
<span class="na">on</span><span class="pi">:</span>
<span class="na">push</span><span class="pi">:</span>
<span class="na">branches</span><span class="pi">:</span> <span class="s">master</span>
<span class="na">paths</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s2">"</span><span class="s">source/**"</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Burada ilk satırda actionumuza bir isim veriyoruz. Bu action blogumuzu oluşturacağı için ben <em>Build Blog</em> dedim. Daha sonraki satırlarda da <em>push</em> işlemi gerçekleştiğinde, yani depoya bir şeyler geldiğinde çalışacağını belirtmişim. Ancak bu depoda sadece yazılar yok, bu action da blogu derliyorsa yazılar haricindeki değişikliklerde çalışmamalı. O yüzden biz de <em>branches</em> ve <em>paths</em> parametrelerini tanımlıyoruz ve sadece <strong>master</strong> branchındaki <strong>source/</strong> klasörüne dosya gönderilirse çalış diyerek kısıtlıyoruz. Burası tam da bizim blogumuzun kaynağının bulunduğu klasör.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="na">jobs</span><span class="pi">:</span>
<span class="na">build</span><span class="pi">:</span>
<span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
<span class="na">steps</span><span class="pi">:</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Devam eden bu satırlarda da <em>jobs</em> yani actionumuzun görevlerini tanımlamaya başlıyoruz. Ben ilk göreve <em>build</em> adını vermişim. Ve bu görevin ubuntu ortamında çalışmasını istemişim. Actionsa bizim için komutlar çalıştıran bilgisayar diyebiliriz demiştik. Burada bizim bilgisayarımız ubuntu işletim sistemine sahip olsun istedik. Daha sonra da <em>steps</em> kısımında bu görevde çalışacak adımları teker teker tanımlayacağız.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Ruby</span>
<span class="na">run</span><span class="pi">:</span> <span class="s">sudo apt install ruby-full</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Insall Bundler</span>
<span class="na">run</span><span class="pi">:</span> <span class="s">sudo gem install bundler</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>İlk iki adımımızı hazırladık, aslında bu iki adım da aynı amacı güdüyor. Bağımlılıkları kurmak. İlk adım ruby dilini sisteme kuruyor, ikinci adım da ruby için bir paket yöneticisi olan bundler’i kuruyor. Bunları tek adımda kurdurmak da mümkün tabi ancak actions’un akış diyagramında ayrı ayrı görmek ve bir hata alınırsa daha kolay müdahale edebilmek adına daha iyi oluyor.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="no">cd source</span>
<span class="no">bundle install </span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>3. adımımızda da bundler ile ihtiyaç duyduğumuz paketleri kurduruyoruz (jekyll ve kullandığı diğer eklentileri). Bu adımımız farkettiyseniz iki ayrı komut içeriyor. Çünkü bundlerin kullanacağı Gemfile dosyamız source klasöründe. Dolayısıyla önce dizini değiştirmemiz gerekiyor.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build blog</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="no">cd source</span>
<span class="no">bundle exec jekyll build</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Sonunda blogumuzun dosyalarını üreteceğimiz adıma geldik. Bu adımda da yine kaynak dosyalarının bulunduğu dizine geçtik ve bundler üzerinde <em>jekyll build</em> komutunu çalıştırdık. Bu komut çalıştıktan sonra Jekyll yine aynı dizinde <em>_site/</em> klasörü oluşturuluyor ve kaynaktan ürettiği dosyaları oraya koyuyor. Biz de bir sonraki adımlarda bu dosyaları Github Pages’e vereceğiz ki sitemiz yayına alınabilsin.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install SSH Client</span>
<span class="na">uses</span><span class="pi">:</span> <span class="s">webfactory/ssh-agent@v0.2.0</span>
<span class="na">with</span><span class="pi">:</span>
<span class="na">ssh-private-key</span><span class="pi">:</span> <span class="s">$</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Tabi ürettiğimiz dosyaları yine depoya göndereceğimiz için Github’a kendimizi tanıtmamız ve yetkili birisi olduğumuza kendisini ikna etmemiz gerekiyor. Bunun için hazırlanmış olan farklı bir actionu çağırıyoruz ve ilgili parametreyi veriyoruz. Bizim için o action gerekli işlemleri yapıyor. Evet, action-in-action.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Initialize Git</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="no">git config --global user.email "bot@bisguzar.com"</span>
<span class="no">git config --global user.name "Deploy Bot"</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Bu adımda da klasik git ayarlamalarımızı yapıyoruz.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Source</span>
<span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
<span class="no">cp -r source/_site/* ./</span>
<span class="no">rm -r source/_site</span>
<span class="no">git add .</span>
<span class="no">git commit -m "🤖 Built and deployed automatically by actions." </span>
<span class="no">git push</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Blogumuzu yayına aldığımız asıl adı burası. Burada sırasıyla Jekyll’in ürettiği dosyaları ana dizine kopyaladık. Çünkü daha önce de bahsettiğim gibi, Github Pages ana dizinden yayına alıyor. Daha sonrasında üretilen dosyaları sildik, çünkü aynı anda iki yerde olmalarına ne gerek var? Bunlardan sonra da değişikliklerimizi git’e bildirdik, mesajımızı yazdık ve depoya gönderdik.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre> <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Finish</span>
<span class="na">run</span><span class="pi">:</span> <span class="s">echo "All done!"</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Evet, bu da son adımımız. Her şey bitti, sorun yaşamadık demek. Sadece ekrana “All done!” yazdırıyor.</p>
<p><a href="https://github.com/bisguzar/bisguzar.github.io/runs/465645033">Bu yazı yayına çıkarken çalışan action’u adım adım incelemek için tıklayın, ve evet, bunu sonradan ekliyorum. Çünkü actionun çalışması için önce yazıyı göndermem gerekiyordu.</a></p>
<p>Cover photo by <a style="background-color:black;color:white;text-decoration:none;padding:4px 6px;font-family:-apple-system, BlinkMacSystemFont, &quot;San Francisco&quot;, &quot;Helvetica Neue&quot;, Helvetica, Ubuntu, Roboto, Noto, &quot;Segoe UI&quot;, Arial, sans-serif;font-size:12px;font-weight:bold;line-height:1.2;display:inline-block;border-radius:3px" href="https://unsplash.com/@yancymin?utm_medium=referral&amp;utm_campaign=photographer-credit&amp;utm_content=creditBadge" target="_blank" rel="noopener noreferrer" title="Download free do whatever you want high-resolution photos from Yancy Min"><span style="display:inline-block;padding:2px 3px"><svg xmlns="http://www.w3.org/2000/svg" style="height:12px;width:auto;position:relative;vertical-align:middle;top:-2px;fill:white" viewBox="0 0 32 32"><title>unsplash-logo</title><path d="M10 9V0h12v9H10zm12 5h10v18H0V14h10v9h12v-9z"></path></svg></span><span style="display:inline-block;padding:2px 3px">Yancy Min</span></a></p></content><author><name>Buğra İşgüzar</name></author><category term="devops" /><summary type="html">Şu anda okuduğunuz bu yazının barındığı platforma (bloga) yıllık alan adı ücreti dışında bir ücret ödemiyorum. Çünkü zaten çok nadir yazdığım ve ziyaretçisinin de az olduğu bir platforma neden ücret ödeyeyim ki diye düşündüm. Getirisi (maddi olarak) olmayan şeyin götürüsü (yine maddi) de olmamalı. Bu yazıda da Jekyll ve Github Actions ile bu blogun yayına çıkma serüveninden bahsedeceğim.</summary></entry><entry><title type="html">Flask Blueprint Yapısı</title><link href="/flask-blueprint" rel="alternate" type="text/html" title="Flask Blueprint Yapısı" /><published>2018-08-29T13:41:03+00:00</published><updated>2018-08-29T13:41:03+00:00</updated><id>/flask-blueprint</id><content type="html" xml:base="/flask-blueprint"><p><strong>İçerik Listesi</strong></p>
<ol id="markdown-toc">
<li><a href="#blueprint-nedir" id="markdown-toc-blueprint-nedir">Blueprint Nedir?</a></li>
<li><a href="#i̇lk-blueprintimizi-oluşturalım" id="markdown-toc-i̇lk-blueprintimizi-oluşturalım">İlk Blueprintimizi Oluşturalım</a></li>
<li><a href="#proje-dosya-yapısı" id="markdown-toc-proje-dosya-yapısı">Proje Dosya Yapısı</a> <ol>
<li><a href="#fonksiyonel-yapı" id="markdown-toc-fonksiyonel-yapı">Fonksiyonel Yapı</a></li>
<li><a href="#bölünmüş-yapı" id="markdown-toc-bölünmüş-yapı">Bölünmüş Yapı</a></li>
<li><a href="#hangisini-daha-i̇yi" id="markdown-toc-hangisini-daha-i̇yi">Hangisini Daha İyi?</a></li>
</ol>
</li>
<li><a href="#i̇leri-seviye-kullanım" id="markdown-toc-i̇leri-seviye-kullanım">İleri Seviye Kullanım</a> <ol>
<li><a href="#url-ön-eki" id="markdown-toc-url-ön-eki">URL Ön Eki</a></li>
</ol>
</li>
<li><a href="#kaynaklar" id="markdown-toc-kaynaklar">Kaynaklar</a></li>
</ol>
<hr style="margin: 10px !important;" />
<h1 id="blueprint-nedir">Blueprint Nedir?</h1>
<p>Blueprint kelime olarak <em>taslak</em> anlamına geliyor.</p>
<p>Eğer daha önce <em>Django</em> kullandıysanız <em>Django</em> projelerinin uygulamalardan (apps) oluştuğunu deneyimlemişsinizdir. Örneğin bir sosyal medya sitesi hazırlıyorsunuz, bu sitede üyelik işlemlerini (kayıt ol, giriş yap, profil) gerçekleştirmesi için bir alt-uygulama, ana yapı (dashboard) için bir uygulama oluşturabiliriz. Böylelikle büyük bir projeyi parçalara bölerek organizasyonu kolaylaştırıp modüler bir yapı elde edebiliriz.</p>
<p>Aynı seneryoyu <strong>Flask</strong>da da yapabiliriz. Yani projemizi parçalar halinde oluşturabilir böylelikle proje yönetimini kolaylaştırabiliriz.</p>
<h1 id="i̇lk-blueprintimizi-oluşturalım">İlk Blueprintimizi Oluşturalım</h1>
<p>Flask proje klasörümüz içinde yeni bir dosya oluşturalım ve adını dashboard_core.py koyalım (siz kendinize göre isim verebilirsiniz tabiki, isimlendirme yaparken işlevini en net ve kısa şekilde belirtmesine dikkat etmeliyiz. ‘There are only two hard things in Computer Science: cache invalidation and naming things.’ - Phil Karlton).</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Blueprint</span>
<span class="n">dashboard</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">'dashboard'</span><span class="p">,</span> <span class="n">__name__</span><span class="p">)</span>
<span class="nd">@dashboard.route</span><span class="p">(</span><span class="s">'/dashboard'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">homepage</span><span class="p">():</span>
<span class="k">return</span> <span class="s">'Şu an dashboard app</span><span class="se">\'</span><span class="s">ının içindeyim'</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Bu kadarcık tanımlamayla aslında bir app (<em>app</em> kelimesini projenin bir bölümü, alt uygulaması anlamında kullandığımızı hatırlayalım) oluşturabiliyoruz. Şimdi biraz bu satırları inceleyelim. Projemizi flask ile hazırladığımız için yine flask’ın app oluşturmak için kullandığımız kütüphanesi olan <em>Blueprint</em>‘i projemize dahil ediyoruz.</p>
<p>4. satırda ise projemize dahil ettiğimiz <em>Blueprint</em> sınıfının bir örneğini oluşturuyoruz. Aslında tam bu aşamada app’ımızı hazırlıyoruz. Verdiğimiz ilk değer app’ımızın adı. İkinci değer ise içe aktarılırken kullanılacağı parametre. Onun haricindeki tüm değerler tercihe bağlı olarak tanımlanabilir. Sonraki satırlar zaten flask kullanımından alışkın olduğumuz şey, bir fonksiyon tanımlayıp onu da bir adrese bağladık. Burada dikkat edilmesi gereken şey <em>route</em> decaratorünün oluşturduğumuz app örneğinden aldığımız (ben ana dosyadaki flask’ın Flask sınıfının örneğini de buraya aktarıp import loopa sokmuştum, sonra insan iki gün sorun ne diye arıyor).</p>
<p>Şu anda aslında bir app oluşturduk, oluşturduğumuz app’ı flask’a haber vermemiz gerekiyor. Bunun için ana flask sınıfının <strong>register_blueprint</strong> fonksiyonunu kullanacağız. Ana flask sınıfından kastım <em>app = Flask(__name__)</em> tanımlamasını yaptığımız dosya (değişkenin adını farklı belirlemiş olabilirsiniz). Bu dosyaya app’ımızı dahil ediyoruz. Eğer ikisinin app dosyamız ile bu dosyanın aynı dizinde olduğunu varsayarsak <strong>from dashboard_core import dashboard</strong> diyerek içe aktarma işlemimizi yapabiliriz. Daha sonrasında ise tanımlama işlemi kaldı. Onun için de <strong>app.register_blueprint(dashboard)</strong> dememiz yeterli.</p>
<p>Şimdi projenizi çalıştırıp /dashboard sayfasına girerseniz oluşturduğunuz app’ın içindeki fonksiyonun döndürdüğü değeri göreceksiniz. Buraya kadar herhangi bir hatayla karşılaşmadıysanız başarıyla bir uygulama (app) oluşturdunuz.</p>
<h1 id="proje-dosya-yapısı">Proje Dosya Yapısı</h1>
<p>Eğer projemizi modüller halinde inşa edeceksek dosyalamayı da çok düzenli yapmalıyız, örneğin A app’ının template dosyaları B app’ının dizininde durmamalı. Onlarca app bulunan bir projede böyle bir durum çıkmaza yol açacaktır. Bunun için sizlere en çok tercih edilen iki yöntemi göstereceğim. Tabii siz kendi istediğiniz yapıyla da dosylama yapabilirsiniz. Ancak her zaman standartlaşmış yöntemleri kullanmak yararımıza olacaktır. En azından projeyi sonradan başka birisi geliştirmek durumunda kalırsa sizi daha az anacaktır.</p>
<p>Üç app’den oluşan bir projemiz olduğunu düşünelim. Sahip olduğumuz appler <em>userManagement</em>, <em>adminPanel</em> ve <em>dashboard</em> olsun. Aşağıdaki örnekleri de bu isimlendirmeleri kullanarak oluşturacağım.</p>
<h2 id="fonksiyonel-yapı">Fonksiyonel Yapı</h2>
<p>Bu yapıda tüm applikasyon dosyalarınız ve bunların kullandığı statik (resim dosyaları, stil dosyaları vs.) dosyalar aynı dizinde bulunurken, applikasyonların tema dosyaları ise templates klasörü içinde kendi aralarında dosyalanmış şekilde bulunur.</p>
<p>eşsizprojem/<br />
__init__.py<br />
static/<br />
templates/<br />
userManagement/<br />
adminPanel/<br />
dashboard/<br />
views/<br />
__init__.py<br />
userManagement.py<br />
adminPanel.py<br />
dashboard.py<br />
models.py</p>
<p>Proje dosyanız bu şekilde görünmeli, tabi eğer fonksiyonel dosyalama yapısını kullanmaya karar verdiyseniz. Bu durumda dashboard app’ını ele alalım. Bu app’ın back-end kodları, yani view kodları <em>dashboard.py</em> dosyasında olacak. Statik dosyları <em>static</em> klasöründe, tema/şablon dosyaları ise <em>templates/dashboard/</em> klasöründe olacak.</p>
<p class="note">
Bu yazı hazırlandığı sıralarda flask'ın resmi sitesi de (http://flask.pocoo.org) bu yapıyı kullanıyordu, hala kullanıyor da olabilir. <a href="https://github.com/pallets/flask-website/tree/master/flask_website">GitHub üzerinde</a> canlı görebilirsiniz.
</p>
<h2 id="bölünmüş-yapı">Bölünmüş Yapı</h2>
<p>Bölünmüş yapıda applerin her birini tüm kendi alt dosyaları ile birlikte özel bir klasörde tutuyoruz. Çok karışık bir cümle oldu ama örnek üzerinde baktığınızda çok basit olduğunu göreceksiniz.</p>
<p>eşsizprojem/<br />
__init__.py<br />
<strong>userManagement</strong>/<br />
__init__.py<br />
views.py<br />
static/<br />
templates/<br />
<strong>adminPanel</strong>/<br />
__init__.py<br />
views.py<br />
static/<br />
templates/<br />
<strong>dashboard</strong>/<br />
__init__.py<br />
views.py<br />
static/<br />
templates/<br />
models.py</p>
<p>En üst seviyedeki <em>__init__.py</em> dosyasında oluşturduğumuz Flask nesnesi ise bu tüm appleri kapsayacak ve projeyi bağlayıcı rol alacak.</p>
<h2 id="hangisini-daha-i̇yi">Hangisini Daha İyi?</h2>
<p>Bu konu tamamen kişisel tercihlere bağlı. Yukarıda örneği bulunan iki yapı dışında da tamamen kendi oluşturacağınız bir yapıyı kullanarak bir proje inşa edebilirsiniz. Ancak ortak bir dil oluşturabilmek adına -tıpcıların latince kullanması gibi- benimsenmiş yapıları kullanmak her zaman daha avantajlı bir durum olacaktır. Bu yapılardan hangisini kullanacağınız ise sizin projenize hangisinin daha uygun olduğunu düşünmenize göre değişir.</p>
<p>Projeniz birden fazla ana parçadan oluşacaksa (yönetici paneli, kullanıcı paneli, ana sayfa) bölünmüş yapıyı kullanmanız ve her bölümün diğerlerinden tamamen bağımsız olmasını sağlamanız daha makul bir yol olacaktır. Ancak projenizdeki parçalar birbirinden kalın çizgilerle ayrılmıyorsa, örneğin applerinizin templateleri ortak bir stil dosyası (style.css) kullanıyorsa fonksiyonel dosyalama yapısı daha kullanışlı olabilir.</p>
<h1 id="i̇leri-seviye-kullanım">İleri Seviye Kullanım</h1>
<p>Flask’ın blueprint yapısı zaten yeterince kullanışlı değilmiş gibi ek olarak kullanabileceğimiz bazı özellikleri mevcut.</p>
<h2 id="url-ön-eki">URL Ön Eki</h2>
<p>Blueprintimizi tanımlarken url_prefix niteliği tanımlamazsak bu değer varsayılan olarak / olacaktır. <strong>profil</strong> adında bir app oluşturduğumuz senaryosunu ele alalım. Bu app’ın alt sayfaları olarak ayarlar, resimler ve gonderiler sayfaları olmasını istiyoruz. Yani elimizde /profil, /profil/ayarlar, /profil/resimler, /profil/gonderiler seklinde 4 ayrı sayfa olmalı. Biz app’ın içinde sayfalarımızı tanımlarken urllerimizi bu şekilde tanımlayabileceğimiz gibi blueprinte bir URL ön eki verirsek kendimizi tekrarlamaktan kurtulmuş oluruz.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">profil</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">'profil'</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">'/profil'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Şeklinde bir blueprint tanımlaması yaptığımızı düşünelim. Daha sonra da bu blueprintimizi kullanarak resimler sayfamızı oluşturalım.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nd">@profil.route</span><span class="p">(</span><span class="s">'/resimler'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">resimler</span><span class="p">():</span>
<span class="c"># Bir şeyler yap</span>
<span class="k">pass</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Bu şekilde oluşturduğumuz sayfamıza erişmek istediğimiz zaman sitemiz.uzantısı/profil/resimler adresine gitmemiz gerekiyor. Sebebi ise sayfamıza (diğer tanımlamayla fonksiyonumuza) /resimler URL’sini bağladık. Ancak sayfamızın dahil olduğu blueprint de /profil URL’sine bağlı. Bu durumda bu sayfaya erişmek isteyen bir kişi öncelike blueprintin bağlı olduğu noktaya erişecek sonrasında ise sayfaya erişecek diyebiliriz. Tabi olay tam olarak böyle işlemiyor, biz senaryolaştırıyoruz.</p>
<p>Şimdi durumu biraz daha karmaşıklaştıralım. Bu örneğimizi facebookdaki gibi bir duruma refactor edelim (tekrar düzenleyelim). Facebookda bu durum nasıl işliyor hatırlayalım. Örneğin johndoe kullanıcı adında biri oldun. Onun resimlerine girebilmek için facebook.com/profil/resimler adresine gitmiyoruz, bunun için kullanmamız gereken adres facebook.com/johndoe/resimler oluyor. Burada farkettiyseniz URL yapısı içerisinde bir kullanıcının kullanıcı adını kullandık. Yani orası değişkenlik gösterebiliyor demek bu. Orası aynı şekilde <strong>joandoe</strong> de olabilirdi. Bu yapıya dinamik URLler diyoruz. Orasını değişebilir kılıyoruz ve ne olduğunu da bir değişken olarak alıyoruz. Böyle bir yapıyı url_prefix niteliğini kullanmadan hazırlayalım hemen.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nd">@profil.route</span><span class="p">(</span><span class="s">'/&lt;username&gt;/resimler'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">resimler</span><span class="p">(</span><span class="n">username</span><span class="p">):</span>
<span class="c"># Bir şeyler yap</span>
<span class="k">pass</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Tekrarlıyorum, <em>url_prefix</em> tanımlaması yapılmadığını varsayıyoruz şu an bu nitelik varsayılan olarak / adresine tanımlı. Yani kısaca tanımlanmamış da diyebiliriz. Eğer tarayıcınızdan sitemiz.uzantısı/<strong>johndoe</strong>/resimler adresine giderseniz <strong>resimler</strong> adındaki fonksiyonumuzun username değeri ‘johndoe’ye eşit olacak. Bunu da fonksiyon içerisinde herhangi bir yerde kullanabiliriz.</p>
<p>Bu şekilde kullanımı ayarlar, resimler ve gönderiler sayfaları için teker teker uygulayabiliriz ancak kendimizi tekrarlamış oluruz. url_prefix niteliğini tanımlarken dinamik URL yapıları kullanabiliyoruz. Şimdi o şekilde kodumuzu yeniden düzenleyelim.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Blueprint</span><span class="p">,</span> <span class="n">g</span>
<span class="n">profil</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">'profil'</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">'/&lt;kadi&gt;'</span><span class="p">)</span>
<span class="nd">@profil.route</span><span class="p">(</span><span class="s">'/resimler'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">resimler</span><span class="p">():</span>
<span class="k">pass</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Şu anda appımız bu şekilde. <strong>kadi</strong>‘yi kullanıcı adının kısaltması olarak kullanıyorum. Gördüğünüz gibi bu sefer dinamik URL tanımlamasını blueprintimizi oluştururken url_prefix niteliğinde yaptık. O yüzden <strong>resimler</strong> fonksiyona bir parametre olarak gelmeyecek bu sefer. Ayrıca yakalamamız gerekecek. Bunun için blueprint sınıfının <em>url_value_preprocessor()</em> fonksiyonundan (decarator) yararlanabiliriz. Ayrıca farkettiyseniz bir üstteki örnekte <strong>g</strong> sınıfını da içeri aktardık, ona da birazdan değineceğim.</p>
<p>Şimdi de sayfa fonksiyonlarımız (örnekteki <em>resimler</em> fonksiyonu gibi) çalışmadan önce kullanıcıdan gelen URL’yi işleme tabii tutacak fonksiyonumuzu hazırlayalım. Böylelikle gelen dinamik veriyi tutup ayıklayabiliriz.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Blueprint</span><span class="p">,</span> <span class="n">g</span>
<span class="n">profil</span> <span class="o">=</span> <span class="n">Blueprint</span><span class="p">(</span><span class="s">'profil'</span><span class="p">,</span> <span class="n">__name__</span><span class="p">,</span> <span class="n">url_prefix</span><span class="o">=</span><span class="s">'/&lt;kadi&gt;'</span><span class="p">)</span>
<span class="nd">@profil.url_value_preprocessor</span>
<span class="k">def</span> <span class="nf">url_ayiklayici</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
<span class="n">g</span><span class="o">.</span><span class="n">kadi</span> <span class="o">=</span> <span class="n">values</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s">'kadi'</span><span class="p">)</span>
<span class="nd">@profil.route</span><span class="p">(</span><span class="s">'/resimler'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">resimler</span><span class="p">():</span>
<span class="k">pass</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Hemen bu örneğimizi de açıklayıp bu başlığı da kapatalım. 7. satırda yeni bir fonksiyon tanımladık. 6. satır tamamen bu fonksiyonu kontrol etmesi için bir decarator çağırmaktan ibaret. Decarator yapısını ayrıca öğrenmenizi şiddetle tavsiye ediyorum. Bu tanımladığımız fonksiyon bazı parametreler alıyor (bu parametreler decarator tarafından veriliyor), ve bunlardan biri de URL’deki dinamik veriler. 8. satırda da bu parametrenin içinden istediğimiz veriyi alıp onu <strong>g</strong> nesnesinin <strong>kadi</strong> niteliğine eşitledik. Yani bir değişken tanımlaması yaptık diyebiliriz, ancak değişkeni <strong>g</strong> sınıfının içine tanımladık. <a href="http://flask.pocoo.org/docs/0.12/appcontext/#locality-of-the-context"><strong>g</strong> sınıfı hakkında detaylı bilgi için tıklayın</a></p>
<h1 id="kaynaklar">Kaynaklar</h1>
<ul>
<li>http://flask.pocoo.org/docs/1.0/blueprints/</li>
<li>http://exploreflask.com/en/latest/blueprints.html</li>
</ul></content><author><name>Buğra İşgüzar</name></author><category term="flask" /><summary type="html">İçerik Listesi</summary></entry><entry><title type="html">Flask ile Blog Yapalım</title><link href="/flask-ile-blog" rel="alternate" type="text/html" title="Flask ile Blog Yapalım" /><published>2018-08-02T17:09:00+00:00</published><updated>2018-08-02T17:09:00+00:00</updated><id>/flask-ile-blog</id><content type="html" xml:base="/flask-ile-blog"><p><strong>İçerik Listesi</strong></p>
<ol id="markdown-toc">
<li><a href="#giriş" id="markdown-toc-giriş">Giriş</a></li>
<li><a href="#i̇nternet-siteleri-nasıl-çalışır" id="markdown-toc-i̇nternet-siteleri-nasıl-çalışır">İnternet Siteleri Nasıl Çalışır</a></li>
<li><a href="#pipenv-kurulumu-ve-kullanımı" id="markdown-toc-pipenv-kurulumu-ve-kullanımı">Pipenv Kurulumu ve Kullanımı</a></li>
<li><a href="#flask-kullanmaya-başlayalım" id="markdown-toc-flask-kullanmaya-başlayalım">Flask Kullanmaya Başlayalım</a></li>
<li><a href="#site-i̇skeletini-oluşturalım" id="markdown-toc-site-i̇skeletini-oluşturalım">Site İskeletini Oluşturalım</a> <ol>
<li><a href="#şablonlarımızı-güzelleştirelim" id="markdown-toc-şablonlarımızı-güzelleştirelim">Şablonlarımızı Güzelleştirelim</a></li>
</ol>
</li>
<li><a href="#veritabanı-modelleri" id="markdown-toc-veritabanı-modelleri">Veritabanı Modelleri</a></li>
<li><a href="#back-endi-hazırlayalım" id="markdown-toc-back-endi-hazırlayalım">Back-end’i Hazırlayalım</a> <ol>
<li><a href="#fonksiyonlarımızı-oluşturalım" id="markdown-toc-fonksiyonlarımızı-oluşturalım">Fonksiyonlarımızı Oluşturalım</a></li>
<li><a href="#girişçıkış-yapısını-hazırlayalım" id="markdown-toc-girişçıkış-yapısını-hazırlayalım">Giriş/Çıkış Yapısını Hazırlayalım</a></li>
<li><a href="#yazı-ekleme-sayfasını-ayarlayalım" id="markdown-toc-yazı-ekleme-sayfasını-ayarlayalım">Yazı Ekleme Sayfasını Ayarlayalım</a></li>
<li><a href="#ana-sayfayı-düzenleyelim" id="markdown-toc-ana-sayfayı-düzenleyelim">Ana Sayfayı Düzenleyelim</a></li>
</ol>
</li>
<li><a href="#devam-etmek-i̇steyenler-i̇çin" id="markdown-toc-devam-etmek-i̇steyenler-i̇çin">Devam Etmek İsteyenler İçin</a></li>
</ol>
<hr style="margin: 10px !important;" />
<h1 id="giriş">Giriş</h1>
<p>Bu yazıda web temelli uygulamalar geliştirmek için hazırlanmış olan python kütüphanesi Flask’ı kullanarak kendi blogumuzu geliştireceğiz. Bunu yaparken birkaç farklı alanda temel seviyede bilgi edinmiş olacağız. Bu alanları listelemek gerekirse;</p>
<ol>
<li>Web temelleri hakkında bilgi edineceğiz, bir web sitesinin nasıl çalıştığını öğreneceğiz</li>
<li>PIPENV’i kullanmayı öğreneceğiz</li>
<li>Flask’ı tanıyacağız</li>
<li>HTML ve CSS kullanarak kendi temalarımızı tasarlayacağız, Jinja2 tema motorunu kullanmayı öğreneceğiz</li>
<li>Veritabanı modellerimizi hazırlarken ORM yapısını göreceğiz, peewee ORM kütüphanesini kullanacağız</li>
<li>Blogumuzun istekleri karşılayan ve yanıt üreten (terimsel olarak back-end) kısımını hazırlayacağız, tarayıcı oturumlarına değineceğiz</li>
</ol>
<p>Yine web temelli uygulamalar geliştirmek için hazırlanmış olan bir başka Python kütüphanesi <em>Django</em> ile blog geliştirmek için benzer bir doküman mevcut. Hem o doküman, hem bu doküman sizlere temel seviyede bilgi verip bu konuları araştırma heyecanı oluşturmak için hazırlandı. <a href="https://tutorial.djangogirls.org/tr/" target="_blank">djangogirls.org üzerindeki o Türkçe dokümana gitmek için tıklayın</a>. Fırsat bulursanız DjangoGirls çalışmalarından birine katılmanızı da öneririm.</p>
<p>Ancak biz Flask’ı kullanarak bu dokümanda ilerleyeceğiz. Birçok şeyi kendimiz yapmak durumunda kalacağız. Djangoda birçok kullanıma hazır yapı bulunurken Flask için ek kütüphaneler kullanarak ilerleyeceğiz. Ayrıca içerik listesinde de gördüğünüz (ya da göremediğiniz) bu dokümanda Python öğrenmeyeceğiz. Hali hazırda iyi kötü bir Python bilginizin olması gerekiyor. Djangogirls’ün hızlandırılmış Python dokümanına <a href="https://tutorial.djangogirls.org/tr/python_introduction/">ulaşmak için tıklayın</a>.</p>
<p>Ayrıca bu yazı boyunca oluşturacağımız tüm dosyalar Github’daki <strong><a href="https://github.com/bisguzar/flask-blog" target="_blank">bisguzar/flask-blog</a></strong> deposunda bulunuyor. Oradan da takip edebilirsiniz.</p>
<h1 id="i̇nternet-siteleri-nasıl-çalışır">İnternet Siteleri Nasıl Çalışır</h1>
<p><img src="https://img.labnol.org/di/how-internet-works1.jpg" alt="" /></p>
<p>İnternet sitelerinin çalışmasını anlamak için bu senaryoyu hikayeleştirelim. Öncelikle Ahmet isminde bir karakterimiz olsun ve bakkala girip bir gofret istediğini varsayalım. Böyle bir durumda gerçekleşecek bir sonraki olay bakkalın Ahmet’e istediği gofreti vermesi olacak. Olay çok basitti. Ahmet bir gofret istedi ve istediğini aldı.</p>
<p>Web siteleri de aynen böyle çalışıyor. Ancak web siteleri gofretten oluşmuyor tabikide, kaynak dosyalarını -yani html,css,js gibi dosyaları- da dolapta tutamıyoruz. Bu dosyaların saklanması gerekiyor, çünkü istek geldiği zaman isteği yapan kişiye gönderilmesi gerekiyor ki senaryomuz tamamlansın. Bu saklama işlemini ise sunucu dediğimiz (genelde) güçlü bilgisayarlar üzerinde yapıyoruz. Bu bilgisayarlara biz web sitemizi oluşturan dosyaları yüklüyoruz ve sitemize bir ziyaretçi girdiği zaman -ziyaretçi sitemize girdiği zaman aslında sunucumuza <em>‘ben bu web sitesini görmek istiyorum’</em> şeklinde bir istek gönderiyor- kaynak dosyaları kendisine gönderip sitemizi görüntülemesini sağlıyoruz.</p>
<p>Yani siz github.com adresini tarayıcınızın bağlantı çubuğuna girdiğiniz zaman tarayıcınız github.com adresine bir istek gönderiyor. Ozaman github.com bir sunucu mu? Aslında hem evet hem hayır. Github.com bir temsili isim. Github.com aslında bir sunucunun IP adresini belirtiyor, ancak tarayıcıya <em>192.30.253.112</em> yazmayı mı yoksa <em>github.com</em> yazmayı mı tercih edersiniz? Daha okunaklı olabilmesi için domain ismi verilen bu yönlendirici yapılar kullanılıyor.</p>
<p>Bu oluşturduğumuz senaryoyu unutmayın, ilerleyen konuları işlerken de bu senaryo üzerinden örneklendirmeler yapacağız.</p>
<h1 id="pipenv-kurulumu-ve-kullanımı">Pipenv Kurulumu ve Kullanımı</h1>
<p>Eğer işletim sisteminiz Linux ise Python’un kurulu geldiğini farketmişsinizdir. Çünkü Linux dağıtımlarının bir çoğunda Python yoğun olarak kullanılıyor. Dolayısıyla bazı kütüphaneler de sisteminizde zaten kurulu durumda. Siz de projenizde o kütüphaneyi hemen kullanmaya başlayabilirsiniz. Böyle bir durumda herhangi bir sorun yok gibi görülüyor. Ancak durum malesef öyle değil, örnek vererek ilerleyelim. Ubuntu kullananların tanıyacağı “apt-add-repository” paketini sisteminize kurmak istediğinizi varsayalım. Bu paket python-requests kütüphanesini kullanıyor. Ancak paketin kullandığı kütüphane sürümü 1.5. Aynı zamanda geliştirdiğiniz bir uygulamanın da python-requests kütüphanesi kullandığını varsayalım. Doğal olarak son sürümü kullanıyor olacaksınız. Şuanda kütüphanenin son sürümü 2.19.1. Sistemde zaten ‘apt-add-repository’ kurulumundan dolayı bulunan bu kütüphanesi son sürüme güncellerseniz ‘apt-add-repository’ paketi çalışamaz duruma gelecek. Çünkü python-requests kütüphanesinde 1.5 sürümünden bu yana köklü değişiklikler yapıldı. Bu tarz uygulamalar arası uyum sorunlarını çözmek için sanal yöntemler kullanıyoruz.</p>
<p>Pipenv ise bu sanallaştırma için şuanda en fazla önerilen yöntem. Alternatifi olarak virtualenv gösterilebilir ancak pipenv de zaten temelde virtualenv’i kullanıyor. Sadece daha basitleştiriyor işlemleri.</p>
<p>Ruby’nin paket yöneticisi bundle veya nodejs’in paket yöneticisi npm kullandıysanız zaten çok kolay adapte olabileceğiniz bir yapısı var.</p>
<p>Sisteminizde zaten python ve pip’in kurulu olduğunu varsayarak pipenv’i kurmak için pip’i kullanabiliriz.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>pip3 install pipenv
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Şimdi projemizi geliştireceğimiz bir klasör oluşturalım. Ben masaüstünde <em>flask-blog</em> adında bir kütüphane oluşturuyorum. Daha sonra terminali açıp (eğer windows kullanıyorsanız cmd’yi) bu klasörün içine girelim. Şimdi burada pipenv için bir ortam oluşturmalıyız. Eğer bir paket kurmak istersek ve orada pipenv ortamı yoksa ilk kurulumu pipenv bizim için yapacak. pipenv kullanarak Flask kütüphanesini <em>flask-blog</em> klasörümüzün içinde kuralım.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>pipenv install flask
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Mevcut bir pipenv ortamı olmadığı için bir ortam oluşturacak. Burası biraz zaman alabilir, beklememiz gerekiyor.</p>
<p>Eğer herhangi bir sorunla karşılaşmadıysak aşağıdaki gibi bir çıktı almış olmalıyız. (çıktıyı biraz kısalttım)</p>
<div class="language-posh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre>Creating a virtualenv <span class="k">for </span>this project...
Pipfile: C:\Users\bisguzar\Pipfile
<span class="k">Using </span>c:\users\bisguzar\appdata\local\programs\python\python37-32\python.exe <span class="o">(</span>3.7.0<span class="o">)</span> to create virtualenv...
Already <span class="k">using </span>interpreter c:\users\bisguzar\appdata\local\programs\python\python37-32\python.exe
<span class="k">Using </span>base prefix <span class="s1">'c:\\users\\bisguzar\\appdata\\local\\programs\\python\\python37-32'</span>
<span class="nb">.</span>
<span class="nb">.</span>
Adding flask to Pipfile<span class="s1">'s [packages]...
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock (662286)!
Installing dependencies from Pipfile.lock (662286)...
================================ 6/6 - 00:00:02
To activate this project'</span>s virtualenv, run pipenv shell.
Alternatively, run a <span class="nb">command </span>inside the virtualenv with pipenv run.
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Sadece <strong>pipenv install</strong> komutunu çalıştırarak içinde hiçbir kütüphane kurulmamış, temiz bir pipenv ortamı da kurabiliriz. Ancak zaten biz Flask’ı kuracaktık. İki işlemi bir arada yapmış olduk.</p>
<p>Kurduğumuz kütüphane aslında sisteme kurulmadı. O yüzden python’u çalıştırıp <em>import flask</em> dersek hata almamız olası (eğer sisteme daha önceden Flask kurmadıysak!). Bunu test etmek için terminale <em>python</em> yazarak Python’un etkileşimli kabuğuna girelim ve <em>import flask</em> komutunu verelim.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="n">C</span><span class="p">:</span>\<span class="n">Users</span>\<span class="n">bisguzar</span>\<span class="n">Desktop</span>\<span class="n">flask</span><span class="o">-</span><span class="n">blog</span>
<span class="err">λ</span> <span class="n">python</span>
<span class="n">Python</span> <span class="mf">3.7</span><span class="o">.</span><span class="mi">0</span> <span class="p">(</span><span class="n">v3</span><span class="o">.</span><span class="mf">7.0</span><span class="p">:</span><span class="mi">1</span><span class="n">bf9cc5093</span><span class="p">,</span> <span class="n">Jun</span> <span class="mi">27</span> <span class="mi">2018</span><span class="p">,</span> <span class="mo">04</span><span class="p">:</span><span class="mo">06</span><span class="p">:</span><span class="mi">47</span><span class="p">)</span> <span class="p">[</span><span class="n">MSC</span> <span class="n">v</span><span class="o">.</span><span class="mi">1914</span> <span class="mi">32</span> <span class="n">bit</span> <span class="p">(</span><span class="n">Intel</span><span class="p">)]</span> <span class="n">on</span> <span class="n">win32</span>
<span class="n">Type</span> <span class="s">"help"</span><span class="p">,</span> <span class="s">"copyright"</span><span class="p">,</span> <span class="s">"credits"</span> <span class="ow">or</span> <span class="s">"license"</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">flask</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s">"&lt;stdin&gt;"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span>
<span class="n">ModuleNotFoundError</span><span class="p">:</span> <span class="n">No</span> <span class="n">module</span> <span class="n">named</span> <span class="s">'flask'</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Evet, bir hata aldık. Çünkü sistemde Flask kütüphanesi kurulu değil. Ama biz az önce bir şeyler kurmuştuk, o nerede?
exit() diyerek etkileşimli kabuktan çıkalım ve proje dosyamızın içinde <strong>pipenv run python</strong> komutunu vererek Python’u bu sefer sanal ortamımızın içinde çalıştıralım ve tekrar Flask’ı içe aktarmayı deneyelim.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="n">C</span><span class="p">:</span>\<span class="n">Users</span>\<span class="n">bisguzar</span>\<span class="n">Desktop</span>\<span class="n">flask</span><span class="o">-</span><span class="n">blog</span>
<span class="err">λ</span> <span class="n">pipenv</span> <span class="n">run</span> <span class="n">python</span>
<span class="n">Python</span> <span class="mf">3.7</span><span class="o">.</span><span class="mi">0</span> <span class="p">(</span><span class="n">v3</span><span class="o">.</span><span class="mf">7.0</span><span class="p">:</span><span class="mi">1</span><span class="n">bf9cc5093</span><span class="p">,</span> <span class="n">Jun</span> <span class="mi">27</span> <span class="mi">2018</span><span class="p">,</span> <span class="mo">04</span><span class="p">:</span><span class="mo">06</span><span class="p">:</span><span class="mi">47</span><span class="p">)</span> <span class="p">[</span><span class="n">MSC</span> <span class="n">v</span><span class="o">.</span><span class="mi">1914</span> <span class="mi">32</span> <span class="n">bit</span> <span class="p">(</span><span class="n">Intel</span><span class="p">)]</span> <span class="n">on</span> <span class="n">win32</span>
<span class="n">Type</span> <span class="s">"help"</span><span class="p">,</span> <span class="s">"copyright"</span><span class="p">,</span> <span class="s">"credits"</span> <span class="ow">or</span> <span class="s">"license"</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span>
<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">flask</span>
<span class="o">&gt;&gt;&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Olması gerektiği gibi oldu ve herhangi bir hata almadık, çünkü sanal ortamımızın içinde Flask kütüphanesi kuruluydu. Neden sanal ortamlar kullanmalı ve sistem genelinde paket kurulumları yapmamalı konularını anladığımıza göre yazımıza devam edebiliriz.</p>
<h1 id="flask-kullanmaya-başlayalım">Flask Kullanmaya Başlayalım</h1>
<p>Zaten oluşturduğumuz <em>flask-blog</em> klasöründe olduğumuzu varsayarak burada <em>app.py</em> adında yeni bir dosya oluşturalım. Burada kısa bir bilgilendirme geçmek istiyorum, hangi metin editörünü kullandığınızın bir önemi yok. Terminal tabanlı bir editör olan vim de kullanabilirsiniz, Python için geliştirilmiş en donanımlı IDE’lerden biri olan PyCharm da. Tamamen size kalmış bir durum. Ben sublime-text kullanarak bu yazıyı hazırlıyorum, belki merak eden olmuştur 🙂.</p>
<p>Şimdi oluşturduğumuz <em>app.py</em> dosyasını tercih ettiğimiz editör (ya da IDE) ile açalım. Üzerinde çalışacağımız ilk satırlarımızı yazalım ve daha sonrasında üstüne konuşalım.</p>
<p><em>app.py</em></p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="nd">@app.route</span><span class="p">(</span><span class="s">'/'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">hello_world</span><span class="p">():</span>
<span class="k">return</span> <span class="s">'Hello, World!'</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">debug</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Burada ilk satırda <em>flask</em> kütüphanesinin içindeki <strong>Flask</strong> sınıfını projemize dahil ettik. Yani aslında flask kütüphanesinin içinden sadece bir parçayı kullanacağımızı, onu projemizin içine çekmesini söyledik Python Bey’e.<br />
İkinci satırda ise içeriye aktardığımız sınıf ile bir nesne oluşturduk. Sınıf, nesne terimleri biraz <em>Nesne Tabanlı Programlama</em> başlığı altına giriyor. Biz o kadar detaylı ilgilenmiyoruz şu anlık. NTP detayları için <a href="https://belgeler.yazbel.com/python-istihza/nesne_tabanli_programlama1.html">buraya tıklayabilirsiniz. (Türkçe)</a>.</p>
<p>Daha sonraki satırlarda ise bir fonksiyon tanımladık ve bu fonksiyon ‘Hello, World!’ diye bir yazı döndürüyor. Basit seviyede Python bilen herkes bunu anlayabilir ama peki ya fonksiyonun üstündeki <em>@</em> ile başlayan alan? Pythonda bu kullanıma <em>decarator</em> deniyor. Aslında o da bir fonksiyon. Farkı, üzerine yazıldığı fonksiyon üzerinde işlem yapması. Bu konu da biraz detaya giriyor. Biz bu konuyla da ilgilenmeyeceğiz. Ama ben öğrenmek istiyorum şuan derseniz <a href="https://wiki.python.org/moin/PythonDecorators">buraya tıklayarak</a> PythonWiki’ye gidebilirsiniz (İngilizce).</p>
<p><em>app.route</em> decaratorünü projemizde bol bol kullanacağız. Burada yaptığı iş aslında fonksiyonumuzun Flask projemizin bir parçası olmasını sağlamak. app objemize (2. satırda oluşturmuştuk) fonksiyonumuzu bağlıyor. Aynı zamanda kendisi bazı değerler de alabiliyor. Şu anlık gördüğümüz gibi ‘/’ değerini almış. Bu değer fonksiyonu hangi adrese bağlayacağını gösteriyor. Yani biz oraya ‘/’ yerine <em>‘/selam’</em> yazsaydık sitemizi yayına aldığımızda <em>site_adresimiz.com/selam</em> adresini bizim fonksiyonumuza bağlayacaktı. O adrese bir ziyaretçi gelip istek gönderdiği zaman bizim fonksiyonumuzun cevabı oraya ulaşacaktı.</p>
<p>Şimdi bakkal ile Ahmet’in senaryosuna geri dönelim ve Ahmet’in bakkaldan yine gofret istediğini düşünelim ama bu sefer Ülker gofret istiyor olsun. Yani bu sefer bir ailenin içinden Ülker markalı olanı istiyor. İstediği şey yine gofret. Bu örneği kendi durumumuzla ilişkilendirmemiz gerekirse biz yine siteyi görmek istiyoruz, ancak bu sefer belli bir noktasındaki dosyayı görmek istiyoruz. Peki ‘/’ ne anlama geliyor? Çünkü burada herhangi bir ibare bulunmuyor, hangi adresi belirtiyor diye düşünüyor olabilirsiniz. ‘/’ ifadesi ana noktayı belirtiyor. Yani hiçbir şeyi. Buda demek oluyor ki <em>site_adresimiz.com</em> adresine giren biri bağlantı noktası ‘/’ olan fonksiyonumuz tarafından cevap alacak.</p>
<p>Gelelim son iki satırımıza. İlkinin bir karşılaştırma satırı olduğunu görüyoruz. Bu kısım eğer dosyanın kendisini çalıştırmışsak anlamına geliyor. Bu biraz karışık gelmiş olabilir. Ama bazı durumlarda <em>app.py</em> dosyamızı başka bir .py dosyasının içine aktarmamız gerekebiliyor. Bu durumlarda da projemizin çalışmaması için sadece app.py’nin kendisini çalıştırdığımızda projenin ayağa kalkmasını<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup> istediğimizi belirtiyoruz. Son satır da kendisini belli ettiği üzere projemizi çalıştırıyor. debug parametresini de True yaparak olası hatalarda tam bir hata mesajı göstermesini sağlıyoruz.</p>
<p>Şimdi projemizi çalıştırıp tarayıcıda görüntüleyebiliriz! Ama unutmayın, projemizi sanal ortam içinde çalıştırmamız -yani pipenv ile- gerekiyor.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>pipenv run python app.py
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Eğer herhangi bir hata almadıysak tarayıcımızdan http://127.0.0.1:5000 adresine gidince bizi ilk sayfamız karşılıyor olmalı!</p>
<p><img src="/assets/images/flask_document/hello_world.jpg" alt="" /></p>
<h1 id="site-i̇skeletini-oluşturalım">Site İskeletini Oluşturalım</h1>
<p>Websiteniz arkaplanda şahane teknolojiler kullanıyor olabilir, dünyada bir ilki gerçekleştiriyor bile olabilir. Ama malesef siteye giren ziyaretçi için bu hiçbir şey ifade etmiyor. Çünkü ziyaretçiler ne arkada çalışan sunucuyu görüyor ne de Flask fonksiyonlarınızdan haberi var. Onlar sadece kendilerine ulaşan html ve html’i güzelleştiren css dosyalarını görüyor. O yüzden web sitemizin iskeletini -yani tasarımını- elimizden geldiğince güzel tutmalıyız. Tabiki bu hızlandırılmış bir doküman olduğu için çok basit çalışacağız, gerisi size ve yaratıcılığınıza kalmış.</p>
<p>Öncelikle iskelet dosyalarımızın (html) barınacağı bir klasör oluşturmamız gerekiyor. <em>flask-blog</em> klasörünün içine <em>templates</em> klasörü oluşturuyoruz. Aslında bu klasör isimlerini kendi istediğimiz gibi yapabiliriz ancak kabul görmüş standartları kullanmak her zaman bizim ve projemizi okuyacak kişilerin yararına olacaktır. Daha sonra da <em>templates</em> klasörü içine <em>base.html</em> adında bir html dosyası oluşturuyoruz. Bu aşamada proje dosyamızın içeriği aşağıdaki gibi olacak.</p>
<p>flask-blog<br />
├───templates<br />
│ └───base.html<br />
├───app.py</p>
<p>Şimdi neden base.html isiminde bir dosya oluşturduğumuza deneyelim. Base kelime anlamı olarak ‘baz, temel’ anlamına geliyor. Yani bu temel iskeletimiz olacak. Oluşturacağımız diğer iskeletler base.html iskeletimizi kullanacak böylelikle genel yapıyı her dosya için tekrar yazmaktan kurtulacağız ve aynı zamanda blogumuzun tüm sayfalarının benzer şemada olmasını sağlayıp bütünlük sağlayabileceğiz. HTML’ etiketlerden oluştuğu konusunda bir bilginiz vardır elbet. Açılan her etiket kapatılır. <etiket> </etiket> örneğinde gördüğünüz gibi kapatırken taksim kullanılır. Bu bilgi açıklamaları okumanızı kolaylaştıracaktır. Bu aşamadan itibaren iskelet dosyalarını oluştururken izleyeceğimiz adımlar tamamen tercihe bağlıdır. Örnek vermek gerekirse siz h1 etiketi yerine h2 etiketi kullanarak daha küçük bir yazı elde edebilirsiniz. Kendi zevkinize göre bu kısımı özelleştirmeyi unutmayın! Neticede bu sizin blogunuz 🙂 Eğer bu dokümanı bir workshopta takip ediyorsanız zaten size yardımcı olacak birisi var demektir. Hata yapmaktan korkmayın.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
<span class="nt">&lt;title&gt;</span>Benim Blogum<span class="nt">&lt;/title&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>base.html içeriğimizi bu şekilde oluşturalım. Burası çok da özelleştirebileceğimiz bir alan değil, HTML’nin standart ve olması gereken etiketleri bunlar.
Burada açıklamak istediğim iki etiket var. Öncelikle <em>title</em> etiketi, bu etiketin içerisine yazdığımız yazı tarayıcılar tarafından site adı olarak yorumlanacak ve sekmede görünecek. <em>body</em> etiketine yazdığımız kodlarımız ise tarayıcı tarafından site içeriği olarak yorumlanacak ve kullanıcıya gösterilecek.</p>
<p>Bu aşamada biraz tema motorlarından bahsetmek istiyorum. Tema motorları (template engines) iskelet dosyalarımızı dinamik yapmamıza yardımcı oluyor. Yani örnek vermek gerekirse ana sayfada başka, profil sayfasında başka site başlığı belirleyebiliyoruz. Yapmamız gereken bu alanlara bir blok geleceğini belirtmek ve daha sonra ilgili fonksiyonların içinde blokları veya değişkenleri doldurmak. Bu kısımı tam anlayamamış olabilirsiniz, örnekler üzerinde daha iyi anlayacağınızı düşünüyorum. Şimdi base.html dosyamızı biraz düzenleyip ilgili alanlara işaretlemeler yapalım.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
<span class="nt">&lt;title&gt;</span>Benim Blogum - {% block title %}{% endblock %}<span class="nt">&lt;/title&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
<span class="nt">&lt;header&gt;</span>
<span class="nt">&lt;main&gt;</span>Benim Blogum<span class="nt">&lt;/main&gt;</span>
<span class="nt">&lt;/header&gt;</span>
<span class="nt">&lt;main&gt;</span>
<span class="nt">&lt;nav&gt;</span>giriş yap<span class="nt">&lt;/nav&gt;</span>
<span class="nt">&lt;content&gt;</span>
{% block main %}{% endblock %}
<span class="nt">&lt;/content&gt;</span>
<span class="nt">&lt;/main&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Yaptığımız değişikleri açıklayalım. <em>title</em> etiketinin içine adı yine <em>title</em> olan bir block oluşturduk. Bunu yaparken {% %} şeklinde bir etiket yazdık. Bu etiketler Flask’ın tema motoru olan Jinja2 tarafından okunup yorumlanacak ve sonra ortaya düzenlenmiş bir html dosyası çıkacak. Bu düzenlenmiş yani render edilmiş dosya da siteyi görmek için istek gönderen ziyaretçiye iletilecek. Böylelikle ziyaretçi için özel bir cevap hazırlamış olacağız. <em>title</em> etiketindeki block kısımını ziyaretçinin görmek istediği sayfaya göre sayfa başlığını değiştirebilmek için kullanacağız.</p>
<p><em>main</em> etiketinde de adı yine <em>main</em> olan bir blockumuz daha var. Daha önce base.html dosyamızın başka iskeletler tarafından temel alınıp üzerine inşa edileceğinden bahsetmiştik. Bu kısıma o iskelet dosyalarının içeriği gelecek. Böylelikle temel HTML etiketlerimizi her dosya için yazmayacağız.</p>
<p>Bunlar dışında <em>header</em> ve <em>nav</em> olmak üzere iki yeni etiket ekledik. Bunlar da sitemizin diğer ilgili alanlarını oluşturuyor. <em>header</em> etiketi sitemizin başlığını, üst kısımını, <em>nav</em> etiketi ise menüsünü belirtiyor. <em>content</em> etiketi ise tamamen alanı belirtmek için.</p>
<p>Şimdi <em>templates</em> dosyamızın içine bir de index.html dosyası oluşturup sitemizin ‘/’ adresine gelen isteklere cevap verecek ana sayfa iskeletimizi oluşturalım.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>{% extends 'base.html' %}
{% block title %} Ana Sayfa {% endblock %}
{% block main %}
'Dünya, naber?'
{% endblock %}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>index.html dosyamızın içeriği bu şekilde, şu anlık. Bir html dosyası oluşturduk ama içine hiç html etiketi yazmadık diye düşünüyor olabilirsiniz. Evet, yazmadık, yazacağız ama yazmadan önce burayı biraz açıklamak istiyorum.</p>
<p>İlk satırda <em>extends</em> ifadesi kullandık. Extend kelime anlamı olarak <strong>genişletmek</strong> anlamına geliyor. Yani index.html dosyamızın aslında <em>base.html</em> dosyasının genişletilmiş hali olduğunu belirtiyoruz. Daha önce kullandığımız değişle base.html dosyasını temel almasını söylüyoruz. Daha sonra burada da block etiketlerini kullandığımızı görebilirsiniz. Burada kullandığımız block etiketleri arasına yazdığımız her şey temel aldığımız base.html dosyasındaki ilgili yere gidecek. Hemen bu durumu canlı canlı görelim.</p>
<p><em>app.py</em> dosyamıza dönelim ve hello_world fonksiyonumuzdaki <strong>return ‘Hello, World!’</strong> satırını <strong>return render_template(‘index.html’)</strong> ile değiştirelim daha sonra projemizi başlatalım. Projemizi pipenv ile başlatmamız gerektiğini unutmayalım!<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup></p>
<p>Projeyi çalıştırdıktan sonra tarayıcıdan açtığımız zaman <strong>NameError: name ‘render_template’ is not defined</strong> hatasıyla karşılaşacağız. Bu çok normal. Sizlere Flask’ı tamamen içeri aktarmadığımızı, sadece Flask’ın Flask sınıfını (bilgisayar bilimlerinde en zor konulardan birinin isimlendirme olduğu gerçeğini tekrar anlıyoruz) içeri aktardığımızdan bahsetmiştik. render_template fonksiyonu da Flask’ın içinde olmasına rağmen onu dahil etmediğimiz için kendisini bulamadık şeklinde hata alıyoruz. request_template fonksiyonunu da içeri aktararak sorunu çözebiliriz. Yani app.py’deki import kısımını aşağıdaki şekilde düzenleyip projeyi tekrar çalıştıralım.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Şimdi sitemizi çalıştırdığımız zaman değişikliklerin geçerli olduğunu görebiliriz.</p>
<p><img src="/assets/images/flask_document/template_helloworld.jpg" alt="" /></p>
<p>Devam etmeden önce şimdiye kadar yaptıklarımızı kısa bir özetleyelim. ‘/’ adresine bağlı fonksiyonumuz artık cevap olarak bir iskeleti (template’yi) döndürüyor. Bu döndürdüğü template’de olmamasına rağmen ‘Giriş Yap’ yazısının yinede sitede bulunmasının sebebinin de aslında index.html dosyasının base.html dosyasını kullanarak bir bütün oluşturması ve cevap olarak bu oluşan bütünü göndermesi olduğunu biliyoruz.</p>
<p>Biz bir blog sitesi yapıyoruz ve ana sayfada blog yazılarını göstermek istiyoruz. Oyüzden şimdilik temsilen iki yazıyı elimizle varmış gibi gireceğiz. Daha sonra fonksiyonları yazarken bu kısımları otomatikleştireceğiz.</p>
<p>index.html dosyamızın içeriğini aşağıdaki gibi düzenleyelim.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre>{% extends 'base.html' %}
{% block title %} Ana Sayfa {% endblock %}
{% block main %}
<span class="nt">&lt;div&gt;</span>
<span class="nt">&lt;h2&gt;</span>Hello World!<span class="nt">&lt;/h2&gt;</span>
<span class="nt">&lt;p&gt;</span>Bu birinci yazımızın kısa bir önizlemesi olsun. Devamı daha sonra :)<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">'#'</span><span class="nt">&gt;</span>Devamını Oku<span class="nt">&lt;/a&gt;</span>
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div&gt;</span>
<span class="nt">&lt;h2&gt;</span>Bir Diğer Yazı!<span class="nt">&lt;/h2&gt;</span>
<span class="nt">&lt;p&gt;</span>Bu da ikinci yazımızın kısa bir önizlemesi olsun. Devamı daha sonra :)<span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">'#'</span><span class="nt">&gt;</span>Devamını Oku<span class="nt">&lt;/a&gt;</span>
<span class="nt">&lt;/div&gt;</span>
{% endblock %}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Bu şimdilik böyle kalsın, daha sonra tekrar döneceğiz index.html şablonumuza. Editörlerin giriş yapabilmesi için login.html şablonuna ihtiyacımız var. O yüzden templates klasörüne login.html dosyası oluşturalım. Bu şablonda da extends etiketi ile base.html’i kullanması gerektiğini belirteceğimizi ve title ile main blocklarını kullanacağımızı biliyoruz. O yüzden benim vereceğim örneğe bakmadan önce kendiniz oluşturmaya çalışın şablonu, içine oluşturacağımız formu beraber yaparız yine 🙂.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre>{% extends 'base.html' %}
{% block title %} Giriş Yap {% endblock %}
{% block main %}
<span class="nt">&lt;form</span> <span class="na">method=</span><span class="s">"POST"</span> <span class="na">action=</span><span class="s">"/girisyap"</span><span class="nt">&gt;</span>
<span class="nt">&lt;table&gt;</span>
<span class="nt">&lt;tr&gt;</span>
<span class="nt">&lt;th&gt;&lt;label</span> <span class="na">for=</span><span class="s">"username"</span><span class="nt">&gt;</span>Kullanıcı Adı:<span class="nt">&lt;/label&gt;&lt;/th&gt;</span>
<span class="nt">&lt;th&gt;&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"username"</span> <span class="na">required=</span><span class="s">""</span><span class="nt">&gt;&lt;/br&gt;&lt;/th&gt;</span>
<span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;tr&gt;</span>
<span class="nt">&lt;td&gt;&lt;label</span> <span class="na">for=</span><span class="s">"password"</span><span class="nt">&gt;</span>Parola:<span class="nt">&lt;/label&gt;&lt;/td&gt;</span>
<span class="nt">&lt;td&gt;&lt;input</span> <span class="na">type=</span><span class="s">"password"</span> <span class="na">name=</span><span class="s">"password"</span> <span class="na">required=</span><span class="s">""</span><span class="nt">&gt;&lt;/td&gt;</span>
<span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;tr&gt;</span>
<span class="nt">&lt;td&gt;&lt;button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">&gt;</span>Giriş Yap<span class="nt">&lt;/button&gt;&lt;/td&gt;</span>
<span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;/table&gt;</span>
<span class="nt">&lt;/form&gt;</span>
{% endblock %}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>login.html şablonumuzun içeriğini bu şekilde hazırlayabiliriz. Editörler bu sayfadan giriş yapacağı için bir giriş formu oluşturmamız gerekiyor. <em>form</em> etiketinin <em>action</em> özelliğine girdiğimiz adresi görüyorsunuz. Bu adresi daha sonra oluşturacağız, şablonumuzda şimdiden bir yönlendirme eklememizde sakınca yok. Aynı şekilde base.html iskeletimizdeki <em>‘giriş yap’</em> yazısına da html’nin <strong>a</strong> etiketini kullanarak aynı adrese link verelim.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/girisyap"</span><span class="nt">&gt;</span>giriş yap<span class="nt">&lt;/a&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Son olarak bu şekilde görünüyor olacak.</p>
<p>Şimdi yine blog sitelerinin gereksinimlerinden biri olan yeni yazı ekleme sayfamızı hazırlayalım. Buraya kadar Jinja2 (hatırlayalım: Jinja2, Flask’ın tema motorudur) etiketlerini ve form yapısını öğrenmiş olmalıyız. Siz yine hazır olan örneğe bakmadan önce <strong>newpost.html</strong> adında içinde bir form barındıran şablon oluşturmaya çalışın. Yapabildiğiniz kadar yapın, daha sonra buradaki örnek ile kıyaslayıp eksiklerini/hatalarını düzeltin. Kopyala yapıştır ile öğrenmek malesef mümkün değil.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre>{% extends 'base.html' %}
{% block title %} Yeni Yazı {% endblock %}
{% block main %}
<span class="nt">&lt;form</span> <span class="na">method=</span><span class="s">"POST"</span> <span class="na">action=</span><span class="s">"/yaziekle"</span><span class="nt">&gt;</span>
<span class="nt">&lt;table&gt;</span>
<span class="nt">&lt;tr&gt;</span>
<span class="nt">&lt;th&gt;&lt;label</span> <span class="na">for=</span><span class="s">"title"</span><span class="nt">&gt;</span>Yazı Başlığı:<span class="nt">&lt;/label&gt;&lt;/th&gt;</span>
<span class="nt">&lt;th&gt;&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">name=</span><span class="s">"title"</span> <span class="na">require</span><span class="nt">&gt;&lt;/br&gt;&lt;/th&gt;</span>
<span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;tr&gt;</span>
<span class="nt">&lt;td&gt;&lt;label</span> <span class="na">for=</span><span class="s">"content"</span><span class="nt">&gt;</span>Yazı İçeriği:<span class="nt">&lt;/label&gt;&lt;/td&gt;</span>
<span class="nt">&lt;td&gt;&lt;textarea</span> <span class="na">name=</span><span class="s">"content"</span> <span class="na">required</span><span class="nt">&gt;&lt;/textarea&gt;&lt;/td&gt;</span>
<span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;tr&gt;</span>
<span class="nt">&lt;td&gt;&lt;button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">&gt;</span>Yazı Ekle<span class="nt">&lt;/button&gt;&lt;/td&gt;</span>
<span class="nt">&lt;/tr&gt;</span>
<span class="nt">&lt;/table&gt;</span>
<span class="nt">&lt;/form&gt;</span>
{% endblock %}
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Sonuç olarak newpost.html dosyamız da buna benziyor olmalı. Şimdi devam edebiliriz.</p>
<h2 id="şablonlarımızı-güzelleştirelim">Şablonlarımızı Güzelleştirelim</h2>
<p>Şablonları oluştururken tamamen benimle aynı şeyleri yapamamanızı, biraz yaratıcılık katmanızı rica etmiştim. Şimdi o kısıma geliyoruz. Şablonlarımızı güzelleştirirken kendi yazı büyüklüklerinizi, arkaplan ve yazı renklerinizi kullanmaktan çekinmeyin. Malesef css kodlarını çok fazla açıklayamayacağım. Ancak css’yi basit seviyede öğrenmek için birkaç doküman okumanız yeterli.</p>
<p>İlk paragraftan da anlaşıldığı üzere şablonlarımızı güzelleştirirken CSS’den yararlanacağız. CSS için özetle, HTML etiketlerinin şekilleri üzerinde oynamalar yapmamızı sağlayan bir dil diyebiliriz. CSS dosyaları eğer HTML dosyalarının içinde belirtilmemişse malesef çalışamıyor. Bu aşamada işin içine static dosyalar dediğimiz durum giriyor. Statik bir websitesi geliştirirken (yani html dosyasından oluşan, herhangi bir özelliği olmayan, düz site) html dosyasının içine direk olarak css dosyasının yolunu belirtebilirdik. Ancak biz Flask kullanarak dinamik bir site geliştirdiğimiz için malesef bu çok mantıklı bir yöntem olmayacaktır. Hem şablonlar ile diğer dosyaların farklı lokasyonlarda tutuluyor olması hem de proje büyüdükçe oluşacak dosya kalabalığında her dosya için elimizde adres vermek mantıklı bir çözüm olmuyor. Burada yardımımıza static dosyalar yetişiyor. Bunun için <em>flask-blog</em> klasörüne yani projenin ana klasörüne bir de static klasörü oluşturalım.</p>
<p>Sonuç olarak proje klasörümüzün içeriği aşağıdaki gibi olmalı.</p>
<p>flask-blog<br />
├───static<br />
├───templates<br />
│ └───base.html<br />
│ └───index.html<br />
│ └───login.html<br />
├───app.py</p>
<p>Şimdi stil dosyamızı oluşturacağız. Biz bir dosya oluşturacağız, ama yine de standartlara uymak için static klasörümüzün içine <em>css</em> adında yeni bir klasör açıyoruz. Stil dosyalarımızı bu klasöre kaydedeceğiz. Aynı şekilde resim dosyalarını da static klasörünün içindeki <em>images</em>, JavaScript dosyalarımızı da static klasörü içindeki <em>js</em> klasörüne kayıt etmeliyiz. Daha önce de söylediğim gibi, dosya isimlerini siz de belirleyebilirsiniz. Hatta dosyaları işlevlerine göre sınıflandırmak yerine hepsini direkt <em>static</em> klasörü içine de oluşturabilirsiniz. Ancak genel kabul görmüş standartları kullanmak ve bunlara alışmak sizin için daha iyi olacaktır.</p>
<p>Oluşturduğumuz <em>css</em> klasörümüzün içine <em>style.css</em> adında bir dosya oluşturalım ve bu dosyamızı düzenlemek üzere açalım. Bu aşamada klasör yapımız şu şekilde olacak.</p>
<p>flask-blog<br />
├───static<br />
│ └───css<br />
│ └───style.css<br />
├───templates<br />
│ └───base.html<br />
│ └───index.html<br />
│ └───login.html<br />
├───app.py</p>
<p>Bu aşamada style.css dosyasını tamamen kendi zevkinize göre oluşturmalısınız. Ancak bu dokümanda -Orhun Yazıtları’na benzememesi için- <em>CSS</em> kullanımına değinmeyeceğiz. Eğer bir workshopta bu dokümanı takip ediyorsanız hazır örnek üzerinde değişiklikler yapabilirsiniz. <em>style.css</em> dosyamızı oluşturmaya başlamadan önce bu dosyamızı <em>base.html</em> dosyamızda içeri aktaralım. Bunun için <em>head</em> HTML etiketinin içine, <em>title</em> etiketinden sonra aşağıdaki gibi bir satır ekleyeceğiz.</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">type=</span><span class="s">"text/css"</span> <span class="na">href=</span><span class="s">"{{ url_for('static', filename='css/style.css') }}"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Eklediğimiz etiketin <em>href</em> özelliğine de bir tema motoru etiketi yazdığımızı görüyorsunuz. Bu etiket ziyaretçiye gönderilecek HTML dosyası oluşturulmadan önce istediğimiz dosyanın -ki bu örnekte css içindeki style.css dosyası- tam adresini oluşturup o kısıma ekleyecek. İsterseniz blogunuzu pipenv ile başlatıp sayfa kaynağını görüntüleyerek kontrol edebilirsiniz.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="rouge-code"><pre><span class="nt">body</span><span class="o">,</span><span class="nt">html</span> <span class="p">{</span>
<span class="nl">margin</span><span class="p">:</span><span class="m">0</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span><span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">main</span><span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">header</span><span class="p">{</span>
<span class="nl">background-color</span><span class="p">:</span> <span class="no">orange</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">32px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">main</span> <span class="nt">div</span><span class="nd">:not</span><span class="o">(</span><span class="nd">:last-child</span><span class="o">)</span><span class="p">{</span>
<span class="nl">border-bottom</span><span class="p">:</span> <span class="m">2px</span> <span class="nb">dotted</span> <span class="no">black</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Ben <em>style.css</em> dosyamı bu şekilde hazırladım. Evet tasarım anlayışımın çok kısıtlı olduğunun farkındayım, sizlerin çok daha güzellerini yapacağınıza eminim. Hazırladığınız style.css dosyalarını bana mail olarak gönderirseniz buradaki basit örnekle değiştirmeyi çok isterim 🙂. Şuanda ana sayfamın son hali aşağıdaki gibi.</p>
<p><img src="/assets/images/flask_document/styled.jpg" alt="" /></p>
<p>Tasarım için bukadar vakit ayırdığımız yeter. Şimdi işin eğlenceli kısımlarına dönelim.</p>
<h1 id="veritabanı-modelleri">Veritabanı Modelleri</h1>
<p>Bir blog sitesi hazırladığımızı tekrar düşünürsek bu blogun verilerini -editör bilgileri, yazılar- tutması için bir depoya ihtiyacı var. İşte bu yüzden bir veritabanı kullanmamız gerekiyor. Veritabanı olarak sqlite kullanacağız. Şuanda basit bir şey yaptığımız ve çok fazla veritabanı sorgusu oluşturmayacağımız için sqlite bize yetecektir. Veritabanı ile Flask fonksiyonlarımızı <em>sql</em> adındaki bir dil ile haberleştirebiliriz. Ancak biz bu yöntemi kullanmak yerine <em>ORM</em> adı verilen ve veritabanı sorgu işlerimizi kolaylaştıran yardımcı ara katmanlardan birini kullanacağız. ORM kütüphanesi olarak bu dokümanda PeeWee kullanmaya karar verdim, o yüzden pipenv ile oluşturduğumuz sanal ortama peewee kütüphanesini de kurmamız gerekiyor. Pipenv ile bu işi kolayca yaptığımızı hatırlayabiliriz. Bir terminal ile proje klasörümüze girip (flask-blog) <strong>pipenv install peewee</strong> komutunu vermemiz yeterli.</p>
<p>Peewee’yi kurduğumuza göre kullanmanın vaktidir! <em>app.py</em> dosyamıza girelim ve en başa <strong>from peewee import *</strong> komutunu ekleyerek peewee’yi içe aktaralım. Şimdi veritabanı işlemlerimizi yapabilmemiz için tanımlanmış bir veritabanına ihtiyacımız var. Bu bağlantıyı da oluşturduğumuzda app.py dosyamızın ilk satırları aşağıdaki gibi görünecek.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span>
<span class="kn">from</span> <span class="nn">peewee</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
<span class="n">database</span> <span class="o">=</span> <span class="n">SqliteDatabase</span><span class="p">(</span><span class="s">'database.sql'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Sıradaki yapmamız gereken iş ise modellerimizi oluşturmak. Modeller, veritabanındaki tabloların (verilerin tutulduğu yerlerin) oluşturulması, yönetilmesini sağlayacağımız araçlar olacak. Bizim ihtiyacımız olan iki model var, birincisi editörün kullanıcı adı ve parola verisinin tutulacağı <strong>Editor</strong> modeli bir diğeri ise blog yazılarının tutulacağı <strong>Post</strong> modeli. Modellerimizi <em>database</em> tanımlamamızdan hemen sonra tanımlayacağız. Onları da oluşturup üzerine konuşalım.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="k">class</span> <span class="nc">Editor</span><span class="p">(</span><span class="n">Model</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
<span class="n">database</span> <span class="o">=</span> <span class="n">database</span>
<span class="n">username</span> <span class="o">=</span> <span class="n">TextField</span><span class="p">()</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">TextField</span><span class="p">()</span>
<span class="k">class</span> <span class="nc">Post</span><span class="p">(</span><span class="n">Model</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">Meta</span><span class="p">:</span>
<span class="n">database</span> <span class="o">=</span> <span class="n">database</span>
<span class="n">title</span> <span class="o">=</span> <span class="n">TextField</span><span class="p">()</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">TextField</span><span class="p">()</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Bu ikişer satırlık iki tablo (her model databasedeki bir tabloyu temsil ediyorve her ikisi de iki farklı satıra/değere sahip) projemizin şu aşamasında bize yeterli. Projemiz genişledikçe tablolarımızı da genişletebilir, yeni tablolar ekleyebiliriz. <strong>app.py</strong> dosyamızı kaydedelim ve hazırladığımız modellerin veritabanımızda da oluşmasını sağlayalım. Bunun için yine bir terminal açıp proje dizinimize girelim ve <strong>pipenv run python</strong> komutu ile sanal ortamımız içinde bir python çalıştıralım. Daha sonra yapacaklarımız ise app.py dosyasını içeri aktarmak ve daha sonra oradaki mevcut database değişkenimiz üzerinde hazırladığımız modelleri oluşturmak olacak.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>C:<span class="se">\U</span>sers<span class="se">\b</span>isguzar<span class="se">\D</span>esktop<span class="se">\f</span>lask-blog
λ pipenv run python
Python 3.7.0 <span class="o">(</span>v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47<span class="o">)</span> <span class="o">[</span>MSC v.1914 32 bit <span class="o">(</span>Intel<span class="o">)]</span> on win32
Type <span class="s2">"help"</span>, <span class="s2">"copyright"</span>, <span class="s2">"credits"</span> or <span class="s2">"license"</span> <span class="k">for </span>more information.
<span class="o">&gt;&gt;&gt;</span> from app import <span class="k">*</span>
<span class="o">&gt;&gt;&gt;</span> database.create_tables<span class="o">([</span>Editor, Post]<span class="o">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Yazdığımız iki satıra bakacak olursanız yukarıda belirttiğim işlemleri yaptığımızı görebilirsiniz. Herhangi bir hata oluşmadı ise hiçbir çıktı almamış olmalısınız. Daha sonra proje dosyanızı kontrol ettiğinizde orada <em>database.sql</em> adında bir dosya oluştuğunu görebilirsiniz. Bu dosya peewee’nin üzerinde çalışacağı ve verilerimizin tutulacağı veritabanımız.</p>
<p>Şimdi hazır veritabanı tablolarımızı oluşturmuşken terminalimizi kapatmayalım ve ilk editörümüzü de oluşturalım. Ama burada dikkat etmemiz gereken bir nokta var. Editörümüz için gireceğimiz bilgiler kullanıcı adı ve parola. Peki parolayı düz metin olarak veritabanında saklamak mantıklı mı? Şuan biz lokalde çalıştığımız için aslında evet diyebilirsiniz, yanlışlayamam da. Ama daha geniş bir projede birçok kullanıcının parolasının bulunduğunu varsayarsak ve kötü amaçlı birilerinin veritabanınıza erişim sağladığını düşünürsek bu felaket olur. O yüzden bazı şifreleme yöntemleri ile parolayı şifreleyip saklayacağız.</p>
<p>Parolayı şifrelemek için Flask ile birlikte zaten kurulmuş olan <strong>werkzeug</strong> kütüphanesinin <strong>security</strong> sınıfından <strong>generate_password_hash</strong> fonksiyonunu kullanacağız. Biraz uzun bir cümle olduğunun farkındayım ama yaparken çok kolay olduğunu göreceksiniz 🙂. Bu fonksiyona bir metin veriyoruz -ki bu bizim kullanmak istediğimiz parola oluyor- ve fonksiyon bize bu parolanın şifrelenmiş halini veriyor. Hadi bunu zaten açık olan terminalimizde deneyelim.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="o">&gt;&gt;&gt;</span> from werkzeug.security import generate_password_hash
<span class="o">&gt;&gt;&gt;</span> generate_password_hash<span class="o">(</span><span class="s1">'123'</span><span class="o">)</span>
<span class="s1">'pbkdf2:sha256:50000$QiivRF34$ae92386b5de080073206455bc7384f42c83e70d313cac2f76b2b9d991671114b'</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Önce gerekli fonksiyonumuzu içe aktardık, daha sonra fonksiyonumuza şifrelemesi için ‘123’ verisini verdik ve o da bize şifrelenmiş halini döndürdü. Şifrelenmiş hali gördüğünüz gibi çok karmaşık bir yapı. Bu yapının geri döndürülmesi mümkün değil. Peki biz giriş yaparken nasıl şifreleri karşılaştıracağız? Kullanıcının giriş yaparken belirttiği parolayı da şifreleyip veritabanındaki ile karşılaştıracağız. Yani biz kullanıcının parolası ile ilgilenmeyeceğiz. Şimdi buradan şifrelenmiş parolamızı kopyalayalım (tırnak işaretleri hariç) ve bir kenara not edelim, hemen birazdan lazım olacak.</p>
<p>Yine açık olan terminalimizde bir editör kaydı oluşturalım.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="o">&gt;&gt;&gt;</span> <span class="n">Editor</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">username</span><span class="o">=</span><span class="s">'bisguzar'</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s">'pbkdf2:sha256:50000$QiivRF34$ae92386b5de080073206455bc7384f42c83e70d313cac2f76b2b9d991671114b'</span><span class="p">)</span>
<span class="o">&lt;</span><span class="n">Editor</span><span class="p">:</span> <span class="mi">1</span><span class="o">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>