-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathflask-ile-blog.html
1759 lines (1345 loc) · 132 KB
/
flask-ile-blog.html
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
<!DOCTYPE html>
<html>
<head>
<!-- Document Settings -->
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Base Meta -->
<!-- dynamically fixing the title for tag/author pages -->
<title>Flask ile Blog Yapalım</title>
<meta name="HandheldFriendly" content="True" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Styles'n'Scripts -->
<link rel="stylesheet" type="text/css" href="/assets/built/screen.css" />
<link rel="stylesheet" type="text/css" href="/assets/built/screen.edited.css" />
<link rel="stylesheet" type="text/css" href="/assets/built/syntax.css" />
<link rel="stylesheet" type="text/css" href="/assets/custom.css" />
<!-- highlight.js -->
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<style>
.highlighter-rouge{
width: 100%;
}
.hljs{
background: none;
}
</style>
<!-- This tag outputs SEO meta+structured data and other important settings -->
<meta name="description" content="" />
<link rel="shortcut icon" href="/assets/images/favicon.png" type="image/png" />
<link rel="canonical" href="/flask-ile-blog" />
<meta name="referrer" content="no-referrer-when-downgrade" />
<!--title below is coming from _includes/dynamic_title-->
<meta property="og:site_name" content="Bisguzar - Hobisel Pythoncu" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Flask ile Blog Yapalım" />
<meta property="og:description" content="İçerik Listesi Giriş İnternet Siteleri Nasıl Çalışır Pipenv Kurulumu ve Kullanımı Flask Kullanmaya Başlayalım Site İskeletini Oluşturalım Şablonlarımızı Güzelleştirelim Veritabanı Modelleri Back-end’i Hazırlayalım Fonksiyonlarımızı Oluşturalım Giriş/Çıkış Yapısını Hazırlayalım Yazı Ekleme Sayfasını Ayarlayalım Ana Sayfayı Düzenleyelim Devam Etmek İsteyenler İçin Giriş Bu yazıda web temelli uygulamalar geliştirmek için hazırlanmış olan python" />
<meta property="og:url" content="/flask-ile-blog" />
<meta property="og:image" content="/assets/images/flask_python.jpg" />
<meta property="article:publisher" content="https://www.facebook.com/" />
<meta property="article:author" content="https://www.facebook.com/" />
<meta property="article:published_time" content="2018-08-02T17:09:00+00:00" />
<meta property="article:modified_time" content="2018-08-02T17:09:00+00:00" />
<meta property="article:tag" content="Flask" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Flask ile Blog Yapalım" />
<meta name="twitter:description" content="İçerik Listesi Giriş İnternet Siteleri Nasıl Çalışır Pipenv Kurulumu ve Kullanımı Flask Kullanmaya Başlayalım Site İskeletini Oluşturalım Şablonlarımızı Güzelleştirelim Veritabanı Modelleri Back-end’i Hazırlayalım Fonksiyonlarımızı Oluşturalım Giriş/Çıkış Yapısını Hazırlayalım Yazı Ekleme Sayfasını Ayarlayalım Ana Sayfayı Düzenleyelim Devam Etmek İsteyenler İçin Giriş Bu yazıda web temelli uygulamalar geliştirmek için hazırlanmış olan python" />
<meta name="twitter:url" content="/" />
<meta name="twitter:image" content="/assets/images/flask_python.jpg" />
<meta name="twitter:label1" content="Written by" />
<meta name="twitter:data1" content="Bisguzar - Hobisel Pythoncu" />
<meta name="twitter:label2" content="Filed under" />
<meta name="twitter:data2" content="Flask" />
<meta name="twitter:site" content="@bugraisguzar" />
<meta name="twitter:creator" content="@bugraisguzar" />
<meta property="og:image:width" content="1400" />
<meta property="og:image:height" content="933" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Website",
"publisher": {
"@type": "Organization",
"name": "Bisguzar - Hobisel Pythoncu",
"logo": "/assets/images/bisguzar_w.png"
},
"url": "/flask-ile-blog",
"image": {
"@type": "ImageObject",
"url": "/assets/images/flask_python.jpg",
"width": 2000,
"height": 666
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "/flask-ile-blog"
},
"description": "İçerik Listesi Giriş İnternet Siteleri Nasıl Çalışır Pipenv Kurulumu ve Kullanımı Flask Kullanmaya Başlayalım Site İskeletini Oluşturalım Şablonlarımızı Güzelleştirelim Veritabanı Modelleri Back-end’i Hazırlayalım Fonksiyonlarımızı Oluşturalım Giriş/Çıkış Yapısını Hazırlayalım Yazı Ekleme Sayfasını Ayarlayalım Ana Sayfayı Düzenleyelim Devam Etmek İsteyenler İçin Giriş Bu yazıda web temelli uygulamalar geliştirmek için hazırlanmış olan python"
}
</script>
<!-- <script type="text/javascript" src="https://demo.ghost.io/public/ghost-sdk.min.js?v=724281a32e"></script>
<script type="text/javascript">
ghost.init({
clientId: "ghost-frontend",
clientSecret: "f84a07a72b17"
});
</script> -->
<meta name="generator" content="Jekyll 3.6.2" />
<link rel="alternate" type="application/rss+xml" title="Flask ile Blog Yapalım" href="/feed.xml" />
</head>
<body class="post-template">
<div class="site-wrapper">
<!-- All the main content gets inserted here, index.hbs, post.hbs, etc -->
<!-- default -->
<!-- The tag above means: insert everything in this file
into the {body} of the default.hbs template -->
<header class="site-header outer">
<div class="inner">
<nav class="site-nav">
<div class="site-nav-left">
<a class="site-nav-logo" href="/"><img src="/assets/images/bisguzar_w.png" alt="Bisguzar - Hobisel Pythoncu" /></a>
<ul class="nav" role="menu">
<li class="nav-home" role="menuitem"><a href="/">Ana Sayfa</a></li>
<li class="nav-getting-started" role="menuitem"><a href="/tag/python/">Python</a></li>
<li class="nav-getting-started" role="menuitem"><a href="/tag/micropython/">MicroPython</a></li>
<li class="nav-getting-started" role="menuitem"><a href="/tag/flask/">Flask</a></li>
</ul>
</div>
<div class="site-nav-right">
<div class="social-links">
<a class="social-link social-link-tw" href="https://twitter.com/bugraisguzar" target="_blank" rel="noopener"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>
</a>
<a class="social-link social-link-gh" href="https://github.com/bisguzar" target="_blank" rel="noopener"><i class="fab fa-github" style="font-size: 18px"></i></a>
</div>
</div>
</nav>
<script defer src="https://use.fontawesome.com/releases/v5.2.0/js/all.js" integrity="sha384-4oV5EgaV02iISL2ban6c/RmotsABqE4yZxZLcYMAdG7FAPsyHYAPpywE9PJo+Khy" crossorigin="anonymous"></script>
</div>
</header>
<!-- Everything inside the #post tags pulls data from the post -->
<!-- #post -->
<main id="site-main" class="site-main outer" role="main">
<div class="inner">
<article class="post-full post ">
<header class="post-full-header">
<section class="post-full-meta">
<time class="post-full-meta-date" datetime=" 2 August 2018"> 2 August 2018</time>
<span class="date-divider">/</span>
<a href='/tag/flask/'>FLASK</a>
</section>
<h1 class="post-full-title">Flask ile Blog Yapalım</h1>
<br>
<span> okuma süresi yaklaşık
35 dakika
</span>
</header>
<figure class="post-full-image" style="background-image: url(/assets/images/flask_python.jpg)">
</figure>
<section class="post-full-content">
<div class="kg-card-markdown" style="text-align: justify;">
<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">>>></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">"<stdin>"</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"><</span><span class="n">module</span><span class="o">></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">>>></span> <span class="kn">import</span> <span class="nn">flask</span>
<span class="o">>>></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"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></span>Benim Blogum<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"></body></span>
<span class="nt"></html></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"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></span>Benim Blogum - {% block title %}{% endblock %}<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><header></span>
<span class="nt"><main></span>Benim Blogum<span class="nt"></main></span>
<span class="nt"></header></span>
<span class="nt"><main></span>
<span class="nt"><nav></span>giriş yap<span class="nt"></nav></span>
<span class="nt"><content></span>
{% block main %}{% endblock %}
<span class="nt"></content></span>
<span class="nt"></main></span>
<span class="nt"></body></span>
<span class="nt"></html></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"><div></span>
<span class="nt"><h2></span>Hello World!<span class="nt"></h2></span>
<span class="nt"><p></span>Bu birinci yazımızın kısa bir önizlemesi olsun. Devamı daha sonra :)<span class="nt"></p></span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">'#'</span><span class="nt">></span>Devamını Oku<span class="nt"></a></span>
<span class="nt"></div></span>
<span class="nt"><div></span>
<span class="nt"><h2></span>Bir Diğer Yazı!<span class="nt"></h2></span>
<span class="nt"><p></span>Bu da ikinci yazımızın kısa bir önizlemesi olsun. Devamı daha sonra :)<span class="nt"></p></span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">'#'</span><span class="nt">></span>Devamını Oku<span class="nt"></a></span>
<span class="nt"></div></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"><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">></span>
<span class="nt"><table></span>
<span class="nt"><tr></span>
<span class="nt"><th><label</span> <span class="na">for=</span><span class="s">"username"</span><span class="nt">></span>Kullanıcı Adı:<span class="nt"></label></th></span>
<span class="nt"><th><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">></br></th></span>
<span class="nt"></tr></span>
<span class="nt"><tr></span>
<span class="nt"><td><label</span> <span class="na">for=</span><span class="s">"password"</span><span class="nt">></span>Parola:<span class="nt"></label></td></span>
<span class="nt"><td><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">></td></span>
<span class="nt"></tr></span>
<span class="nt"><tr></span>
<span class="nt"><td><button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">></span>Giriş Yap<span class="nt"></button></td></span>
<span class="nt"></tr></span>
<span class="nt"></table></span>
<span class="nt"></form></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"><a</span> <span class="na">href=</span><span class="s">"/girisyap"</span><span class="nt">></span>giriş yap<span class="nt"></a></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"><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">></span>
<span class="nt"><table></span>
<span class="nt"><tr></span>
<span class="nt"><th><label</span> <span class="na">for=</span><span class="s">"title"</span><span class="nt">></span>Yazı Başlığı:<span class="nt"></label></th></span>
<span class="nt"><th><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">></br></th></span>
<span class="nt"></tr></span>
<span class="nt"><tr></span>
<span class="nt"><td><label</span> <span class="na">for=</span><span class="s">"content"</span><span class="nt">></span>Yazı İçeriği:<span class="nt"></label></td></span>
<span class="nt"><td><textarea</span> <span class="na">name=</span><span class="s">"content"</span> <span class="na">required</span><span class="nt">></textarea></td></span>
<span class="nt"></tr></span>
<span class="nt"><tr></span>
<span class="nt"><td><button</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">></span>Yazı Ekle<span class="nt"></button></td></span>
<span class="nt"></tr></span>
<span class="nt"></table></span>
<span class="nt"></form></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"><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">></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">>>></span> from app import <span class="k">*</span>
<span class="o">>>></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">>>></span> from werkzeug.security import generate_password_hash
<span class="o">>>></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">>>></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"><</span><span class="n">Editor</span><span class="p">:</span> <span class="mi">1</span><span class="o">></span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Zaten app.py dosyamızdaki her şeyi içeri aktarmıştık, yani Editor modelimiz de çalıştırdığımız etkileşimli kabukta mevcuttu. Biz de onu kullanarak yeni bir kayıt oluşturduk. Username ve password değerlerini kendinizinkilerle değiştirmeyi unutmayın!</p>
<p>Modellerle işimiz şimdilik bukadar. Şimdi blogumuzun tüm hamallığını yapacak olan arka tarafı yazmaya başlayabiliriz.</p>
<h1 id="back-endi-hazırlayalım">Back-end’i Hazırlayalım</h1>
<p>Aslında şimdiye kadar birçok şeyi hallettik, bundan sonra yapacağımız işlemler sitemizi hazırlamak için son adımlar. Öncelikle her sayfayı farklı bir fonksiyonun ve bağlantı noktasının hazırladığından bahsetmiştik. O yüzden sayfalarımız için yeni fonksiyonlar oluşturmalıyız. Şu anda zaten ‘/’ adresine bağlı <em>hello_world</em> adında bir fonksiyonumuz mevcut. Hadi fonksiyonumuzun adını <em>hello_world</em> değil de <em>index</em> olarak değiştirelim. Websitelerinde ana bağlantı noktasını karşılayan her şeye <strong>index</strong> diyoruz. Hatırlarsanız şablonunun adını da <em>index.html</em> koymuştuk. Fonksiyonumuz şu aşamada son olarak 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
</pre></td><td class="rouge-code"><pre><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">index</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'index.html'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<h2 id="fonksiyonlarımızı-oluşturalım">Fonksiyonlarımızı Oluşturalım</h2>
<p>Ancak bizim blogumuz sadece index’den -yani ana sayfadan- ibaret değil. Editörlerin giriş yapabileceği ve yazı ekleyebileceği iki ayrı sayfamız daha var. Onlar için de birer fonksiyon oluşturup decaratör ile bağlantı adreslerini ayarlamamız gerekiyor. Hadi şimdi adı <strong>login</strong> olan ve <strong>/girisyap</strong> bağlantı noktasına bağlanmış bir fonksiyon oluşturalım. Ve aynı zamanda bu fonksiyonumuz da değer olarak bir temayı döndürecek, <em>login.html</em> temasını.</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
</pre></td><td class="rouge-code"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">'/girisyap'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'login.html'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Aynen bu şekilde görünüyor olmalı. Sanırım fonksiyon adı, bağlantı noktası ve şablon döndürme konularını anladık. O zaman sıra sizde. Şimdi de adı <strong>newpost</strong> olan, <strong>/yaziekle</strong> adresine bağlı ve <strong>newpost.html</strong> şablonunu döndüren bir fonksiyon oluşturun. Daha sonra buradaki örnek ile kıyaslayın. Hata yapmaktan korkmayın.</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
</pre></td><td class="rouge-code"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">'/yaziekle'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">newpost</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'newpost.html'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Son eklediğimiz fonksiyon da böyle görünüyor olmalı. Şimdi yaptığımız tüm değişikliklerden sonra <em>app.py</em> dosyamız nasıl görünüyor ona bir bakalım, eğer aralarda bir şeyleri kaçırmışsanız düzenleme şansınız olsun.</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
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
</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>
<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>
<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">index</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'index.html'</span><span class="p">)</span>
<span class="nd">@app.route</span><span class="p">(</span><span class="s">'/girisyap'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'login.html'</span><span class="p">)</span>
<span class="nd">@app.route</span><span class="p">(</span><span class="s">'/yaziekle'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">newpost</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'newpost.html'</span><span class="p">)</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>Sırasıyla import (içe aktarma) işlemlerimizi yaptık, tanımlamalarımızı yaptık, modellerimizi hazırladık, fonksiyonlarımızı hazırladık ve son olarak projemizi çalıştırma komutumuzu yazdık.</p>
<h2 id="girişçıkış-yapısını-hazırlayalım">Giriş/Çıkış Yapısını Hazırlayalım</h2>
<p>Her şey çok güzel görünüyor, bir /yaziekle sayfamız var. Ama bu sayfaya sadece siteye giriş yapmış editörlerin erişebilmesi gerekiyor. Yoksa siteye giriş yapan tüm ziyaretçiler yeni yazı ekleyebilir ve bunu pek istemeyiz. Aslında belki isteyebiliriz, ziyaretçi defteri yapmış oluruz. Ama bu seferlik istemediğimizi varsayarak ilerleyelim. Bunun için öncelikle üye girişi sayfamızı hazırlamamız gerekiyor. Zaten <em>login</em> fonksiyonumuz mevcut onun üzerinde çalışabiliriz. Ama bundan önce http metodları hakkında biraz bilgi sahibi olmamız gerekiyor. Açıklayacağım iki farklı metod var ve bu metodları kabaca inceleyeceğiz.</p>
<p><strong>GET</strong><br />
GET metodumuz aslında veri isterken kullanılır. Burada referans noktamız ziyaretçi. Yani ziyaretçi bizden veri isterken GET metodunu kullanacaktır. Veri istemekten kastımız Ahmet’in bakkaldan gofret istemesi gibi ziyaretçinin html verisini istemesidir. GET kelime anlamı olarak da almak, edinmek anlamlarına gelir.</p>
<p><strong>POST</strong><br />
POST metodumuz ise yine ziyaretçimizin bizden veri istemek yerine bize veri göndermesi için kullanılır. Bu sefer veriyi yakalayan biz, gönderen ziyaretçi olacaktır. POST da kelime anlamı olarak posta, mektup anlamlarına gelir.</p>
<p>Biz aslında fonksiyonlarımızı oluştururken fonksiyonların kabul edeceği HTTP metodlarını da belirtiyoruz. decaratöre nasıl bağlantı noktasını belirtiyorsak <strong>methods=[‘METOD1’, ‘METOD2’]</strong> diyerek fonksiyonun kabul edeceği metodları da belirtebiliyoruz. Eğer belirtmezsek Flask bizim için sadece <em>GET</em> metodunu kabul edecek şekilde düzenliyor fonksiyonlarımızı. Ancak biz <em>login</em> metodumuzda bir form aracılığıyla veri almak istiyoruz ziyaretçiden. O yüzden POST metodunu da kabul etmemiz gerekiyor. O zaman hadi decaratörümüzü düzenleyelim ve son haline bir bakalı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
</pre></td><td class="rouge-code"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">'/girisyap'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">,</span> <span class="s">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'login.html'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Bu görünümü aldı fonksiyonumuz ve onun decaratörü. POST metodunu formdan gelen verileri kabul edebilmek için ekledik ama aynı zamanda kullanıcı veri gönderebilmek için formu görebilmeli. Yani bize veriyi <em>POST</em> etmeden önce form yapısını <em>GET</em> edebilmeli. Biliyorum plaza ağzı gibi oldu, ama yapacak bir şey yok :(. Mantık şöyle işliyor; ziyaretçi /girisyap sayfasına girer ve önüne bir form çıkar (burada GET metodu ile bizden veri aldı), formu doldurur ve gönder butonuna basar (burada da POST metodu ile veriyi bize gönderdi).</p>
<p>Eğer böyle bir durum varsa biz bir fonksiyonda birden fazla iş yapacağız demektir. Eğer form verisi geldiyse (yani method POST ise) giriş işlemlerini yapacağız ve parolayı kontrol edeceğiz, eğer böyle bir durum yoksa ziyaretçi formu görmek istemiş demektir o zaman da form göstereceğiz. Bunları yaparken Flask’ın request sınıfını kullanacağız. O yüzden onu da importlarımız arasına ekleyelim. Ancak onunla beraber farklı yapılar da kullanacağız. O yüzden farklı sınıfları/fonksiyonları da ekliyoruz, ilerleyen zamanlarda onları da açıklayacağı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="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="p">(</span><span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span>
<span class="n">redirect</span><span class="p">,</span> <span class="n">url_for</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Son olarak durum böyle. Şimdi <em>login</em> fonksiyonumuzu yazalım. Ben hazırlayıp geliyorum, üzerinde 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
</pre></td><td class="rouge-code"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">'/girisyap'</span><span class="p">,</span> <span class="n">methods</span><span class="o">=</span><span class="p">[</span><span class="s">'GET'</span><span class="p">,</span> <span class="s">'POST'</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">login</span><span class="p">():</span>
<span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">method</span> <span class="o">==</span> <span class="s">'POST'</span><span class="p">:</span>
<span class="n">username</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">'username'</span><span class="p">]</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">request</span><span class="o">.</span><span class="n">form</span><span class="p">[</span><span class="s">'password'</span><span class="p">]</span>
<span class="k">return</span> <span class="n">redirect</span><span class="p">(</span><span class="n">url_for</span><span class="p">(</span><span class="s">'index'</span><span class="p">))</span>
<span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s">'login.html'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Son durumu bu olan login fonksiyonumuza neler eklemişiz bir bakalım. Açıklamaya geçmeden hemen önce Python hakkında bir bilgi vermek istiyorum. Her fonksiyon bir tane değer döndürebilir. Yani bir şey aynı zamanda hem elma hem de armut olamaz. O yüzden eğer bir fonksiyon oluşturup için birden fazla return ifadesi yazıp çalıştırırsanız ilk değerin döndürüldüğünü, sonraki değerlerin ise çalışmadığını göreceksiniz. Çünkü Python döndürecek bir değer bulduğu zaman o fonksiyon ile işi bittiğini düşünür ve sonrakilere bakmaz bile. Bu aklımızda kalsın, çünkü hazırladığımız fonksiyonda da böyle bir durum söz konusu.</p>
<p>Hadi şimdi inceleyelim. 3. satırda bir şart koşmuşuz. Şartımız ziyaretçimizin <em>POST</em> metodunu kullanmış olması. Eğer metod POST ise bu şartımızdaki kodlarımız çalışacak. Ama eğer metodumuz POST değilse -ki bu durumda metod kesinlikle GET’tir. Çünkü fonksiyonumuz diğer metodları şuanda kabul etmiyor, listeye eklemedik.- 8. satır çalışacak ve ziyaretçiye form bilgisini döndürecek.</p>
<p>Varsayalım ki kullanıcı POST metodunu kullandı, yani formu doldurup butona bastı ve bize veri postaladı, gönderdi. Şimdi şartımızın içindeki kodlarımız çalışacak, 4. ve 5. satırlarda kullanıcının doldurduğu formdaki <em>‘username’</em> ve <em>‘password’</em> verilerini aldık ve yine aynı isimlerdeki değişkenlere atadık. Parola karşılaştırması yaparken bunlara ihtiyacımız olacak. Daha sonra ise ilk kez gördüğünüz fonksiyonları kullanarak bir şeyler yaptık. Aslında burada yaptığımız şey çok basit, <em>redirect</em> fonksiyonunu ziyaretçiyi yönlendirmek istediğimiz zaman kullanıyoruz. Ancak bu fonksiyon tam bir adres istiyor yönlendirmek için, yani argüman olarak bir URL istiyor. Biz de bu URL’yi <em>url_for</em> fonksiyonu ile oluşturuyoruz. Bu fonksiyon da metin olarak yönlendirme yapmak istediğimiz fonksiyonun adını alıyor ve onun için bir URL oluşturup döndürüyor. Yani orada zincirleme fonksiyon tamlaması gibi bir şey var 🙂.</p>
<p>Şimdi elimizde kullanıcı adı ve parola verileri de olduğuna göre kullanıcıyı giriş yaptırabiliriz. Bunun için yine Flask sınıfı olan <strong>session</strong>‘u içeri aktarmamız gerekiyor.</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="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="p">(</span><span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span>