Selamlar,
Ben Rest API mimarisini öğrenmek için Python’ın Flask framework’ünü ve Sqlite veritabanını kullanarak basit bir uygulama yapıyorum. Uygulamanın olayı temel olarak, girişini yaptığımız harcama kalemleri doğrultusunda ne kadar bakiyemizin kaldığını göstermek, kişisel bir gider defteri gibi.
Şu ana kadar hazırladığım kısmı, veritabanı işlemlerini (eğer yoksa tablo oluşturmak ve CRUD) gerçekleştirecek kodlar ve veritabanı ile iletişim kurmayı sağlayacak API’ın kodları.
Veritabanındaki tablonun yapısı:
Veritabanı işlemlerinin gerçekleştiği kısım:
db.py
#!/usr/bin/env python3
import sqlite3
import logging
logging.basicConfig(level=logging.DEBUG)
class DBUtil:
''' Database class for OOP architecture. '''
def __init__(self, db_name, table_name):
self.db_name = db_name
self.table_name = table_name
try:
self.connection = sqlite3.connect(db_name, check_same_thread=False)
self.cursor = self.connection.cursor()
self.create_table_if_not_exists()
except sqlite3.Error as err:
logging.error(err)
def create_table_if_not_exists(self):
''' Create table. '''
query = f"CREATE TABLE IF NOT EXISTS {self.table_name} (id INTEGER PRIMARY KEY, \
item_name VARCHAR(256) NOT NULL, category VARCHAR(256) NOT NULL, price REAL, \
payment_method VARCHAR(256) NOT NULL)"
try:
self.cursor.execute(query)
except sqlite3.Error as err:
logging.error(err)
def insert_data(self, item_name: str, category: str, price: float, payment_method: str):
''' Insert data.
item_name: Bought item name
category: Bought item category
price: Bought item price
payment_method: Bought item payment method '''
query = f"INSERT INTO {self.table_name} \
(item_name, category, price, payment_method) VALUES (?,?,?,?)"
try:
self.cursor.execute(query, (item_name, category, price, payment_method))
self.connection.commit()
except sqlite3.Error as err:
logging.error(err)
self.cursor.execute(f"SELECT * FROM {self.table_name} WHERE id == last_insert_rowid();")
result = self.cursor.fetchone()
logging.info(f"Row inserted: {result}")
return result
def get_all_data(self):
''' Get all data.'''
query = f"SELECT * FROM {self.table_name};"
results = None
try:
self.cursor.execute(query)
self.connection.commit()
results = self.cursor.fetchall()
except sqlite3.Error as err:
logging.error(err)
return results
def get_data(self, item_id):
''' Get specific data.'''
query = f"SELECT * FROM {self.table_name} WHERE id == ?"
try:
self.cursor.execute(query, (item_id,))
result = self.cursor.fetchone()
except sqlite3.Error as err:
logging.error(err)
return result
def update_data(self, item_id, update_column, new_data):
''' Update data. '''
query = f"UPDATE {self.table_name} SET {update_column} = ? WHERE id == ?"
try:
self.cursor.execute(query, (new_data, item_id))
self.connection.commit()
except sqlite3.Error as err:
logging.error(err)
updated_row = self.get_data(item_id)
return updated_row
def delete_data(self, item_id):
''' Delete data. '''
deleted_row = self.get_data(item_id)
query = f"DELETE FROM {self.table_name} WHERE id == ?"
try:
self.cursor.execute(query, (item_id,))
self.connection.commit()
except sqlite3.Error as err:
logging.error(err)
return deleted_row
API kodları:
api.py
from flask import Flask, jsonify, request
from db import DBUtil
db_agent = DBUtil("pocketDB", "pocket_table")
app = Flask(__name__)
@app.route("/pocket/getAllData", methods=["GET"])
def get_all_data():
# TODO: data'yi json olarak dondur
all_data = db_agent.get_all_data()
json_data = []
for item in all_data:
json_data.append({"id": item[0],
"item_name": item[1],
"category": item[2],
"price": item[3],
"payment_method": item[4]})
return jsonify(json_data)
@app.route("/pocket/getData/<int:item_id>", methods=["GET"])
def get_data(item_id):
# TODO: data'yi json olarak dondur
item_data = db_agent.get_data(item_id)
json_data = {"id": item_data[0],
"item_name": item_data[1],
"category": item_data[2],
"price": item_data[3],
"payment_method": item_data[4]}
return jsonify(json_data)
@app.route("/pocket", methods=["POST"])
def post_data():
item_name = request.json["item_name"]
category = request.json["category"]
price = request.json["price"]
payment_method = request.json["payment_method"]
response = db_agent.insert_data(item_name, category, price, payment_method)
json_data = {
"id": response[0],
"item_name": response[1],
"category": response[2],
"price": response[3],
"payment_method": response[4]
}
return jsonify(json_data)
@app.route("/pocket", methods=["PUT"])
def put_data():
item_id = request.json["id"]
update_column = request.json["update_column"]
new_data = request.json["new_data"]
response = db_agent.update_data(item_id, update_column, new_data)
json_data = {
"id": response[0],
"item_name": response[1],
"category": response[2],
"price": response[3],
"payment_method": response[4]
}
return jsonify(json_data)
@app.route("/pocket/<int:item_id>", methods=["DELETE"])
def delete_data(item_id):
response = db_agent.delete_data(item_id)
json_data = {
"id": response[0],
"item_name": response[1],
"category": response[2],
"price": response[3],
"payment_method": response[4]
}
return jsonify(json_data)
if __name__ == "__main__":
app.run(debug=False)
Sorularım:
-
Veritabanı işlemleri için db.py’daki DBUtil sınıfından nesneyi api.py’da üretmem doğru mu? Yanlış gelmesinin sebebi, api.py’da yalnızca logic işlemlerini gerçekleştirmem, herhangi bir implementasyon yapmamam gerektiğini düşünmem. Ama bu durumda da örneğin tüm datayı çekmek için gerekli
all_data = db_agent.get_all_data()
gibi bir satırda, db_agent’i nasıl çağıracağım? -
Veritabanından gelen SQL sorgusu cevabını, db.py’da, veritabanından geldiği gibi mi; yoksa api.py’da API Server’a sunarken mı JSON’a çevirmem lazım? Yani
item_data = db_agent.get_data(item_id)
# >>> (1, 'gofret', 'gida', 21.45, 'nakit')
json_data = {"id": item_data[0],
"item_name": item_data[1],
"category": item_data[2],
"price": item_data[3],
"payment_method": item_data[4]}
# >>> {'id': 1, 'item_name': 'gofret', 'category': 'gida', 'price': 21.45, 'payment_method': 'nakit'}
return jsonify(json_data)
şu dönüştürme api.py’da mı yapılmalı db.py’da mı?
-
Her bir CRUD işlemini (commit, execute, connection vs. gerektiren) try-catch’e sarmam doğru mu? Ancak bu şekilde düzgün loglama yapabileceğimi düşündüm.
-
Class yapısını kullanış şeklimde bir terslik var mı? “Bu böyle kullanılmaz” veya çok daha basit halledilebilecek şeyi gereksizce uzatmış olabileceğim bir durum gibi.
-
Şu anda API ve DB iletişim kurup CRUD operasyonları yapabiliyor ancak ben çok yetersiz olduğunu düşünüp, eksiklerimin ne olduğunu göremiyorum. Çalışıyor ama bu doğru yol değil sadece bir workaround yapmışsın diyeceğiniz veya gözünüze çarpan herhangi bir usülsüzlüğü bildirirseniz çok sevinirim.
Vakit ayırdığınız için şimdiden teşekkür ederim.