Web Forms
- Projemizde Web Formlarını kullanmak için
Flask-WTF
extension'ını kuracağız.pip install Flask-WTF
diyerek kuruyoruz. - Python shell'i açarak flask_wtf'i import ederseniz, versiyonunu görebilirsiniz.
xxxxxxxxxx
>>> import flask_wtf
>>> flask_wtf.__version__
'0.14.2'
- Bu arada projemizin ihtiyaç duyduğu kütüphaneleri, paketleri vs.
requirements.txt
dosyası altında tutacağız. Böylece projemizi paylaştığımız zaman, takım arkadaşlarımız veya diğer insanlar$ pip install -r requirements.txt
komutu ile kolayca proje gereksinimlerini kurup projeyi ayağa kaldırabilir. - Örnek
requirements.txt
dosyamız aşağıdaki gibi duracak.==
'den sonrası versiyonları belirtmektedir.
xxxxxxxxxx
Flask==0.12.2
Flask-WTF==0.14.2
Configurations (Yapılandırmalar)
- Projemiz şimdiye kadar herhangi bir yapılandırmaya ihtiyaç duymadı çünkü basit işler yaptık. Ancak proje dışarıdan paketler yüklendikçe, büyüdükçe belirli yapılandırmalara(konfigürasyonlara) ihtiyaç duyacak.
- Projemizin temel dizinine bir adet modül ekleyelim.
blog/config.py
xxxxxxxxxx
import os
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'cok-gizli-anahtar'
SECRET_KEY
Flask uygulamaları için çok önemlidir. Bazı Flask kütüphaneleri, yazılımlarıSECRET_KEY
'i kriptografi için, token üretmek için kullanırlar. Flask-WTF de web formlarında CSRF (Cross-Site Request Forgery) saldırılarına karşı güvenlik amacıyla buSECRET_KEY
'i kullanır.- Web formlarında bu SECRET_KEY ile token anahtar (uzun karakterlerden oluşan stringler) üretilir. Her form submit edildiğinde farklı anahtar üretilir. Böylece saldırılara karşı güvenlik önlemi alınır.
- Şuradan CSRF nedir diye bakabilirsiniz -> django - What is a CSRF token ? What is its importance and how does it work? - Stack Overflow
- Bizim
SECRET_KEY
'imiz önceden tanımlı var ise onu alır yoksa elle girdiğimizcok-gizli-anahtar
gibi verdiğimiz stringi alır. Stringi güçlü ve zor karakterlerden oluşturmak gerekli tabii ki. Burada örnek olması açısından yazdık. - Konfigürasyonumuzu projemize uygulayalım.
myapp/__init__.py
xxxxxxxxxx
# external
from flask import Flask
#internal
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
from myapp import routes
config.py
dosyamızdanConfig
classını import ettik veapp.config.from_object()
methoduna parametre olarak verince konfigürasyonumuz bağlandı.
User Log-in Form (Kullanıcı Giriş Formu)
- Flask-WTF, Pyhon sınıflarını(classes) web formları temsil etmek için kullanır. Bir form sınıfı, form alanlarını sınıf değişkenleri olarak tanımlar.
- Modülerlik açısından formlarımızı
myapp/forms.py
dosyasında tanımlayalım.
xxxxxxxxxx
# external
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
class LoginForm(FlaskForm):
"""Kullanıcı Giriş formu için alanları tanımlayan form sınıfı"""
username = StringField('Kullanıcı Adı', validators=[DataRequired()])
password = PasswordField('Parola', validators=[DataRequired()])
remember_me = BooleanField('Beni Hatırla!')
submit = SubmitField('Giriş Yap')
LoginForm
sınıfımızFlaskForm
sınıfını extend ediyor.- StringField, Password Field .. gibi sınıflardan obje oluşturarak username, password.. gibi alanlara atama yaptık. Bunlar bizim web formlarımızı temsil edecek.
- StringField sınıfına ve diğer sınıflara bakacak olursak; ilk parametre gösterim amaçlı yani label, varsa ikinci parametre olan validators Form alanının boş gitmemesi, düzgün formatta gitmesi için kullanılıyor. Burada biz boş olmaması için DataRequired( ) sınıfını kullandık.
- submit butonu için de tanımlama yapmak gerekiyor.
Form Templates
- Tanımladığımız
LoginForm
sınıfının gösterimi için HTML template'e ihtiyacımız var. myapp/templates/login.html
dosyasına alanları yazıyoruz.
xxxxxxxxxx
{%extends 'base.html'%}
{%block content%}
<h1>Giriş Yap</h1>
<form action="" method="POST">
{{form.hidden_tag()}}
<table>
<tr>
<td>{{form.username.label}}</td>
<td>{{form.username(size=32)}}</td>
</tr>
<tr>
<td>{{form.password.label}}</td>
<td>{{form.password(size=32)}}</td>
</tr>
<tr>
<td>
{{form.remember_me()}}
{{form.remember_me.label}}
</td>
</tr>
</table>
<p>{{ form.submit() }}</p>
</form>
{%endblock%}
- Bu login template'i
LoginForm
'dan oluşturulmuş bir nesne(form
) bekleyecektir. <form action=" " method = "POST" ...>
satırındaki action form submit edildiğinde hangi url'e gidileceğini gösterir. Request tipinin GET yerine POST seçilmesi daha güvenlidir. GET ile gönderilen formların alanları ve alanlara yazılan değerler URL alanına(adres çubuğuna) eklenir. POST ile gönderildiğinde URL alanında görülmez. Form datası request body'ye eklenir.{{form.hidden_tag()}}
CSRF ataklarına karşı güvenlik içintoken anahtar
ekler.SECRET_KEY
veform.hidden_tag()
işlemlerini gerçekleştirdiyseniz güvenlik için gerisini Flask-WTF sizin için yapacaktır.- Daha önceden HTML formları ile çalışanlar için bu form tag'leri ilginç gelebilir. Burada herşey
form
objesi üzerinden gidiyor.form.username.label
üzerinde label kullanımını görüyoruz.form.username(size=32)
kod parçasındasize
HTML attribute(özellik, nitelik)'lerini temsil eder. - HTML attribute'lerini bazı yerlerde özellikle kullanmak gerekebilir. Bunlar için örnek kullanım:
xxxxxxxxxx
{{ form.myfield(name='test', **{'data-provide':'typeahead','data-items':'3','data-source': '["x","y","z"]'}) }}
- Şimdi login.html template'ini render edecek methodu yazalım.
app/routes.py
içerisinde yeni bir method tanımlıyoruz.
xxxxxxxxxx
from forms import LoginForm
#...
route('/login') .
def login():
"""Kullanıcı login isteklerini karşılayacak method"""
form = LoginForm()
return render_template('login.html', title="Giriş Yap", form=form)
- base.html'e de Navbar için
<td><a href="/login">Giriş Yap</a></td>
satırını ekledik.
- Giriş Yap butonuna bastıysanız Method not Allowed hatası alacaksınız. Şu ana kadar sadece gösterim işini yaptık. Form'un submit sonrası iş mantığı için bazı eklemeler yapmamız gerekiyor.
- Şimdi form'dan dataları alalım.
myapp/routes.py
xxxxxxxxxx
from flask import render_template, flash, redirect
#...
route('/login', methods=['GET', 'POST']) .
def login():
"""Kullanıcı login isteklerini karşılayacak method"""
form = LoginForm()
if form.validate_on_submit():
flash("Giriş yapma isteği alındı. Kullanıcı: {}, Beni hatırla: {}".format(\
form.username.data, form.remember_me.data))
return redirect('/index')
return render_template('login.html', title="Giriş Yap", form=form)
@app.route
varsayılan olarakGET
methodu isteklerini karşılıyor. Burada bizPOST
isteklerini de kabul edeceğimizi belirttik.- Form submit edildiğinde
form.valiadate_on_submit()
değeri True olur ve form dataları ile ne yapacaksak burada yaparız. - Form submit edilmediğinde
if
bloğunu es geçer ve login sayfasını render eder. flash()
methodu kullanıcıya mesaj vermek için kullanılan faydalı bir methoddur.redirect()
methoduna verilen URL ile istediğiniz URL'e yönlendirme yapabilirsiniz. Burada kullanıcı giriş yaptıysa index sayfasına yönlendirme yaptık.- Şimdi
base.html
sayfamızdaflash()
methodundan gelen mesajları gösterecek kodları yazalım.
xxxxxxxxxx
<html>
<head>
{% if title%}
<title>{{title}}- Blog</title>
{% else%}
<title>Blog'a hoşgeldiniz.</title>
{%endif%}
</head>
<table>
<tr>
<td><a href="/index">Anasayfa</a></td>
<td><a href="/login">Giriş Yap</a></td>
</tr>
</table>
<hr>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<hr />
<!-- body olarak kullanılacak kısım -->
{% block content %} {%endblock%}
</html>
- Burada with yapısı ile Flask'in bize sunduğu
get_flashed_messages()
methodundan dönen mesajları,if
bloğunda boş değilse for döngüsü ile<li>
tag'lerinde gösteriyoruz. login.html
'i de hata mesajları için biraz düzenleyelim.
xxxxxxxxxx
{%extends 'base.html'%} {%block content%}
<h1>Kaydol</h1>
<form action="" method="POST">
{{form.hidden_tag()}}
<table>
<tr>
<td>{{form.username.label}}</td>
<td>{{form.username(size=20)}}</td>
<td>{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</td>
</tr>
<tr>
<td>{{form.password.label}}</td>
<td>{{form.password(size=20)}}</td>
<td>{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</td>
</tr>
<tr>
<td>{{form.remember_me()}} {{form.remember_me.label}} </td>
</tr>
</table>
<p>{{ form.submit() }}</p>
</form>
{%endblock%}
url_for( ) kullanımı
- Flask'in bize sunduğu kullanışlı methodlar'dan biri de
url_for()
methodudur. Bu method yardımı ile html sayfalarındaki/index, /login
gibi URL'ler yerineurl_for('method_adi')
diyerek kolayca python tarafına method isimleri ile erişebileceğiz. Linklerin değişiminin method isimlerinin değişiminden fazla olması, kompleks URL'lere erişim ve o URL'leri kullanımınurl_for()
ile daha kolay olması, bizi bu kullanışlı methoda yönlendiriyor.base.html
'de değişiklik yapalım.
xxxxxxxxxx
<td><a href="{{url_for('index')}}">Anasayfa</a></td>
<td><a href="{{url_for('login')}}">Giriş Yap</a></td>
- Ve
redirect('/index')
yerineredirect(url_for('index'))
uygulayalım.from flask import url_for
diyerek import etmeyi unutmayalım. - Şimdi değişiklikler sonrası uygulamanın son halini görelim.
Giriş yaptıktan sonra ..
Yorumlar