From a912adb1f7ba4adcb0c01a79f7100993098fce1d Mon Sep 17 00:00:00 2001 From: fahmi-ramadhan Date: Sat, 23 Sep 2023 21:06:33 +0700 Subject: [PATCH 1/7] Revert "Revert "Update Tugas 3"" This reverts commit 0b4a61b0ddec9798a2d700b41f16d99226502eb0. --- main/migrations/0004_item_user.py | 22 +++++++++++++ main/models.py | 2 ++ main/templates/login.html | 45 ++++++++++++++++++++++++++ main/templates/main.html | 8 +++++ main/templates/register.html | 34 ++++++++++++++++++++ main/urls.py | 5 ++- main/views.py | 53 +++++++++++++++++++++++++++---- 7 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 main/migrations/0004_item_user.py create mode 100644 main/templates/login.html create mode 100644 main/templates/register.html diff --git a/main/migrations/0004_item_user.py b/main/migrations/0004_item_user.py new file mode 100644 index 0000000..8443e84 --- /dev/null +++ b/main/migrations/0004_item_user.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.5 on 2023-09-23 03:56 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('main', '0003_rename_book_item'), + ] + + operations = [ + migrations.AddField( + model_name='item', + name='user', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/main/models.py b/main/models.py index a0e8da2..4c7df94 100644 --- a/main/models.py +++ b/main/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.contrib.auth.models import User class Item(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) name = models.CharField(max_length=255) author = models.CharField(max_length=255) category = models.CharField(max_length=255) diff --git a/main/templates/login.html b/main/templates/login.html new file mode 100644 index 0000000..ce64cd6 --- /dev/null +++ b/main/templates/login.html @@ -0,0 +1,45 @@ +{% extends 'base.html' %} + +{% block meta %} + Login +{% endblock meta %} + +{% block content %} + +
+ +

Login

+ +
+ {% csrf_token %} + + + + + + + + + + + + + + + +
Username:
Password:
+
+ + {% if messages %} + + {% endif %} + + Don't have an account yet? Register Now + +
+ +{% endblock content %} \ No newline at end of file diff --git a/main/templates/main.html b/main/templates/main.html index 7801015..c11db01 100644 --- a/main/templates/main.html +++ b/main/templates/main.html @@ -27,6 +27,8 @@

There are currently {{total_books}} books with {{books|length}} book titles {% endfor %} +

Sesi terakhir login: {{ last_login }}
+
@@ -35,5 +37,11 @@

There are currently {{total_books}} books with {{books|length}} book titles + + + + {% endblock content %} diff --git a/main/templates/register.html b/main/templates/register.html new file mode 100644 index 0000000..9bed1d0 --- /dev/null +++ b/main/templates/register.html @@ -0,0 +1,34 @@ +{% extends 'base.html' %} + +{% block meta %} + Register +{% endblock meta %} + +{% block content %} + + + +{% endblock content %} \ No newline at end of file diff --git a/main/urls.py b/main/urls.py index 4e166a2..8c31ae3 100644 --- a/main/urls.py +++ b/main/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id +from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id, register, login_user, logout_user app_name = 'main' @@ -10,4 +10,7 @@ path('json/', show_json, name='show_json'), path('xml//', show_xml_by_id, name='show_xml_by_id'), path('json//', show_json_by_id, name='show_json_by_id'), + path('register/', register, name='register'), + path('login/', login_user, name='login'), + path('logout/', logout_user, name='logout'), ] \ No newline at end of file diff --git a/main/views.py b/main/views.py index 85c6015..f8f2317 100644 --- a/main/views.py +++ b/main/views.py @@ -1,23 +1,29 @@ -from django.shortcuts import render -from django.http import HttpResponseRedirect +from django.shortcuts import render, redirect +from django.http import HttpResponseRedirect, HttpResponse from main.forms import ItemForm from django.urls import reverse from main.models import Item -from django.http import HttpResponse from django.core import serializers +from django.contrib.auth.forms import UserCreationForm +from django.contrib import messages +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required +import datetime +@login_required(login_url='/login') def show_main(request): - books = Item.objects.all() + books = Item.objects.filter(user=request.user) total_books = 0 for book in books: total_books += book.amount context = { 'app_name': 'Library Management System', - 'student_name': 'Fahmi Ramadhan', + 'student_name': request.user.username, 'class': 'PBP A', 'books': books, 'total_books': total_books, + 'last_login': request.COOKIES['last_login'], } return render(request, "main.html", context) @@ -26,12 +32,47 @@ def add_book(request): form = ItemForm(request.POST or None) if form.is_valid() and request.method == "POST": - form.save() + item = form.save(commit=False) + item.user = request.user + item.save() return HttpResponseRedirect(reverse('main:show_main')) context = {'form': form} return render(request, "add_book.html", context) +def register(request): + form = UserCreationForm() + + if request.method == "POST": + form = UserCreationForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Your account has been successfully created!') + return redirect('main:login') + context = {'form':form} + return render(request, 'register.html', context) + +def login_user(request): + if request.method == 'POST': + username = request.POST.get('username') + password = request.POST.get('password') + user = authenticate(request, username=username, password=password) + if user is not None: + login(request, user) + response = HttpResponseRedirect(reverse("main:show_main")) + response.set_cookie('last_login', str(datetime.datetime.now())) + return response + else: + messages.info(request, 'Sorry, incorrect username or password. Please try again.') + context = {} + return render(request, 'login.html', context) + +def logout_user(request): + logout(request) + response = HttpResponseRedirect(reverse('main:login')) + response.delete_cookie('last_login') + return response + def show_xml(request): data = Item.objects.all() return HttpResponse(serializers.serialize('xml', data), content_type="application/xml") From 423c02b90f2f28d3e3ceeb15b7772f11a02b7cf8 Mon Sep 17 00:00:00 2001 From: fahmi-ramadhan Date: Sat, 23 Sep 2023 21:07:36 +0700 Subject: [PATCH 2/7] Revert "Update Tugas 3" This reverts commit a608aa48f11ac5f82aaad8fdae599fbdc15f525c. --- main/migrations/0004_item_user.py | 22 ------------- main/models.py | 2 -- main/templates/login.html | 45 -------------------------- main/templates/main.html | 8 ----- main/templates/register.html | 34 -------------------- main/urls.py | 5 +-- main/views.py | 53 ++++--------------------------- 7 files changed, 7 insertions(+), 162 deletions(-) delete mode 100644 main/migrations/0004_item_user.py delete mode 100644 main/templates/login.html delete mode 100644 main/templates/register.html diff --git a/main/migrations/0004_item_user.py b/main/migrations/0004_item_user.py deleted file mode 100644 index 8443e84..0000000 --- a/main/migrations/0004_item_user.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-23 03:56 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('main', '0003_rename_book_item'), - ] - - operations = [ - migrations.AddField( - model_name='item', - name='user', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - preserve_default=False, - ), - ] diff --git a/main/models.py b/main/models.py index 4c7df94..a0e8da2 100644 --- a/main/models.py +++ b/main/models.py @@ -1,8 +1,6 @@ from django.db import models -from django.contrib.auth.models import User class Item(models.Model): - user = models.ForeignKey(User, on_delete=models.CASCADE) name = models.CharField(max_length=255) author = models.CharField(max_length=255) category = models.CharField(max_length=255) diff --git a/main/templates/login.html b/main/templates/login.html deleted file mode 100644 index ce64cd6..0000000 --- a/main/templates/login.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends 'base.html' %} - -{% block meta %} - Login -{% endblock meta %} - -{% block content %} - - - -{% endblock content %} \ No newline at end of file diff --git a/main/templates/main.html b/main/templates/main.html index c11db01..7801015 100644 --- a/main/templates/main.html +++ b/main/templates/main.html @@ -27,8 +27,6 @@

There are currently {{total_books}} books with {{books|length}} book titles {% endfor %} -

Sesi terakhir login: {{ last_login }}
-
@@ -37,11 +35,5 @@
Sesi terakhir login: {{ last_login }}
- - - - {% endblock content %} diff --git a/main/templates/register.html b/main/templates/register.html deleted file mode 100644 index 9bed1d0..0000000 --- a/main/templates/register.html +++ /dev/null @@ -1,34 +0,0 @@ -{% extends 'base.html' %} - -{% block meta %} - Register -{% endblock meta %} - -{% block content %} - - - -{% endblock content %} \ No newline at end of file diff --git a/main/urls.py b/main/urls.py index 8c31ae3..4e166a2 100644 --- a/main/urls.py +++ b/main/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id, register, login_user, logout_user +from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id app_name = 'main' @@ -10,7 +10,4 @@ path('json/', show_json, name='show_json'), path('xml//', show_xml_by_id, name='show_xml_by_id'), path('json//', show_json_by_id, name='show_json_by_id'), - path('register/', register, name='register'), - path('login/', login_user, name='login'), - path('logout/', logout_user, name='logout'), ] \ No newline at end of file diff --git a/main/views.py b/main/views.py index f8f2317..85c6015 100644 --- a/main/views.py +++ b/main/views.py @@ -1,29 +1,23 @@ -from django.shortcuts import render, redirect -from django.http import HttpResponseRedirect, HttpResponse +from django.shortcuts import render +from django.http import HttpResponseRedirect from main.forms import ItemForm from django.urls import reverse from main.models import Item +from django.http import HttpResponse from django.core import serializers -from django.contrib.auth.forms import UserCreationForm -from django.contrib import messages -from django.contrib.auth import authenticate, login, logout -from django.contrib.auth.decorators import login_required -import datetime -@login_required(login_url='/login') def show_main(request): - books = Item.objects.filter(user=request.user) + books = Item.objects.all() total_books = 0 for book in books: total_books += book.amount context = { 'app_name': 'Library Management System', - 'student_name': request.user.username, + 'student_name': 'Fahmi Ramadhan', 'class': 'PBP A', 'books': books, 'total_books': total_books, - 'last_login': request.COOKIES['last_login'], } return render(request, "main.html", context) @@ -32,47 +26,12 @@ def add_book(request): form = ItemForm(request.POST or None) if form.is_valid() and request.method == "POST": - item = form.save(commit=False) - item.user = request.user - item.save() + form.save() return HttpResponseRedirect(reverse('main:show_main')) context = {'form': form} return render(request, "add_book.html", context) -def register(request): - form = UserCreationForm() - - if request.method == "POST": - form = UserCreationForm(request.POST) - if form.is_valid(): - form.save() - messages.success(request, 'Your account has been successfully created!') - return redirect('main:login') - context = {'form':form} - return render(request, 'register.html', context) - -def login_user(request): - if request.method == 'POST': - username = request.POST.get('username') - password = request.POST.get('password') - user = authenticate(request, username=username, password=password) - if user is not None: - login(request, user) - response = HttpResponseRedirect(reverse("main:show_main")) - response.set_cookie('last_login', str(datetime.datetime.now())) - return response - else: - messages.info(request, 'Sorry, incorrect username or password. Please try again.') - context = {} - return render(request, 'login.html', context) - -def logout_user(request): - logout(request) - response = HttpResponseRedirect(reverse('main:login')) - response.delete_cookie('last_login') - return response - def show_xml(request): data = Item.objects.all() return HttpResponse(serializers.serialize('xml', data), content_type="application/xml") From 6cde3be558c376dc3416d5056332f65966b6f6da Mon Sep 17 00:00:00 2001 From: fahmi-ramadhan Date: Sat, 23 Sep 2023 21:18:32 +0700 Subject: [PATCH 3/7] Revert "Revert "Update Tugas 3"" This reverts commit 423c02b90f2f28d3e3ceeb15b7772f11a02b7cf8. --- main/migrations/0004_item_user.py | 22 +++++++++++++ main/models.py | 2 ++ main/templates/login.html | 45 ++++++++++++++++++++++++++ main/templates/main.html | 8 +++++ main/templates/register.html | 34 ++++++++++++++++++++ main/urls.py | 5 ++- main/views.py | 53 +++++++++++++++++++++++++++---- 7 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 main/migrations/0004_item_user.py create mode 100644 main/templates/login.html create mode 100644 main/templates/register.html diff --git a/main/migrations/0004_item_user.py b/main/migrations/0004_item_user.py new file mode 100644 index 0000000..8443e84 --- /dev/null +++ b/main/migrations/0004_item_user.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.5 on 2023-09-23 03:56 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('main', '0003_rename_book_item'), + ] + + operations = [ + migrations.AddField( + model_name='item', + name='user', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/main/models.py b/main/models.py index a0e8da2..4c7df94 100644 --- a/main/models.py +++ b/main/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.contrib.auth.models import User class Item(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) name = models.CharField(max_length=255) author = models.CharField(max_length=255) category = models.CharField(max_length=255) diff --git a/main/templates/login.html b/main/templates/login.html new file mode 100644 index 0000000..ce64cd6 --- /dev/null +++ b/main/templates/login.html @@ -0,0 +1,45 @@ +{% extends 'base.html' %} + +{% block meta %} + Login +{% endblock meta %} + +{% block content %} + + + +{% endblock content %} \ No newline at end of file diff --git a/main/templates/main.html b/main/templates/main.html index 7801015..c11db01 100644 --- a/main/templates/main.html +++ b/main/templates/main.html @@ -27,6 +27,8 @@

There are currently {{total_books}} books with {{books|length}} book titles {% endfor %} +

Sesi terakhir login: {{ last_login }}
+
@@ -35,5 +37,11 @@

There are currently {{total_books}} books with {{books|length}} book titles + + + + {% endblock content %} diff --git a/main/templates/register.html b/main/templates/register.html new file mode 100644 index 0000000..9bed1d0 --- /dev/null +++ b/main/templates/register.html @@ -0,0 +1,34 @@ +{% extends 'base.html' %} + +{% block meta %} + Register +{% endblock meta %} + +{% block content %} + + + +{% endblock content %} \ No newline at end of file diff --git a/main/urls.py b/main/urls.py index 4e166a2..8c31ae3 100644 --- a/main/urls.py +++ b/main/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id +from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id, register, login_user, logout_user app_name = 'main' @@ -10,4 +10,7 @@ path('json/', show_json, name='show_json'), path('xml//', show_xml_by_id, name='show_xml_by_id'), path('json//', show_json_by_id, name='show_json_by_id'), + path('register/', register, name='register'), + path('login/', login_user, name='login'), + path('logout/', logout_user, name='logout'), ] \ No newline at end of file diff --git a/main/views.py b/main/views.py index 85c6015..f8f2317 100644 --- a/main/views.py +++ b/main/views.py @@ -1,23 +1,29 @@ -from django.shortcuts import render -from django.http import HttpResponseRedirect +from django.shortcuts import render, redirect +from django.http import HttpResponseRedirect, HttpResponse from main.forms import ItemForm from django.urls import reverse from main.models import Item -from django.http import HttpResponse from django.core import serializers +from django.contrib.auth.forms import UserCreationForm +from django.contrib import messages +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required +import datetime +@login_required(login_url='/login') def show_main(request): - books = Item.objects.all() + books = Item.objects.filter(user=request.user) total_books = 0 for book in books: total_books += book.amount context = { 'app_name': 'Library Management System', - 'student_name': 'Fahmi Ramadhan', + 'student_name': request.user.username, 'class': 'PBP A', 'books': books, 'total_books': total_books, + 'last_login': request.COOKIES['last_login'], } return render(request, "main.html", context) @@ -26,12 +32,47 @@ def add_book(request): form = ItemForm(request.POST or None) if form.is_valid() and request.method == "POST": - form.save() + item = form.save(commit=False) + item.user = request.user + item.save() return HttpResponseRedirect(reverse('main:show_main')) context = {'form': form} return render(request, "add_book.html", context) +def register(request): + form = UserCreationForm() + + if request.method == "POST": + form = UserCreationForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Your account has been successfully created!') + return redirect('main:login') + context = {'form':form} + return render(request, 'register.html', context) + +def login_user(request): + if request.method == 'POST': + username = request.POST.get('username') + password = request.POST.get('password') + user = authenticate(request, username=username, password=password) + if user is not None: + login(request, user) + response = HttpResponseRedirect(reverse("main:show_main")) + response.set_cookie('last_login', str(datetime.datetime.now())) + return response + else: + messages.info(request, 'Sorry, incorrect username or password. Please try again.') + context = {} + return render(request, 'login.html', context) + +def logout_user(request): + logout(request) + response = HttpResponseRedirect(reverse('main:login')) + response.delete_cookie('last_login') + return response + def show_xml(request): data = Item.objects.all() return HttpResponse(serializers.serialize('xml', data), content_type="application/xml") From 0eaf9b5c7f33a57f2f99925aafe8ea0739ce9f1b Mon Sep 17 00:00:00 2001 From: fahmi-ramadhan Date: Wed, 27 Sep 2023 00:46:12 +0700 Subject: [PATCH 4/7] Finish Tugas 4 --- README.md | 323 ++++++++++++++++++++++++++++++++++++++- main/templates/main.html | 21 ++- main/urls.py | 7 +- main/views.py | 22 ++- 4 files changed, 369 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e95ec27..fea69e9 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,331 @@ Nama: Fahmi Ramadhan
NPM: 2206026473
Kelas: PBP A
+# Tugas 4: Implementasi Autentikasi, Session, dan Cookies pada Django + +
+ +## Apa itu Django `UserCreationForm` dan apa kelebihan dan kekurangannya? + +`UserCreationForm` adalah salah satu form yang disediakan oleh _framework_ Django untuk mempermudah proses pembuatan _user_ dalam aplikasi web. Dengan adanya hal tersebut, kita tidak perlu susah-susah menulis kode dari awal untuk membuat _form_ dan validasi inputnya sehingga kita dapat menghemat waktu. Namun, `UserCreationForm` juga memiliki kekurangan karena bentuknya yang sederhana. Jika kita ingin menambahkan fitur yang kompleks, misalnya konfirmasi email atau CAPTCHA, kita harus menulis kode sendiri. + +## Apa perbedaan autentikasi dan otorisasi dalam konteks Django dan mengapa keduanya penting? + +Autentikasi adalah proses untuk memverifikasi identitas pengguna yang mengakses suatu aplikasi, misalnya proses login dalam aplikasi web. Pada Django, terdapat method `authenticate` yang digunakan untuk mengautentikasi _username_ dengan _password_-nya. Sementara itu, otorisasi adalah proses untuk memverifikasi apakah pengguna yang sudah terautentikasi memiliki akses pada suatu fitur dalam aplikasi web tersebut. Misalnya, pada scele, pengguna terautentikasi yang memiliki role dosen memiliki hak akses yang berbeda dengan pengguna terautentikasi yang memiliki role mahasiswa. + +Autentikasi dan otorisasi merupakan hal penting untuk diimplementasikan pada suatu aplikasi web. Dengan menggabungkan keduanya, kita bisa membuat aplikasi web yang aman dengan kontrol yang tepat atas siapa saja yang dapat melakukan hal tertentu. + +## Apa itu _cookies_ dalam konteks aplikasi web dan bagaimana Django menggunakan cookies untuk mengelola data sesi pengguna? + +_Cookies_ adalah sebuah potongan kecil dari data yang disimpan di sisi klien, yaitu _web browser_ dari pengguna, agar data tersebut dapat digunakan kembali dalam _request_ selanjutnya. _Cookies_ biasa digunakan dalam menyimpan token autentikasi, melacak aktivitas pengguna, dan menyimpan preferensi pengguna. _Cookies_ akan dihapus secara otomatis jika sudah mencapai waktu kedaluwarsanya. + +Django memiliki dukungan bawaan untuk mengelola data sesi pengguna. Django menyediakan API untuk membaca nilai _cookies_ dari HTTP _request_ yang diterima _browser_ pengguna dan kita bisa mengakses _value_ dari _dictionary_ `request.COOKIES`. Untuk mengatur atau membuat _cookies_ baru, kita bisa menggunakan `response.set_cookie()`. Kemudian, untuk penghapusan _cookies_ bisa menggunakan `response.delete_cookie()` + +## Apakah penggunaan cookies aman secara _default_ dalam pengembangan web atau apakah ada risiko potensial yang harus diwaspadai? + +_Cookies_ bersifat aman karena ia hanya menyimpan data, bukan kode program, tidak dapat membaca atau menghapus data pada komputer pengguna. Namun, jika _cookies_ tidak diatur dengan baik, misalnya terdapat informasi personal di dalamnya, ada risiko data tersebut dicuri oleh suatu _script_, bukan _cookies_-nya yang mencuri. + +## Implementasi _Checklist_ + +### 1. Mengimplementasikan fungsi registrasi, login, dan logout untuk memungkinkan pengguna untuk mengakses aplikasi sebelumnya dengan lancar. + +Pertama-tama, saya membuka file `views.py` pada subdirektori `main` lalu mengimport _method-method_ yang dibutuhkan dan menambahkan fungsi `register`, `login`, dan `logout` berikut: + +```python +def register(request): + form = UserCreationForm() + + if request.method == "POST": + form = UserCreationForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, 'Your account has been successfully created!') + return redirect('main:login') + context = {'form':form} + return render(request, 'register.html', context) + +def login_user(request): + if request.method == 'POST': + username = request.POST.get('username') + password = request.POST.get('password') + user = authenticate(request, username=username, password=password) + if user is not None: + login(request, user) + return redirect('main:show_main') + else: + messages.info(request, 'Sorry, incorrect username or password. Please try again.') + context = {} + return render(request, 'login.html', context) + +def logout_user(request): + logout(request) + return redirect('main:login') +``` + +Setelah itu, pada direktori `main/templates`, saya membuat berkas `register.html` dan `login.html` yang meng-_extends_ `base.html`. + +1. `register.html` +```html +{% extends 'base.html' %} + +{% block meta %} + Register +{% endblock meta %} + +{% block content %} + + + +{% endblock content %} +``` + +2. `login.html` +```html +{% extends 'base.html' %} + +{% block meta %} + Login +{% endblock meta %} + +{% block content %} + + + +{% endblock content %} +``` + +Selanjutnya, saya menambahkan _path_ url ke dalam `urlpatterns` pada `urls.py` di subdirektori `main` untuk mengakses fungsi `register`, `login`, dan `logout` tadi. + +```python +urlpatterns = [ + ... + path('register/', register, name='register'), + path('login/', login_user, name='login'), + path('logout/', logout_user, name='logout'), +] +``` + +Kemudian, saya melakukan restriksi akses halaman main agar hanya bisa diakses oleh pengguna yang sudah login (terautentikasi) dengan menambahkan kode `@login_required(login_url='/login')` di atas fungsi `show_main` + +### 2. Membuat dua akun pengguna dengan masing-masing tiga _dummy_ data menggunakan model yang telah dibuat pada aplikasi sebelumnya untuk setiap akun di lokal. + +Saat ini, kedua akun tersebut akan terhubung ke data yang sama. Oleh karena itu, selanjutnya saya akan menghubungkan model `Item` dengan `User` agar masing-masing _user_ hanya melihat item-item yang telah ia buat sendiri. + +### 3. Menghubungkan model `Item` dengan `User`. + +Untuk menghubungkan model `Item` dengan `User`, saya menambahkan _field_ baru bernama `user` pada model. + +```python +from django.contrib.auth.models import User + +class Item(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + ... +``` + +Selanjutnya, saya mengubah fungsi `show_main` dan `add_book` pada `views.py` menjadi sebagai berikut: + +```python +... +def show_main(request): + books = Item.objects.filter(user=request.user) + ... + context = { + ... + 'name': request.user.username, + ... + } + ... + +def add_book(request): + form = ItemForm(request.POST or None) + + if form.is_valid() and request.method == "POST": + item = form.save(commit=False) + item.user = request.user + item.save() + return HttpResponseRedirect(reverse('main:show_main')) + ... +``` + +Karena terdapat perubahan pada _models_, saya perlu melakukan migrasi dengan menjalankan `python manage.py makemigrations` lalu `python manage.py migrate`. + +### 4. Menampilkan detail informasi pengguna yang sedang _logged in_ seperti _username_ dan menerapkan `cookies` seperti `last login` pada halaman utama aplikasi. + +Pada tahap ini, saya menerapkan _cookies_ yang bernama `last_login` untuk melihat kapan terakhir kali suatu _user_ melakukan _login_. Untuk itu, saya mengubah fungsi `login_user` agar men-_set_ _cookies_ dengan _key_ `last_login` dengan _value_ waktu sekarang. Selain itu, saya juga mengubah fungsi `logout_user` agar menghapus _cookies_ dengan _key_ `last_login` saat _user_ melakukan _logout_. + +```python +def login_user(request): + if request.method == 'POST': + username = request.POST.get('username') + password = request.POST.get('password') + user = authenticate(request, username=username, password=password) + if user is not None: + login(request, user) + response = HttpResponseRedirect(reverse("main:show_main")) + response.set_cookie('last_login', str(datetime.datetime.now())) + return response + else: + messages.info(request, 'Sorry, incorrect username or password. Please try again.') + context = {} + return render(request, 'login.html', context) + +def logout_user(request): + logout(request) + response = HttpResponseRedirect(reverse('main:login')) + response.delete_cookie('last_login') + return response +``` + +Kemudian, untuk menampilkan data _last login_ ke halaman _main_, saya menambahkan sebuah data pada _dictionary_ `context` di fungsi `show_main` dan menambahkan sebaris kode pada `main.html` + +```python +context = { + ... + 'last_login': request.COOKIES['last_login'], +} +``` + +```html +... +
Sesi terakhir login: {{ last_login }}
+... +``` + +## BONUS + +Saya menambahkan fungsi `add_book_amount`, `dec_book_amount`, dan `remove_book` pada `views.py`. + +```python +def remove_book(request, book_id): + if request.method == 'POST' and 'Remove' in request.POST: + book = Item.objects.get(id=book_id) + book.delete() + return HttpResponseRedirect(reverse('main:show_main')) + +def add_book_amount(request, book_id): + if request.method == 'POST' and 'Increment' in request.POST: + book = Item.objects.get(id=book_id) + book.amount += 1 + book.save() + return HttpResponseRedirect(reverse('main:show_main')) + +def dec_book_amount(request, book_id): + if request.method == 'POST' and 'Decrement' in request.POST: + book = Item.objects.get(id=book_id) + book.amount -= 1 + book.save() + return HttpResponseRedirect(reverse('main:show_main')) +``` + +Selanjutnya, saya menambahkan _path_ url ke dalam `urlpatterns` pada `urls.py` di subdirektori `main` untuk mengakses fungsi-fungsi tersebut. + +```python +urlpatterns = [ + ... + path('add_book_amount//', add_book_amount, name='add_book_amount'), + path('dec_book_amount//', dec_book_amount, name='dec_book_amount'), + path('remove_book//', remove_book, name='remove_book'), +] +``` + +Kemudian, saya menambahkan tombol pada tabel di `main.html` untuk melakukan fungsi-fungsi di atas. + +```html + + + ... + + + + {% for book in books %} + + ... + + + + + {% endfor %} +
Actions
+
+ {% csrf_token %} + +
+
+
+ {% csrf_token %} + +
+
+
+ {% csrf_token %} + +
+
+``` + +
# Tugas 3: Implementasi _Form_ dan _Data Delivery_ pada Django -
+
## Perbedaan _form_ `POST` dan _form_ `GET` dalam Django diff --git a/main/templates/main.html b/main/templates/main.html index c11db01..1c26573 100644 --- a/main/templates/main.html +++ b/main/templates/main.html @@ -3,7 +3,7 @@ {% block content %}

{{app_name}}

-

{{student_name}} - {{class}}

+

{{name}} - {{class}}

There are currently {{total_books}} books with {{books|length}} book titles stored in the system

@@ -14,6 +14,7 @@

There are currently {{total_books}} books with {{books|length}} book titles Category Amount Description + Actions {% for book in books %} @@ -23,6 +24,24 @@

There are currently {{total_books}} books with {{books|length}} book titles {{book.category}} {{book.amount}} {{book.description}} + +
+ {% csrf_token %} + +
+ + +
+ {% csrf_token %} + +
+ + +
+ {% csrf_token %} + +
+ {% endfor %} diff --git a/main/urls.py b/main/urls.py index 8c31ae3..42dc4da 100644 --- a/main/urls.py +++ b/main/urls.py @@ -1,5 +1,7 @@ from django.urls import path -from main.views import show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id, register, login_user, logout_user +from main.views import (show_main, add_book, show_xml, show_json, show_xml_by_id, show_json_by_id, + register, login_user, logout_user, add_book_amount, dec_book_amount, + remove_book) app_name = 'main' @@ -13,4 +15,7 @@ path('register/', register, name='register'), path('login/', login_user, name='login'), path('logout/', logout_user, name='logout'), + path('add_book_amount//', add_book_amount, name='add_book_amount'), + path('dec_book_amount//', dec_book_amount, name='dec_book_amount'), + path('remove_book//', remove_book, name='remove_book'), ] \ No newline at end of file diff --git a/main/views.py b/main/views.py index f8f2317..63e3aa4 100644 --- a/main/views.py +++ b/main/views.py @@ -19,7 +19,7 @@ def show_main(request): context = { 'app_name': 'Library Management System', - 'student_name': request.user.username, + 'name': request.user.username, 'class': 'PBP A', 'books': books, 'total_books': total_books, @@ -40,6 +40,12 @@ def add_book(request): context = {'form': form} return render(request, "add_book.html", context) +def remove_book(request, book_id): + if request.method == 'POST' and 'Remove' in request.POST: + book = Item.objects.get(id=book_id) + book.delete() + return HttpResponseRedirect(reverse('main:show_main')) + def register(request): form = UserCreationForm() @@ -73,6 +79,20 @@ def logout_user(request): response.delete_cookie('last_login') return response +def add_book_amount(request, book_id): + if request.method == 'POST' and 'Increment' in request.POST: + book = Item.objects.get(id=book_id) + book.amount += 1 + book.save() + return HttpResponseRedirect(reverse('main:show_main')) + +def dec_book_amount(request, book_id): + if request.method == 'POST' and 'Decrement' in request.POST: + book = Item.objects.get(id=book_id) + book.amount -= 1 + book.save() + return HttpResponseRedirect(reverse('main:show_main')) + def show_xml(request): data = Item.objects.all() return HttpResponse(serializers.serialize('xml', data), content_type="application/xml") From 43fe79e941c12a1970b4e8d09721006b3da4d3ba Mon Sep 17 00:00:00 2001 From: fahmi-ramadhan Date: Wed, 4 Oct 2023 01:45:39 +0700 Subject: [PATCH 5/7] Finish Tugas 5 --- README.md | 224 ++++++++++++++++++++++++++++++++++- main/templates/add_book.html | 33 ++++-- main/templates/login.html | 71 +++++------ main/templates/main.html | 126 ++++++++++++-------- main/templates/register.html | 53 +++++---- templates/base.html | 10 +- 6 files changed, 389 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index fea69e9..7b6fe9a 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,232 @@ Nama: Fahmi Ramadhan
NPM: 2206026473
Kelas: PBP A
-# Tugas 4: Implementasi Autentikasi, Session, dan Cookies pada Django +# Tugas 5: Desain Web Menggunakan HTML, CSS dan FrameWork CSS
+## Manfaat dari setiap _element selector_ dan kapan waktu yang tepat untuk menggunakannya. + +- _Universal Selector_ digunakan untuk memilih semua elemen HTML pada halaman. _selector_ ini cocok digunakan ketika ingin _reset style_ atau ketika ingin memberikan _style_ umum pada semua elemen dalam halaman, misalnya mengatur jenis _font_ (`* {}`). + +- _Element Selector_ digunakan untuk memilih semua elemen HTML dengan nama elemen tertentu (misalnya, semua `

`). _Selector_ ini cocok digunakan ketika ingin memberikan _style_ umum pada semua elemen dengan jenis tertentu. (`p {}`). + +- _Class Selector_ digunakan untuk memilih elemen berdasarkan atribut `class` yang diberikan. Dikarenakan beberapa elemen bisa memiliki `class` yang sama dengan elemen lainnya, kita bisa mengelompokkan elemen-elemen tersebut dalam kelas-kelas tertentu. Oleh karena itu, _selector_ ini cocok digunakan ketika ingin memberikan _style_ pada kelompok elemen dengan class tertentu yang sama (`.nama-class {}`). + +- _ID Selector_ digunakan untuk memilih elemen berdasarkan atribut `id` yang diberikan. Berbeda dengan `class`, atribut `id` bersifat unik untuk setiap elemen dalam satu file `html`. Oleh karena itu, _selector_ ini cocok digunakan ketika ingin memberikan _style_ atau manipulasi spesifik pada elemen tertentu dengan ID yang unik (`.value-id {}`). + +- _Descendant Selector_ digunakan untuk memilih elemen dalam hierarki tertentu. _Selector_ ini cocok digunakan ketika ingin memberikan _style_ pada semua elemen tertentu yang berada di dalam elemen tertentu, misalnya semua elemen `

  • ` dalam elemen `
      ` (`ul li {}`). + +- _Adjacent Selector_ digunakan untuk memilih suatu elemen yang bersebelahan dalam tingkat hierarki yang sama. _Selector_ ini cocok digunakan ketika ingin memberikan _style_ suatu elemen yang tepat didahului oleh suatu elemen tertentu, misalnya elemen `

      ` yang tepat setelah elemen `

      ` (`h1 + p {}`). + +- _Direct-Descendant Selector_ digunakan untuk memilih suatu elemen yang berada dalam elemen tertentu dan hanya berbeda satu tingkat hierarki. _Selector_ ini cocok digunakan ketika ingin memberikan _style_ misalnya pada elemen `
    • ` yang berada satu tingkat di dalam elemen `
        ` (`ul > li {}`). + +- _Attribute Selector_ digunakan untuk memilih semua elemen yang atributnya diset memiliki nilai yang sama. Selector_ ini cocok digunakan ketika ingin memberikan _style_ misalnya pada elemen `input` yang atribut `type`-nya diset ke `"text"` (`input[type="text"] {}`). + +## Penjelasan HTML5 Tag + +- `