Hımm,
Merhabalar,
Bu programlama jargonlarına çok fazla hakim değilim. Ben de son zamanlarda flask ile kendi kişisel websitemi geliştiriyordum. Bu site için bir comment-like
sistemine ihtiyaç duydum. Aradım ve Flask-Comments gibi bir kütüphane buldum. Ama tasarımı hoşuma gitmedi. Ben de kendi yorum-beğeni sistemimi yazmak istedim.
Tasarlamam gereken yorum-beğeni sistemi nasıl olmalıdır sorusuna şöyle yanıtlar verirken buldum kendimi:
- Bir yorumu veya beğeniyi oluşturmak istediğimiz zaman, tam sayfa yenilenmesinin yaşanmaması gerekir. Örnek: Youtube’da yorum yaptığınız zaman sayfa yenilenmez, html içeriği dinamik olarak oluşturulur.
- HTML sayfasında mevcut olan bir yorumun veya beğeninin değiştirilme işlemi esnasında sayfanın tekrar yüklenmemesi gerekir.
- Mevcut bir sayfadaki bir html içeriği eğer bütün html içeriğinden çıkarılacaksa, sayfanın yeniden yüklenmemesi gerekir.
- Sayfa açılırken, hangi html yapılarının değiştirilebilir, hangi yapıların değiştirilemez olduğu, istek gönderen kullanıcının kimliğine göre belirlenir.
Bu projede AngularJS kullanmadım hiç. Frontend kısmını js ile, backend kısmını ise Flask ile hallediyorum. Yorumu meydana getiren html içeriği javascript dosyasında bir class
olarak tutuluyor. card
isimli, biçimi yorum olmaya müsait bir bootstrap
html etiket grubu bu. Özellikleri, hem kullanıcıya ait olan bir takım kişisel verilerle, hem de sınıfa ait genel özelliklerle belirleniyor. Yorumların ve beğenilerin olduğu templatelerde full-page refresh özelliğini kaldırmak için istek formunu Flask-WTF formlarını kullanarak değil doğrudan javascript kullanarak oluşturuyorum. Eminim, şu anda yaptığım işlemleri kolaylaştıran araçlar geliştirilmiştir.
Tasarladığım uygulamada, kullanıcı, current_user
olarak sayfaya giriş yaparken, haberi dahi olmadan, javascript yoluyla otomatik olarak backend tarafına istek tipini gönderir. backend kısmında bu istek tipi ve current_user
bilgisi kullanılarak, sayfada bulunacak olan bütün yorumların ve beğenilerin user
niteliiklerinin bu current_user
değerine eşit olup olmadığı öz-yinelemeli
bir şekilde sorgulanır, sonra da bu sorgulamaya göre belirli kısımları kullanıcı tarafından değiştirilebilir olan bir html içeriği oluşturulur. Bütün bu işleme dinamik html yapıları oluşturma süreci de diyorlar. current_user
, anonim olduğunda yorumlar ve beğeniler yine yüklenir ama yüklenen yorumların mesela Update ve Delete buttonları görünmez.
Özetle, genel olarak kullanıcıyla temas frontend
kısmında gerçekleşir, kullanıcının istediği bilginin ne olduğu da bu aşamada backend
tarafına gönderilir (Request
) . Gönderilen bilgi backend tarafında, current_user
'a göre sorgulanır sonra da current_user
'a göre olan bilgileri içeren bir json Response döner, doğrulanamazsa da başka bir status code’a sahip bir Response döner. Sonra da bu json tipindeki cevaba (response’a) göre html içeriğinin nasıl oluşması gerektiğini belirleriz.
Her bir article ve yorum için bir tane card nesnesi oluşturulur. Nesnelerin özellikleri ise, oluşma esnasında veritabanındaki tabloların birbirleriyle olan ilişkilerine göre düzenlenir. Article için Card nesnesinin sadece footer kısmı, yorum için ise header, body ve footer kısımları aktif olur.
Her bir article için çalışacak sayfa:
article.html
{% extends "base.html" %}
{% block content %}
{% include "view.html" %}
{% block comment_block %}
<script>
init_card();
init_comments();
</script>
{% endblock comment_block %}
{% endblock content %}
main.js’den gelen kurucu fonksiyonlar.
function init_card() {
var div = document.createElement("div");
div.id = `comments-${title}`;
document.body.append(div);
var card = new Card(
primary_id=title,
secondary_id="secondary",
content="",
src="",
href="",
date="",
username="",
like="",
dislike=""
);
card.footer();
}
function init_comments() {
var form = new FormData();
form.append("comments", true)
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if ((xhr.status === 200) && (xhr.readyState == XMLHttpRequest.DONE)) {
var comments = JSON.parse(xhr.responseText);
if (comments.length > 0) {
recursively_init_comments(comments, `${title}-secondary`)
}
article_stats(comments);
}
}
xhr.open("POST", `/article/${title}`, true);
xhr.send(form);
}
function recursively_init_comments(obj, primary_id) {
if (obj.length > 0) {
document.getElementById(`span-comments-${primary_id}`).innerHTML = `(${obj.length})`;
}
for (var child of obj) {
create_card(primary_id=primary_id, comment=child);
if (child["children"].length > 0) {
recursively_init_comments(child["children"], `${primary_id}-${child.id}`);
}
}
}
function article_stats() {
if (article_like_count > 0) {
document.getElementById(`span-like-${title}-secondary`).innerHTML = `(${article_like_count})`;
}
if (article_dislike_count > 0) {
document.getElementById(`span-dislike-${title}-secondary`).innerHTML = `(${article_dislike_count})`;
}
}
function create_card(primary_id, comment) {
var card = new Card(
primary_id=primary_id,
secondary_id=comment.id,
content=comment["content"],
src=comment["src"],
href=comment["href"],
date=comment["date"],
username=comment["username"],
like=comment["likes"],
dislike=comment["dislikes"]
);
card.header();
card.body();
card.footer();
document.getElementById(`hidden-value-${primary_id}-${comment['id']}`).value = comment["hidden_value"];
if (comment["likes"] > 0) {
document.getElementById(`span-like-${primary_id}-${comment['id']}`).innerHTML = `(${comment["likes"]})`;
}
if (comment["dislikes"] > 0) {
document.getElementById(`span-dislike-${primary_id}-${comment['id']}`).innerHTML = `(${comment["dislikes"]})`;
}
if (comment["username"] != comment["current_user"]) {
document.getElementById(`btn-update-${primary_id}-${comment['id']}`).style.display = "none";
document.getElementById(`btn-delete-${primary_id}-${comment['id']}`).style.display = "none";
}
}
Card
nesnesinin kodları ve yorumlar üzerinde yapılan işlemler için yazılmış olan javascript ve python fonksiyonlarını paylaşmıyorum. Sadece şunu söyleyebilirim ki, bir model üzerinde yapılmak istenen işlem için, backend tarafında bir python fonksiyonu, frontend tarafında da bir javascript fonksiyonu çalışıyor. Javascript fonksiyonu Python’a bir istek gönderiyor, Python fonksiyonu ise Javascript’e bir yanıt veriyor.
Backend tarafında, javascriptten yapılan isteklere göre bir takım yardımcı fonksiyonlar kullanılarak işlemler yapılır:
Backend:
@articles.route("/article/<string:article_title>", methods=["GET", "POST"])
def article(article_title):
a = Article.query.filter_by(title=article_title).first_or_404()
if "comments" in request.form:
return response_children(a, request.url_root)
if request.method == "POST":
if current_user.is_authenticated:
if "add" in request.form:
return add_comment(request, db, a)
elif "delete" in request.form:
return delete_comment(request, db, a)
elif "update" in request.form:
return update_comment(request, db)
elif "like_dislike" in request.form:
return like_dislike_comment(request, db, a)
else:
return Response("", 404)
if request.method == "GET":
if a:
data = {"title": a.title}
else:
data = {"title": None}
Response(json.dumps(data), 201)
return render_template(
"articles/article.html",
title=a.title,
articles=[a],
primary_id=a.id
)
Backend kısmında çalışan ve sayfa açılır açılmaz kullanıcının sadece belirli yerlerini “değiştirebileceği” html içeriklerinin sunulmasında rol alan yardımcı fonksiyonlardan bir tanesi şu:
def response_children(model, url_root):
data = find_children_recursively(orphan_comments(model), url_root)
return Response(json.dumps(data), 200)
Bu anlattığım, html sayfası açılırken gerçekleşen bir işlem bu arada. Sayfa içinde yapılabilecek add_comment
, update_comment
, delete_comment
, like_dislike_comment
gibi değişiklikler için de benzer işlemler uygulanır.