diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c69d96d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +**/*.pyc +**/*.pyo +**/*.mo +**/*.db +**/*.css.map +**/*.egg-info +**/*.sql.gz +**/__pycache__/ +.cache +.project +.idea +.pydevproject +.idea/workspace.xml +.DS_Store +.git/ +.sass-cache +.vagrant/ +dist +docs +env +logs +src/{{ project_name }}/settings/local.py +src/node_modules +web/media +web/static/CACHE +stats +Dockerfile +.gitignore +Dockerfile +db.sqlite3 +**/*.md +logs/ \ No newline at end of file diff --git a/.github/workflows/pbp-deploy.yml b/.github/workflows/pbp-deploy.yml new file mode 100644 index 0000000..55d9dfd --- /dev/null +++ b/.github/workflows/pbp-deploy.yml @@ -0,0 +1,24 @@ +name: Deploy + +on: + push: + branches: + - main + - master + +jobs: + Deployment: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Cloning repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Push to Dokku server + uses: dokku/github-action@master + with: + branch: 'main' + git_remote_url: ssh://dokku@${{ secrets.DOKKU_SERVER_IP }}/${{ secrets.DOKKU_APP_NAME }} + ssh_private_key: ${{ secrets.DOKKU_SSH_PRIVATE_KEY }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..82ba771 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM python:3.10-slim-buster + +WORKDIR /app + +ENV PYTHONUNBUFFERED=1 \ + PYTHONPATH=/app \ + DJANGO_SETTINGS_MODULE=shopping_list.settings \ + PORT=8000 \ + WEB_CONCURRENCY=2 + +# Install system packages required Django. +RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \ +&& rm -rf /var/lib/apt/lists/* + +RUN addgroup --system django \ + && adduser --system --ingroup django django + +# Requirements are installed here to ensure they will be cached. +COPY ./requirements.txt /requirements.txt +RUN pip install -r /requirements.txt + +# Copy project code +COPY . . + +RUN python manage.py collectstatic --noinput --clear + +# Run as non-root user +RUN chown -R django:django /app +USER django + +# Run application +# CMD gunicorn shopping_list.wsgi:application \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..f621aa2 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +release: django-admin migrate --noinput +web: gunicorn adventurers_inventory.wsgi \ No newline at end of file diff --git a/README.md b/README.md index 25d0b84..97afad0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ PBP E` [Link to Adventurer's Inventory](https://adventurers-inventory.adaptable.app/main) ## Tugas 2 ->1. Jelaskan bagaimana cara kamu mengimplementasikan checklist di atas secara step-by-step (bukan hanya sekadar mengikuti tutorial). +
+1. Jelaskan bagaimana cara kamu mengimplementasikan checklist di atas secara step-by-step (bukan hanya sekadar mengikuti tutorial). - [x] Membuat sebuah proyek Django baru. @@ -113,14 +114,18 @@ Pada PBP sekarang, kepentingan *deployment* bertujuan untuk menampilkan secara l Terakhir, aplikasi yang saya buat memiliki *domain* bernama `https://adventurers-inventory.adaptable.app/main`. +
->2. Buatlah bagan yang berisi request client ke web aplikasi berbasis Django beserta responnya dan jelaskan pada bagan tersebut kaitan antara urls.py, views.py, models.py, dan berkas html. +
+2. Buatlah bagan yang berisi request client ke web aplikasi berbasis Django beserta responnya dan jelaskan pada bagan tersebut kaitan antara urls.py, views.py, models.py, dan berkas html. ![bagan](https://github.com/fathonidf/adventurers-inventory/assets/105644250/9cb5536b-83d7-45ea-ae2b-a8abde7cde9e) Saat pengguna mengirimkan permintaan HTTP aplikasi main melalui web browser, urls.py melakukan pemetaan URL untuk meneruskan permintaan HTTP ke views.py sesuai dengan URL yang diminta. Kemudian, view menghasilkan response HTTP berupa halaman HTML. Dalam proses ini, views.py mengambil data yang diperlukan melalui models.py dan menampilkan data tersebut menggunakan template main.html. +
->3. Jelaskan mengapa kita menggunakan virtual environment? Apakah kita tetap dapat membuat aplikasi web berbasis Django tanpa menggunakan virtual environment? +
+3. Jelaskan mengapa kita menggunakan virtual environment? Apakah kita tetap dapat membuat aplikasi web berbasis Django tanpa menggunakan virtual environment? Virtual environment digunakan untuk mengisolasi *dependencies* dan modul Python yang dipakai untuk kebutuhan proyek Anda masing-masing sehingga tidak akan bertabrakan dan terpengaruh oleh modul atau konfigurasi proyek yang lain. Hal ini akan menghindari instalasi paket atau modul secara global karena semisal paket atau modul tersebut hanya untuk proyek tertentu. @@ -129,8 +134,10 @@ Semisal Proyek A menggunakan Django 4.0 dan Proyek B menggunakan Django 4.1, den Virtual environment dibuat dengan perintah `python -m venv env`, dan diaktifkan dengan perintah `env\Scripts\activate.bat`. Membuat aplikasi tanpa *virtual environment* tetap dapat dijalankan namun lebih dianjurkan mengimplementasikan *virtual environment* karena hal ini dapat memudahkan untuk pengelolaan konsistensi dari masing-masing *dependencies* proyek sehingga menjadikannya sebuah *good practice* +
->4. Jelaskan apakah itu MVC, MVT, MVVM dan perbedaan dari ketiganya. +
+4. Jelaskan apakah itu MVC, MVT, MVVM dan perbedaan dari ketiganya. | MVC | MVT | MVVM | | --- | ---- | --- | @@ -139,4 +146,34 @@ Membuat aplikasi tanpa *virtual environment* tetap dapat dijalankan namun lebih | View: Bertanggung jawab sebagai pengelola antarmuka pengguna dan menampilkan data yang diberikan model lalu mengirim input ke Controller | View: Visualisasi dan menampilkan data ke pengguna tetapi dalam Framework Python Django| View: Menginformasi ke ViewModel terkait interaksi pengguna, dan hanya menampilkan data yang disediakan oleh ViewModel | | Controller: Menjembatani hubungan antara View dan Model dan sebagai inti logika dan alur aplikasi dengan menginformasi interaksi user ke Model | Template: Mengambil data dari model dan menampilkannya, berupa HTML | ViewModel: Perantara antara Model dan View, mengubah data dari Model menjadi format sesuai dengan tampilan | |![mvc](https://media.geeksforgeeks.org/wp-content/uploads/20201002214740/MVCSchema.png) |![mvp](https://media.geeksforgeeks.org/wp-content/uploads/20201024233154/MVPSchema.png) |![mvvm](https://media.geeksforgeeks.org/wp-content/uploads/20201002215007/MVVMSchema.png) | -|MVC adalah pola yang umum digunakan dalam pengembangan aplikasi berbasis desktop dan web tradisional. Ini memisahkan tiga komponen utama aplikasi untuk meningkatkan pemeliharaan dan pengembangan kode. |MVT adalah pola yang spesifik untuk kerangka kerja Django, yang dirancang khusus untuk pengembangan aplikasi web dengan Python. Ini menggantikan View dalam MVC dengan Template, yang memungkinkan pemisahan yang lebih jelas antara tampilan dan pemrosesan HTTP. |MVVM adalah pola desain yang sering digunakan dalam pengembangan aplikasi berbasis antarmuka pengguna (UI), terutama pada platform seperti WPF (Windows Presentation Foundation). Ini fokus pada pemisahan antara tampilan dan logika bisnis, dengan menggunakan ViewModel sebagai perantara. | \ No newline at end of file +|MVC adalah pola yang umum digunakan dalam pengembangan aplikasi berbasis desktop dan web tradisional. Ini memisahkan tiga komponen utama aplikasi untuk meningkatkan pemeliharaan dan pengembangan kode. |MVT adalah pola yang spesifik untuk kerangka kerja Django, yang dirancang khusus untuk pengembangan aplikasi web dengan Python. Ini menggantikan View dalam MVC dengan Template, yang memungkinkan pemisahan yang lebih jelas antara tampilan dan pemrosesan HTTP. |MVVM adalah pola desain yang sering digunakan dalam pengembangan aplikasi berbasis antarmuka pengguna (UI), terutama pada platform seperti WPF (Windows Presentation Foundation). Ini fokus pada pemisahan antara tampilan dan logika bisnis, dengan menggunakan ViewModel sebagai perantara. | + +
+ +--- +## Tugas 3 + +
+1. Apa perbedaan antara form POST dan form GET dalam Django? + +
+ +
+2. Apa perbedaan utama antara XML, JSON, dan HTML dalam konteks pengiriman data? + +
+ +
+3. Mengapa JSON sering digunakan dalam pertukaran data antara aplikasi web modern? + +
+ +
+4. Jelaskan bagaimana cara kamu mengimplementasikan checklist di atas secara step-by-step (bukan hanya sekadar mengikuti tutorial). + +
+ +
+Screenshot Postman + +
\ No newline at end of file diff --git a/adventurers_inventory/settings.py b/adventurers_inventory/settings.py index c5036ce..10e051e 100644 --- a/adventurers_inventory/settings.py +++ b/adventurers_inventory/settings.py @@ -11,10 +11,13 @@ """ from pathlib import Path +import environ +import os # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent +env = environ.Env() # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ @@ -22,6 +25,10 @@ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-^_h^l&&h_)%-e(+^^bj%8#pk$4wpm0#*wb!0)=a^z260m0f8+3' +# Automatically determine environment by detecting if DATABASE_URL variable. +# DATABASE_URL is provided by Heroku if a database add-on is added (e.g. Heroku Postgres). +PRODUCTION = env.bool('PRODUCTION', False) + # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -55,7 +62,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -81,6 +88,13 @@ } } +# Set database settings automatically using DATABASE_URL. +if PRODUCTION: + DATABASES = { + 'default': env.db('DATABASE_URL') + } + DATABASES["default"]["ATOMIC_REQUESTS"] = True + # Password validation # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators @@ -118,6 +132,8 @@ STATIC_URL = 'static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static') + # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field diff --git a/adventurers_inventory/urls.py b/adventurers_inventory/urls.py index 7595238..0471e26 100644 --- a/adventurers_inventory/urls.py +++ b/adventurers_inventory/urls.py @@ -19,5 +19,5 @@ urlpatterns = [ path('admin/', admin.site.urls), - path('main/', include('main.urls')) + path('', include('main.urls')) ] diff --git a/main/forms.py b/main/forms.py new file mode 100644 index 0000000..6445135 --- /dev/null +++ b/main/forms.py @@ -0,0 +1,7 @@ +from django.forms import ModelForm +from main.models import Item + +class ItemForm(ModelForm): + class Meta: + model = Item + fields = ["name", "amount", "description", "price", "item_level", "use"] \ No newline at end of file diff --git a/main/templates/create_item.html b/main/templates/create_item.html new file mode 100644 index 0000000..a3a2882 --- /dev/null +++ b/main/templates/create_item.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} + +{% block content %} +

Add New Item

+ +
+ {% csrf_token %} + + {{ form.as_table }} + + + + +
+ +
+
+ +{% endblock %} \ No newline at end of file diff --git a/main/templates/main.html b/main/templates/main.html index 44c7fb5..6eaecdf 100644 --- a/main/templates/main.html +++ b/main/templates/main.html @@ -1,6 +1,44 @@ -

{{app_name}}

+{% extends 'base.html' %} -
Nama:
-

{{ name }}

-
Kelas:
-

{{ class }}

\ No newline at end of file +{% block content %} +

{{app_name}}

+ +
Name:
+

{{name}}

+ +
Class:
+

{{class}}

+ + + + + + + + + + + + {% comment %} Berikut cara memperlihatkan data produk di bawah baris ini {% endcomment %} + + {% for product in products %} + + + + + + + + + {% endfor %} +
NameAmountDescriptionPriceiLvlUse
{{product.name}}{{product.amount}}{{product.description}}{{product.price}}{{product.item_level}}{{product.use}}
+ +
+ + + + + +{% endblock content %} \ No newline at end of file diff --git a/main/urls.py b/main/urls.py index 5c1b341..2d925fb 100644 --- a/main/urls.py +++ b/main/urls.py @@ -1,8 +1,16 @@ from django.urls import path, include -from main.views import show_main +from main.views import show_main, create_item, show_xml, show_json, show_xml_by_id, show_json_by_id + + + app_name = 'main' urlpatterns = [ - path('', show_main, name='show_main') + path('', show_main, name='show_main'), + path('create-item', create_item, name='create_item'), + path('xml/', show_xml, name='show_xml'), + 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') ] \ No newline at end of file diff --git a/main/views.py b/main/views.py index 4b838f7..ac60797 100644 --- a/main/views.py +++ b/main/views.py @@ -1,11 +1,46 @@ 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 # Create your views here. def show_main(request): + item = Item.objects.all() + context = { 'app_name': 'Adventurer\'s Inventory', 'name': 'Daffa Mohamad Fathoni', - 'class': 'PBP E' + 'class': 'PBP E', + 'Item': item } - return render(request, "main.html", context) \ No newline at end of file + return render(request, "main.html", context) + +def create_item(request): + form = ItemForm(request.POST or None) + + if form.is_valid() and request.method == "POST": + form.save() + return HttpResponseRedirect(reverse('main:show_main')) + + context = {'form': form} + return render(request, "create_item.html", context) + +def show_xml(request): + data = Item.objects.all() + return HttpResponse(serializers.serialize("xml", data), content_type="application/xml") + +def show_json(request): + data = Item.objects.all() + return HttpResponse(serializers.serialize("json", data), content_type="application/json") + +def show_xml_by_id(request, id): + data = Item.objects.filter(pk=id) + return HttpResponse(serializers.serialize("xml", data), content_type="application/xml") + +def show_json_by_id(request, id): + data = Item.objects.filter(pk=id) + return HttpResponse(serializers.serialize("json", data), content_type="application/json") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index c458349..1d1bf52 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ gunicorn whitenoise psycopg2-binary requests -urllib3 \ No newline at end of file +urllib3 +django-environ \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..f6727a3 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,18 @@ +{% load static %} + + + + + + {% block meta %} + {% endblock meta %} + + + + {% block content %} + {% endblock content %} + + \ No newline at end of file