Selamlar.
Flask uygulamalarında illa ki application factory kullanmışsınızdır. Kullanmadıysanız işleriniz bir parça daha sıkıntılı.
Factory pattern kullanmanın birçok avantajı bulunuyor. Bunlardan ilki yazı başlığında da belirtildiği gibi test işlemleri. Factory pattern kullanırken aslında environment set etmeniz mümkün oluyor.
Bir diğer avantajı ise tek bir uygulama için birden fazla ayara bağlı ayağa kaldırma set edebilirsiniz. Örnek vereyim. App factory ile 2 farklı environment’e bağlı ayağa kaldırma şöyle olsun;
1-) Development
export FLASK_APP=app.py
export FLASK_ENV=development
export FLASK_RUN_PORT=8080
# çalıştır
flask run
Yukarıdaki komutu çalıştırdığınızda aşağıdaki gibi bir çıktı alırsınız;
* Serving Flask app "app.py" (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 233-955-365
2-) Production
1-) Development
export FLASK_APP=app.py
export FLASK_ENV=production
export FLASK_RUN_PORT=8090
# çalıştır
flask run
Bunda da şöyle bir çıktı alırsınız;
* Serving Flask app "app.py"
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:8090/ (Press CTRL+C to quit)
Burada configlerden birisi de test oluyor tabii. Environment her zaman terminalden gelmeyebilir. Bu durumda oluşan factory ile environment set edebiliriz. Örnek verelim;
class Test(BaseConfig):
PRESERVE_CONTEXT_ON_EXCEPTION = False
DEBUG = True
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'dolphin_test.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
Yukarıdaki bizim Test için configlerimizi içeren bir Python class’ı. Normal database’imiz dolphin.db iken Test database’imiz dolphin_test.db oldu. Böylelikle development ve test dblerimiz tamamen birbirinden bağımsız olmuş oldu.
Peki ben nasıl bi factory method oluşturuyorum? Ben şöyle kullanıyorum.
config = {
"development": "config.Development",
"production": "config.Production",
"test": "config.Test"
}
db = SQLAlchemy()
def create_app(cfg: str = ''):
app = Flask(__name__)
env = (os.getenv('ENV') or os.getenv('FLASK_ENV')) or 'development'
if cfg:
env = cfg
app.config.from_object(config.get(env))
db.init_app(app)
app.app_context().push()
# burada diğer uygulama logici var, route ve login gibi.
return app
Yukarıdaki kodda eğer parametreyi default olarak set etmezseniz flask run komutunda flask script alır doğru çalışamayabilirsiniz.
Uygulamanın dotenv ya da export üzerinden gelmesine karşın kontrol işlemi yaptım. Bunlardan hiçbirisi yoksa da default development verdim. Eğer cfg truthy bir value ise gidip onu object üzerinden çağıracak.
Normalde aşağıdaki gibi bir test kodu yazmazsınız ama bu yazı için ilk aklıma geleni yazdım şöyle bir test işlemi yapacaksınız. Ben unittest kütüphanesini kullandım.
tests/test_user.py
import unittest
import random
import string
from werkzeug.security import generate_password_hash, check_password_hash
from app import create_app, db
from app.models import User
class UsersTestCase(unittest.TestCase):
"""
Test users table create
"""
def setUp(self):
self.app = create_app('test')
db.init_app(self.app)
db.create_all(app=self.app)
self.client = self.app.test_client()
def test_add_user(self):
letters = string.ascii_lowercase
random_letter = ''.join(random.choice(letters) for i in range(10))
random_letter_end = ''.join(random.choice(letters) for i in range(10))
name = "John Doe"
email = f"{random_letter}@{random_letter_end}"
username = random_letter
password = random_letter
new_user = False
user = User.query.filter((User.email==email) | (User.username==username)).first()
if not user:
new_user = User(name=name, email=email, username=username, password=generate_password_hash(password, method='sha256'))
db.session.add(new_user)
db.session.commit()
self.assertTrue(new_user, 'The user couldn\'t be added')
def test_get_users(self):
users = User.query.all()
self.assertTrue(len(users) > 0, 'There are no users')
setUp metodu içerisinde application factory içine parametre olarak test ortamını kullanacağımı söyledim. Böylelikle dolphin_test.db dosyası oluşturulacak ve development içerisindeki databaseim hiçbir değişikliğe uğramayacak.
Testimizi de unutmadan şöyle çalıştıralım;
python -m unittest discover -v -s tests/
Başarılı 🙂
test_add_user (test_users.UsersTestCase) ... ok
test_get_users (test_users.UsersTestCase) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.158s
OK
Umarım application factory kullanmanın önemini bu yazı ile anlatabilmişimdir 🙂 Hatalı, eksik gördüğünüz şeyler var ise belirtirseniz güncellerim hatta ekstra katmak istediğiniz var ise yine güncellerim yazıyı 🙂
Okuduğunuz için teşekkürler 🙂
Kaynaklar
- https://flask.palletsprojects.com/en/1.1.x/patterns/appfactories/
- https://hackersandslackers.com/demystifying-flask-application-factory/