Advanced Application Development
Transactions
Во BiblioPremium се користат трансакции кај сценарија во кои една корисничка акција предизвикува повеќе поврзани промени во базата. На овој начин се обезбедува сите операции да бидат успешно извршени како една целина или, во случај на грешка, сите промени да бидат поништени.
Трансакција при купување книга
При купување книга се извршуваат повеќе операции во рамки на една трансакција:
- Се проверува цената и достапната количина на книгата.
- Се креира нова нарачка во табелата
naracki. - Се додава ставката во табелата
sodrzi. - Се евидентира плаќањето во табелата
plakanja. - Се намалува количината на залиха во табелата
knigi.
Доколку сите операции се успешни се повикува:
conn.commit()
Доколку настане грешка, се повикува:
conn.rollback()
со што се поништуваат промените направени во рамки на трансакцијата.
Дел од имплементацијата:
try:
cursor.execute("""
INSERT INTO project.naracki
(datum, status, vkupna_cena, korisnik_id)
VALUES
(CURRENT_TIMESTAMP, %s, %s, %s)
RETURNING naracka_id;
""", ("ZAVRSENA", vkupna_cena, session["korisnik_id"]))
naracka_id = cursor.fetchone()[0]
cursor.execute("""
INSERT INTO project.sodrzi
(naracka_id, kniga_id, kolicina, edinechna_cena)
VALUES (%s, %s, %s, %s);
""", (naracka_id, kniga_id, kolicina, cena))
cursor.execute("""
INSERT INTO project.plakanja
(datum, iznos, nacin_na_plakjanje, status, naracka_id)
VALUES
(CURRENT_TIMESTAMP, %s, %s, %s, %s);
""", (vkupna_cena, "KARTICKA", "USPESNO", naracka_id))
cursor.execute("""
UPDATE project.knigi
SET kolicina_na_zaliha = kolicina_na_zaliha - %s
WHERE kniga_id = %s;
""", (kolicina, kniga_id))
conn.commit()
except Exception as e:
conn.rollback()
Трансакцијата беше тестирана преку апликацијата со успешно купување на книгата „Ikigai“. Беше креирана нарачка #16 со количина 1 и вкупна цена 750 денари. Нарачката беше успешно прикажана и во историјата „Мои нарачки“.
Трансакција при зачувување кориснички преференции
Трансакција се користи и при промена на корисничките преференции. Старите жанровски и категориски преференции се бришат, а потоа се внесуваат новоизбраните вредности.
Доколку сите операции се успешни се извршува commit(), а при грешка rollback(). На овој начин корисникот не може да остане со делумно ажурирани преференции.
Database Connection Pooling
За управување со конекциите кон PostgreSQL базата е имплементиран connection pool со psycopg2.pool.
При стартување на апликацијата се креира pool со минимално 1 и максимално 10 конекции:
from psycopg2 import pool
db_pool = pool.SimpleConnectionPool(
1,
10,
host="localhost",
port=tunnel.local_bind_port,
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD
)
За земање и враќање конекција се користат функции:
def get_db_connection():
return db_pool.getconn()
def release_db_connection(conn):
db_pool.putconn(conn)
Пред пристап до базата, потребната конекција се зема од pool:
conn = get_db_connection() cursor = conn.cursor()
По завршување на работата cursor-от се затвора, а конекцијата се враќа во pool:
cursor.close() release_db_connection(conn)
Овој пристап се користи во рутите кои пристапуваат до базата, вклучувајќи пребарување и приказ на книги, препораки, омилени книги, нарачки, преференции и административните функционалности.
Connection pool-от беше успешно тестиран при стартување и користење на апликацијата. Апликацијата успешно зема конекции од pool-от, извршува операции кон базата и ги враќа конекциите за повторна употреба.
Пагинација на административни податоци
Во администраторскиот дел е имплементирана пагинација за прегледот на нарачките и плаќањата.
Наместо сите нарачки и плаќања да се вчитуваат и прикажуваат одеднаш, податоците се прикажуваат по 10 записи на една страница.
За ова во SQL пребарувањата се користат LIMIT и OFFSET.
LIMIT го ограничува бројот на записи што се прикажуваат на една страница, додека OFFSET определува од кој запис треба да започне прикажувањето.
Тековната страница се зема преку параметарот page:
page = request.args.get("page", 1, type=int)
per_page = 10
offset = (page - 1) * per_page
Потоа во SQL пребарувањето се користат:
LIMIT %s OFFSET %s;
со вредностите:
(per_page, offset)
Пред прикажувањето се пресметува и вкупниот број на записи:
SELECT COUNT(*) FROM project.naracki;
односно за плаќањата:
SELECT COUNT(*) FROM project.plakanja;
Вкупниот број на страници се пресметува според вкупниот број на записи и бројот на записи што се прикажуваат на една страница:
total_pages = (total_orders + per_page - 1) // per_page
За плаќањата на ист начин се користи вкупниот број на плаќања.
Во административниот интерфејс се прикажуваат тековната страница и вкупниот број на страници, а администраторот може да се движи помеѓу нив преку копчињата „Претходна“ и „Следна“.
Пагинацијата е имплементирана на:
- прегледот на сите нарачки;
- прегледот на сите плаќања.
Со ова административниот дел е попрегледен и не е потребно сите записи да се вчитуваат и прикажуваат одеднаш кога бројот на нарачки и плаќања ќе се зголеми.
Заклучок
Во оваа фаза апликацијата е надградена со трансакциско извршување на операции кои бараат атомарност, connection pooling за поефикасно управување со конекциите кон PostgreSQL базата и пагинација за поефикасен и попрегледен приказ на административните податоци.
