**DJANGO** ##### Apa Itu Django? Django adalah framework web berbasis Python yang membantu membuat website dengan cepat dan mudah. Framework ini menyediakan banyak alat dan fitur siap pakai, sehingga kita tidak perlu membuat semuanya dari awal. ##### Struktur Dasar Django 1. Project: Proyek utama yang berisi seluruh aplikasi dan pengaturan. 2. App: Bagian kecil dari proyek yang memiliki fungsi spesifik (contoh: aplikasi untuk user, blog, dll). 3. Model: Menentukan struktur database . 4. View: Logika yang mengatur apa yang akan ditampilkan di halaman web. 5. Template: File HTML yang menentukan tampilan halaman web. 6. URL: Menghubungkan alamat website dengan view yang sesuai. ##### Kelebihan Django 1. Cepat Dibangun: Banyak fitur siap pakai, jadi kita bisa fokus ke fitur unik website kita. 2. Aman: Punya sistem keamanan bawaan buat ngelindungi dari serangan umum kayak SQL injection. 3. Skalabel: Cocok buat proyek kecil sampai besar (contohnya, Instagram pake Django). 4. Komunitas Besar: Banyak dokumentasi dan dukungan dari komunitas. 5. Mudah Dipelajari: Kalau udah ngerti Python, belajar Django jadi lebih gampang. ##### Kenapa Pake Django? 1. Buat bikin website dengan cepat, aman, dan efisien tanpa perlu mulai dari nol. 2. Cocok buat pemula karena banyak alat bantu dan dokumentasi yang jelas. 3. Pas banget buat proyek yang butuh database dan backend yang kompleks. ## Section 1 : Welcome to Try Django 1.11 1. Install Django di Windows • Install python versi 3 di website resmi ![Screenshot 2025-03-11 231838](https://hackmd.io/_uploads/SJrAMkAjye.png) ![image](https://hackmd.io/_uploads/Byg-rJAoJl.png) ![image](https://hackmd.io/_uploads/rkGMHk0s1g.png) • Buka CMD windows lalu buat direktori untuk project yang akan dibuat ``` C:\Users\ofcyu>cd .. C:\Users>cd .. C:\>mkdir Dev && cd Dev C:\Dev> ``` • install Virtual Environment ``` C:\Dev\tryjango1-11>pip install virtualenv C:\Dev\tryjango1-11>pip list Package Version ------------ ------- distlib 0.3.9 filelock 3.17.0 pip 24.3.1 platformdirs 4.3.6 virtualenv 20.29.3 ``` • Membuat Environment ``` C:\Dev\tryjango1-11>virtualenv -p python3 . ``` ``` C:\Dev\tryjango1-11>.\Scripts\activate ``` • Install django latest version & start project ``` (tryjango1-11) C:\Dev\tryjango1-11>pip install Django==5.1.7 ``` ``` (tryjango1-11) C:\Dev\tryjango1-11>mkdir src && cd src (tryjango1-11) C:\Dev\tryjango1-11\src>django-admin startproject cfehome . ``` • Create Settings Module ``` (tryjango1-11) C:\Dev\tryjango1-11\src\cfehome>mkdir settings && cd settings ``` ```markdown code __init__.py ``` `code` merupakan text editor yang langsung terhubung ke visual studio code ```markdown= from .base import * from .production import * try: from .local import * except: pass ``` ```markdown code base.py ``` ```markdown= """ Django settings for cfehome project. Generated by 'django-admin startproject' using Django 5.1.7. For more information on this file, see https://docs.djangoproject.com/en/5.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'django-insecure-a_e3%5sjzt#r@b^j_m745sv@7gm#eor9g35q7b+3gp_ojd0zx7' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'cfehome.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'cfehome.wsgi.application' # Database # https://docs.djangoproject.com/en/5.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.1/howto/static-files/ STATIC_URL = 'static/' # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' ``` ```markdown! code production.py ``` ```markdown! code local.py ``` • install common tools ```markdown! pip install psycopg2 pip install gunicorn dj-database-url pip install django-crispy-forms pip install pillow ``` Tools Fungsi Utama **psycopg2** : Menghubungkan Django dengan database PostgreSQL. **gunicorn** : Server WSGI untuk menjalankan aplikasi Django di lingkungan produksi. **dj-database-url** : Memudahkan konfigurasi database menggunakan URL. **django-crispy-forms** : Membuat form Django lebih menarik dan responsif. **pillow** : Memproses dan memanipulasi gambar di aplikasi Django. > **Penjelasan lebih lanjut mengenai Gunicorn** > Gunicorn (Green Unicorn) adalah WSGI HTTP Server yang digunakan untuk menjalankan aplikasi web Python, seperti Django atau Flask, di lingkungan produksi. Gunicorn dirancang untuk menangani banyak permintaan (requests) secara bersamaan dan bekerja dengan baik di lingkungan deployment seperti Heroku, AWS, atau server Linux. > > WSGI (Web Server Gateway Interface) adalah antarmuka standar antara server web (seperti Gunicorn) dan aplikasi web Python (seperti Django atau Flask). WSGI memungkinkan server web dan aplikasi Python untuk berkomunikasi dengan cara yang seragam. > >Kenapa Butuh Gunicorn dan WSGI? >- Tanpa Gunicorn: Django bisa berjalan di server pengembangan (development), tetapi tidak kuat menangani banyak pengguna sekaligus. > - Tanpa WSGI: Gunicorn dan Django tidak bisa berkomunikasi dengan baik. > > Dengan Gunicorn dan WSGI: > - Aplikasi Django bisa menangani banyak pengguna sekaligus. > - Aplikasi Django bisa berjalan dengan aman dan cepat di lingkungan produksi. - buat requirements.txt file ```markdown! pip freeze > requirements.txt ``` ```markdown= asgiref==3.8.1 dj-database-url==2.3.0 Django==5.1.7 django-crispy-forms==2.3 gunicorn==23.0.0 packaging==24.2 pillow==11.1.0 psycopg2==2.9.10 sqlparse==0.5.3 typing_extensions==4.12.2 tzdata==2025.1 ``` • Run migration & createsuperuser ```markdown! python manage.py migrate ``` Fungsi: Menerapkan perubahan skema database (seperti membuat atau mengubah tabel) berdasarkan model Django ke database. Kegunaan: Digunakan setelah membuat atau mengubah model untuk memperbarui struktur database. ``` python manage.py createsuperuser ``` Fungsi: Membuat akun superuser (admin) untuk mengakses Django admin panel. Kegunaan: Digunakan untuk membuat akun admin yang dapat mengelola data melalui antarmuka admin Django. ## Section 2 : Create apps (HTML & Django) ```markdown= 1. Handles URLS 2. Return Response 3. Remember things account -- user "django app" videos -- 'dajngo app' analytics menu listings ``` #### 1. Create apps ```markdown! python manage.py startapp restaurants ``` - Ini adalah isi dari direktori restaurant ketika kita membuat app baru ``` restaurants/ ├── __init__.py ├── admin.py ├── apps.py ├── migrations/ │ └── __init__.py ├── models.py ├── tests.py └── views.py~~ ``` > models.py : Berfungsi sebagai tempat untuk mendefinisikan model database. Model adalah representasi tabel di database dan berisi field-field yang akan menyimpan data aplikasi Anda. > > admin.py : Berfungsi sebagai tempat untuk mendaftarkan model ke admin panel Django. Ini memungkinkan Anda mengelola data model (seperti menambah, mengedit, atau menghapus) melalui antarmuka admin. > > views.py : Berfungsi sebagai tempat untuk menulis logika aplikasi. View menerima request dari pengguna, memprosesnya, dan mengembalikan response (seperti halaman HTML atau data JSON). > > urls.py (dibuat manual) : Berfungsi sebagai tempat untuk mendefinisikan URL routing aplikasi. File ini menghubungkan URL tertentu dengan view yang sesuai. > > templates/ (dibuat manual) : Berfungsi sebagai tempat untuk menyimpan file template HTML. Template digunakan oleh view untuk merender tampilan yang akan dilihat oleh pengguna. > > settings.py (di direktori proyek utama) : Berfungsi sebagai tempat untuk mengonfigurasi pengaturan proyek Django, seperti menambahkan aplikasi ke INSTALLED_APPS, mengatur database, dan konfigurasi lainnya. - ubah `views.py` ```markdown= from django.http import HttpResponse from django.shortcuts import render # Create your views here. # fuction based view def home(request): return HttpResponse("hello") #return render(request, "home.html", {})#response ``` - ubah `urls.py` ```markdown= from django.contrib import admin from django.urls import path from restaurants.views import home urlpatterns = [ path('admin/', admin.site.urls), path('', home), ] ``` - jalankan server menggunakan perintah berikut ```markdown! python manage.py runserver ``` #### 2. Rendering HTML Di tahap ini, kita belajar membuat function-based view di Django menggunakan HttpResponse untuk mengembalikan HTML dinamis dengan f-string, seperti This is {html_var} coming through. Ini adalah dasar sebelum beralih ke template HTML dan render. - rubah `views.py` untuk membuat tampilan baru ```markdown= from django.http import HttpResponse from django.shortcuts import render # Create your views here. # fuction based view def home(request): html_var = 'f strings' html_ = f"""<!doctype html> <html lang="en" data-bs-theme="auto"> <head> </head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> </body> </html> """ #fstrings return HttpResponse(html_) #return render(request, "home.html", {})#response ``` #### 3. Render a Django Template Di tahap ini, kita belajar menggunakan template HTML di Django dengan membuat direktori templates dan file base.html. Kita mengonfigurasi settings.py untuk mengenali direktori template dan mengedit views.py untuk menggunakan fungsi render guna mengirim data dinamis (seperti html_var dan num) ke template. Ini adalah langkah untuk memisahkan logika Python dari tampilan HTML, menggantikan penggunaan HttpResponse langsung. - buat direktori baru didalam direktori src bernama `templates` lalu buat file baru `base.html` ![image](https://hackmd.io/_uploads/ryDxCIl3yx.png) lalu isi `base.html` seperti berikut ```markdown= <!doctype html> <html lang="en" > <head> </head> <body> <h1>Hello World!</h1> <p> This is {{ html_var }} coming through </p> <p> Random number is {{ num }} </p> </body> </html> ``` - dengan django kita bisa merubah template di `base.py` yang kita buat sebelumnya di direktori settings dengan merubah menjadi sebagai berikut pada bagian base dir, templates, dan database. ```markdown= import os from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) ``` ```markdown= TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] ``` ```markdown= DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } ``` - Edit `views.py` ubah menjadi sebagai berikut ```markdown= import random from django.http import HttpResponse from django.shortcuts import render # Create your views here. # fuction based view def home_old(request): html_var = 'f strings' num = random.randint(0, 100000000) html_ = """<!doctype html> <html lang="en" data-bs-theme="auto"> <head> </head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> <p>This is a random number: {num} coming through</p> </body> </html> """ #fstrings return HttpResponse(html_) def home(request): num = random.randint(0, 100000000) return render(request, "base.html", {"html_var": True, "num": num })#response ``` - edit `init.py` yanga da di direktori settings menjadi sebagai berikut ```markdown= from .base import * # from .production import * # try: # from .local import * # except: # pass ``` #### 4. Context in Django Template Di tahap ini, kita belajar tentang Context in Django Template. Kita mengirim data dari views.py ke template base.html menggunakan dictionary context. Data yang dikirim termasuk variabel boolean (bool_item), angka acak (num), dan list angka (some_list). Di template, kita menggunakan Django Template Language (DTL) seperti {% if %}, {% for %}, dan filter divisibleby untuk menampilkan data secara dinamis. Ini memungkinkan kita mengontrol tampilan HTML berdasarkan data yang dikirim dari view. - lakukan perubahan pada `views.py` ```markdown= import random from django.http import HttpResponse from django.shortcuts import render # Create your views here. # fuction based view def home(request): num = None some_list = [ random.randint(0, 100000000), random.randint(0, 100000000), random.randint(0, 100000000) ] condition_bool_item = False if condition_bool_item: num = random.randint(0, 100000000) context = { "bool_item": False, "num": num, "some_list": some_list } return render(request, "base.html", context) ``` - lakukan perubahan pada `base.html` ```markdown= <!doctype html> <html lang="en" > <head> </head> <body> <h1>Hello World!</h1> <p> This is <code> {% verbatim %}{{ html_var }} {% endverbatim %} </code> coming through </p> <p> {% if num is not None %} Random number is {{ num }} {% endif %} </p> <p> {% for some_item in some_list %} {% if some_item|divisibleby:"2" %} Even number {% endif %} {{ some_item }}<br/> {% endfor %} </p> <p>Some item is {{ some_item }} </p> </body> </html> ``` - Hasil ketika di test di browser ![image](https://hackmd.io/_uploads/HJ4qXzm3kl.png) #### 5. Template Inheritance - Tambahkan 2 views tambahan di file `views.py` ```markdown= import random from django.http import HttpResponse from django.shortcuts import render def home(request): num = None some_list = [ random.randint(0, 100000000), random.randint(0, 100000000), random.randint(0, 100000000) ] condition_bool_item = False if condition_bool_item: num = random.randint(0, 100000000) context = { "bool_item": False, "num": num, "some_list": some_list } return render(request, "home.html", context) def about(request): context = { } return render(request, "about.html", context) def contact(request): context = { } return render(request, "contact.html", context) ``` - Tambahkan template baru di `urls.py` untuk membuat halama baru ```markdown= from django.contrib import admin from django.urls import path from restaurants.views import home, about, contact urlpatterns = [ path('admin/', admin.site.urls), path('', home), path('about/', about), path('contact/', contact), ] ``` - Ubah `base.py` ```markdown= <!doctype html> <html lang="en" > <head> <title>{% block head_title %}Mendoan.com{% endblock head_title %}</title> </head> <body> <h1>Mendoan.com</h1> <a href='/'>Home</a> <a href='/about/'>About</a> <a href='/contact/'>Contact</a> <div class='container'> {% block content %}{% endblock content %} </div> </body> </html> ``` - Buat template baru di direktori templates `code home.html` `code about.html` `code contact.html` >Home ```markdown= {% extends "base.html" %} {% block head_title %}Welcome || {{ block.super }}{% endblock head_title %} {% block content %} <h1>Hello World!</h1> <h3>{{ block.super }}</h3> <p> This is <code> {% verbatim %}{{ html_var }} {% endverbatim %} </code> coming through </p> <p> {% if num is not None %} Random number is {{ num }} {% endif %} </p> <p> {% for some_item in some_list %} {% if some_item|divisibleby:"2" %} Even number {% endif %} {{ some_item }}<br/> {% endfor %} </p> <p>Some item is {{ some_item }} </p> {% endblock content %} ``` >About ```markdown= {% extends "base.html" %} {% block head_title %}About || {{ block.super }}{% endblock head_title %} {% block content %} <h1>About</h1> {% endblock %} ``` > contact ```markdown= {% extends "base.html" %} {% block head_title %}Contact || {{ block.super }}{% endblock head_title %} {% block content %} <h1>Content</h1> {% endblock %} ``` - Hasil home ![image](https://hackmd.io/_uploads/Sy2QjnSh1l.png) - Hasil About ![image](https://hackmd.io/_uploads/rkFIinBhJe.png) - Hasil Contact ![image](https://hackmd.io/_uploads/ryQOjhBnyg.png) #### 6. Include Template Tag - buat direktori baru didalam direktori template ```markdown! mkdir snippets ``` - buat file html baru didalam direktori snippets ```markdown! code nav.html code css.html code js.html code sidebar.html ``` - edit `base.py` ```markdown= <!doctype html> <html lang="en" > <head> <title>{% block head_title %}Mendoan.com{% endblock head_title %}</title> {% include 'snippets/css.html' %} </head> <body> {% include 'snippets/nav.html' %} <div class='container'> {% block content %}{% endblock content %} </div> {% include 'snippets/js.html' %} </body> </html> ``` - edit `nav.html` ```markdown= <div class='container'> <h1>Mendoan.com</h1> <a href='/'>Home</a> <a href='/about/'>About</a> <a href='/contact/'>Contact</a> </div> ``` - edit `css.html` > didapatkan dari Bootstrap CDN version 3.3.7 ```markdown= <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <!-- Optional theme --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> ``` - edit `js.html` >didapatkan dari Bootstrap CDN version 3.3.7 ```markdown= <!-- Latest compiled and minified JavaScript --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> ``` >copy link jquary core 3.7.1 minified ```markdown= <script src='https://code.jquery.com/jquery-3.7.1.min.js'></script> ``` - edit `sidebar.html` ```markdown= <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> ``` - tambahkan include tag di `about.html` dan `contact html` ```markdown= {% extends "base.html" %} {% block head_title %}About || {{ block.super }}{% endblock head_title %} {% block content %} <h1>About</h1> {% include 'snippets/sidebar.html' %} {% endblock %} ``` ```markdown= {% extends "base.html" %} {% block head_title %}Contact || {{ block.super }}{% endblock head_title %} {% block content %} <h1>Content</h1> {% include 'snippets/sidebar.html' %} {% endblock %} ``` - hasil ketika di browse ![image](https://hackmd.io/_uploads/BJFbrarnJl.png) ![image](https://hackmd.io/_uploads/BJDGSTShkx.png) ![image](https://hackmd.io/_uploads/BJ3QB6Bnkg.png) #### 7. Class based View - edit `views.py` dengan menabahkan class Contactview ```markdown= import random from django.http import HttpResponse from django.shortcuts import render from django.views import View def home(request): num = None some_list = [ random.randint(0, 100000000), random.randint(0, 100000000), random.randint(0, 100000000) ] condition_bool_item = False if condition_bool_item: num = random.randint(0, 100000000) context = { "bool_item": False, "num": num, "some_list": some_list } return render(request, "home.html", context) def about(request): context = { } return render(request, "about.html", context) def contact(request): context = { } return render(request, "contact.html", context) class ContactView(View): def get(self, request, *args, **kwargs): print(kwargs) context = {} return render(request, "contact.html", context) ``` - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from restaurants.views import home, about, contact, ContactView urlpatterns = [ path('admin/', admin.site.urls), path('', home), path('about/', about), re_path('contact/(?P<id>\d+)', ContactView.as_view()), ``` hasilnya adalah ketika kita membuka aplikasi secara default maka akan error ![image](https://hackmd.io/_uploads/BkgygRr3Jl.png) tetapi ketika kita menambahkan id maka aplikasi akan terbuka ![image](https://hackmd.io/_uploads/rkgAMlCr3kx.png) dan akan menghasil output berikut di server ![image](https://hackmd.io/_uploads/SJ7SxCBhJe.png) - Template View cara pertama - edit `views.py` ```markdown= import random from django.http import HttpResponse from django.shortcuts import render from django.views import View from django.views.generic import TemplateView class HomeView(TemplateView): template_name = 'home.html' def get_context_data(self, *args, **kwargs): context = super(HomeView, self).get_context_data(*args, **kwargs) num = None some_list = [ random.randint(0, 100000000), random.randint(0, 100000000), random.randint(0, 100000000) ] condition_bool_item = True if condition_bool_item: num = random.randint(0, 100000000) context = { "bool_item": False, "num": num, "some_list": some_list } return context class AboutView(TemplateView): template_name = 'about.html' class ContactView(TemplateView): template_name = 'contact.html' ``` - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from restaurants.views import HomeView, AboutView, ContactView urlpatterns = [ path('admin/', admin.site.urls), path('', HomeView.as_view()), path('about/', AboutView.as_view()), re_path('contact/', ContactView.as_view()), ] ``` Metode Kedua, memindahkan template `about.html` dan `contact.html` ke `urls.py` >kita dapat melakukan hal yang sama kepada template `home.html` jika tidak terdapat context didalamnya - edit `views.py` ```markdown= import random from django.http import HttpResponse from django.shortcuts import render from django.views import View from django.views.generic import TemplateView class HomeView(TemplateView): template_name = 'home.html' def get_context_data(self, *args, **kwargs): context = super(HomeView, self).get_context_data(*args, **kwargs) num = None some_list = [ random.randint(0, 100000000), random.randint(0, 100000000), random.randint(0, 100000000) ] condition_bool_item = True if condition_bool_item: num = random.randint(0, 100000000) context = { "bool_item": False, "num": num, "some_list": some_list } return context ``` - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import HomeView urlpatterns = [ path('admin/', admin.site.urls), path('', HomeView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), re_path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` ## Section 3 : Remembering Things #### 1. Remembering things with models - buat super user ```markdown! python manage.py createsuperuser ``` - Akses django admin di browser `http:<ip>:8080/admin` ![image](https://hackmd.io/_uploads/S1hiUqLnJl.png) - Masukan username dan password yang telah dibuat sebelumnya ![image](https://hackmd.io/_uploads/S1MZDcLhJg.png) - edit `base.py` ```markdown= INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'restaurants', ] ``` - edit `models.py` yang ada di direktori restaurants ```markdown= from django.db import models # Create your models here. class RestaurantLocation(models.Model): name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) ``` - migrate database ```markdown! python manage.py makemigrations python manage.py migrate ``` >makemigrations: Membuat catatan perubahan (seperti menulis daftar tugas). > > migrate: Menjalankan perubahan tersebut (seperti melakukan tugas yang sudah ditulis). - edit `admin.py` ```markdown= from django.contrib import admin # Register your models here. from .models import RestaurantLocation admin.site.register(RestaurantLocation) ``` - Hasil ![image](https://hackmd.io/_uploads/H1wmQOvhJl.png) ![image](https://hackmd.io/_uploads/rJJS7uDhyl.png) ![image](https://hackmd.io/_uploads/S1zUm_vhJg.png) #### 2. More on Model Fields - Edit `models.py` untuk menambahkan models baru ```markdown= from django.db import models # Create your models here. class RestaurantLocation(models.Model): name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True) timestamp = models.DateTimeField(auto_now=False, auto_now_add=False) ``` >`category = models.CharField(max_length=120, null=True, blank=False)` >category : Nama field di database. >models.CharField : Tipe data untuk teks (string). >max_length=120 : Maksimal 120 karakter. >null=True : Boleh kosong di database (disimpan sebagai NULL). >blank=True : Boleh kosong saat input data di form Django. - lakukan makemigrations & migrate ```markdown! (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py makemigrations Migrations for 'restaurants': restaurants\migrations\0005_alter_restaurantlocation_category.py ~ Alter field category on restaurantlocation (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py makemigrations It is impossible to add a non-nullable field 'timestamp' to restaurantlocation without specifying a default. This is because the database needs something to populate existing rows. Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit and manually define a default value in models.py. Select an option: 1 Please enter the default value as valid Python. The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value. Type 'exit' to exit this prompt >>> timezone.now Migrations for 'restaurants': restaurants\migrations\0006_restaurantlocation_timestamp.py + Add field timestamp to restaurantlocation (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, restaurants, sessions Running migrations: Applying restaurants.0005_alter_restaurantlocation_category... OK Applying restaurants.0006_restaurantlocation_timestamp... OK ``` - hasil ![image](https://hackmd.io/_uploads/Hy0Nqdv2kg.png) #### 3. Displaying Saved Data - edit `views.py` ```markdown= import random from django.http import HttpResponse from django.shortcuts import render from django.views import View from django.views.generic import TemplateView from .models import RestaurantLocation def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) ``` - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import restaurant_listview urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', restaurant_listview), path('about/', TemplateView.as_view(template_name='about.html')), re_path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - buat direktori templates didalam direktori aplikasi restaurant ```markdown! C:\Dev\tryjango1-11\src\restaurants> mkdir templates && cd templates C:\Dev\tryjango1-11\src\restaurants\templates>mkdir restaurants && cd restaurants ``` - buat file `restaurants_list.html` didalam direktori restaurants ```markdown! code restaurants_list.html ``` - isi file `restaurants_list.html` seperti dibawah ini ```markdown= {% extends "base.html" %} {% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Restaurant List</h1> <ul> {% for obj in object_list %} <li>{{ obj }} <br/> {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li> {% endfor %} </ul> {% endblock %} ``` - edit `models.py` ```markdown= from django.db import models # Create your models here. class RestaurantLocation(models.Model): name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True) timestamp = models.DateTimeField(auto_now=False, auto_now_add=False) def __str__(self): return self.name ``` - Jalankan migrate ```markdown! python manage.py makemigrations python manage.py migrate ``` - hasil ![image](https://hackmd.io/_uploads/rk72X__2ye.png) #### 4. Understading Querysets - masuk ke shell django ```markdown! (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py shell Python 3.13.2 (tags/v3.13.2:4f8bb39, Feb 4 2025, 15:23:48) [MSC v.1942 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> ``` - Melihat isi database ```markdown! >>> from restaurants.models import RestaurantLocation >>> RestaurantLocation.objects.all() <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>]> >>> for obj in RestaurantLocation.objects.all(): ... print(obj.name) ... Baja Fish Tacos Masakan Jawa >>> ``` - filter dan mengupdate database ```markdown! >>> qs = RestaurantLocation.objects.all() >>> qs.filter(category='mexican') <QuerySet []> >>> qs.filter(category__iexact='mexican') <QuerySet [<RestaurantLocation: Baja Fish Tacos>]> >>> qs.update(category='American') 2 >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>]> >>> qs.filter(category__iexact='mexican') <QuerySet []> >>> qs.filter(category__iexact='American') <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>]> >>> ``` - Membuat Restaurant Location menggunakan shell ```markdown! >>> obj = RestaurantLocation() >>> obj.name = "Pei Wei" >>> obj.location = "Newport Beach" >>> obj.category = "Asian Fusion" >>> obj.save() >>> qs = RestaurantLocation.objects.all() >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>]> ``` - Membuat Restaurant Location dengan 1 baris perintah ```markdown! >>> obj = RestaurantLocation.objects.create(name='Chronic Tacos', location='Corona Del Mar', category='Mexican') >>> obj <RestaurantLocation: Chronic Tacos> >>> qs = RestaurantLocation.objects.all() >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>]> >> qs.count() 4 ``` - Keluar dari shell ```markdown! >>> exit() now exiting InteractiveConsole... (tryjango1-11) C:\Dev\tryjango1-11\src> ``` #### 5. Generic list View - edit `views.py` ```markdown= import random from django.db.models import Q from django.http import HttpResponse from django.shortcuts import render from django.views import View from django.views.generic import TemplateView, ListView from .models import RestaurantLocation def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset ``` - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_listview, RestaurantListView, ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), re_path(r'^restaurants/(?P<slug>\w+)/$', RestaurantListView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), re_path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - ubah penamaan templates restaurant `restaurant_list.html` menjadi `restaurantlocation.html` ```markdown rename restaurant_list.html restaurantlocation.html ``` #### 6. Restaurant Profile Detail - edit `views.py` ```markdown import random from django.db.models import Q from django.http import HttpResponse from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView from .models import RestaurantLocation def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() def get_object(self, *args, **kwargs): rest_id = self.kwargs.get('rest_id') obj = get_object_or_404(RestaurantLocation, id=rest_id) #pk = rest_id return obj ``` - edit `urls.py` ```markdown from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_listview, RestaurantListView, RestaurantDetailView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), path('restaurants/<rest_id>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), re_path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - buat file html baru di dalam templates restaurant ```markdown! code restaurantlocation_detail.html ``` - isi file `restaurantlocation_detail.html` seperti sebagai berikut ```markdown= {% extends "base.html" %} {% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Restaurant List</h1> <ul> {% for obj in object_list %} <li>{{ obj }} <br/> {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li> {% endfor %} </ul> {% endblock %} ``` - hasil ![image](https://hackmd.io/_uploads/B1iWSp_hJx.png) #### 7. Slugfield and Uniqe Slug Generator - edit `models.py` ```markdown= from django.db import models # Create your models here. class RestaurantLocation(models.Model): name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True) timestamp = models.DateTimeField(auto_now_add=True) update = models.DateTimeField(auto_now_add=True) slug = models.SlugField(null=True, blank=True) def __str__(self): return self.name @property def title(self): return self.name #obj.title ``` - buat file `utils.py` di dalam direktori aplikasi restaurant ```markdown code utils.py ``` - edit `utils.py` ```markdown= import random import string from django.utils.text import slugify ''' random_string_generator is located here: http://joincfe.com/blog/random-string-generator-in-python/ ''' def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits): return ''.join(random.choice(chars) for _ in range(size)) def unique_slug_generator(instance, new_slug=None): """ This is for a Django project and it assumes your instance has a model with a slug field and a title character (char) field. """ if new_slug is not None: slug = new_slug else: slug = slugify(instance.title) Klass = instance.__class__ qs_exists = Klass.objects.filter(slug=slug).exists() if qs_exists: new_slug = "{slug}-{randstr}".format( slug=slug, randstr=random_string_generator(size=4) ) return unique_slug_generator(instance, new_slug=new_slug) return slug ``` - edit `restaurantlocation_detail.html` ```markdown= {% extends "base.html" %} {% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>{{ object.title }} <small> {{ object.category }}</small></h1> <p>{{ object.location}}</p> <p>{{ object.timestamp }}, updated {{ opject.updated|timesince }} ago </p> {% endblock %} ``` - lakukan migrate ```markdown python manage.py makemigrations python manage.py migrate ``` - jalankan shell django ```markdown python manage.py shell ``` ```markdown! >>> from restaurants.models import RestaurantLocation >>> obj = RestaurantLocation.objects.get(id=1) >>> obj.name 'Baja Fish Tacos' >>> obj.title 'Baja Fish Tacos' >>> from restaurants.utils import unique_slug_generator >>> print (unique_slug_generator(obj)) baja-fish-tacos >>> exit() ``` #### 8. Signal for Uniqe Slug - edit `models.py` ```markdown= from django.db import models from django.db.models.signals import pre_save, post_save from .utils import unique_slug_generator class RestaurantLocation(models.Model): name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True) timestamp = models.DateTimeField(auto_now_add=True) update = models.DateTimeField(auto_now_add=True) slug = models.SlugField(unique=True, null=True, blank=True) def __str__(self): return self.name @property def title(self): return self.name #obj.title def rl_pre_save_receiver(sender, instance, *args, **kwargs): print("saving...") print(instance.timestamp) if not instance.slug: instance.slug = unique_slug_generator(instance) def rl_post_save_receiver(sender, instance, created, *args, **kwargs): print('saved') print(instance.timestamp) instance.save() pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation) post_save.connect(rl_post_save_receiver, sender=RestaurantLocation) ``` - lakukan perubahan item, lalu save and continue ![image](https://hackmd.io/_uploads/Sk_JtDK2kg.png) - terminal akan menghasilkan output seperti dibawah ini ```markdown! saving.. 2025-03-19 17:24:25.820563+00:00 saved 2025-03-19 17:24:25.820563+00:00 [20/Mar/2025 17:04:25] "POST /admin/restaurants/restaurantlocation/3/change/ HTTP/1.1" 302 0 ``` ## Section 4 : Froms, Views, Models, and More #### 1. Slug as Urls Params - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_listview, RestaurantListView, RestaurantDetailView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - edit `views.py` ```markdown= import random from django.db.models import Q from django.http import HttpResponse from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView from .models import RestaurantLocation def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() ``` - edit `restaurantlocation_list.html` ```markdown= {% extends "base.html" %} {% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Restaurant List</h1> <ul> {% for obj in object_list %} <li><a href='/restaurants/{{ obj.slug }}'>{{ obj }}</a><br/> {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li> {% endfor %} </ul> {% endblock %} ``` - maka hasilnya ketika kita membuka profile maka akan ada tag yang sesuai dengan profile restaurantnya ![image](https://hackmd.io/_uploads/ry-jXoKhJx.png) #### 2. Get Single Items from DB - masuk ke shell django ```markdown python manage.py shell ``` - Melihat Semua Data ```markdown! >>> from restaurants.models import RestaurantLocation >>> qs = RestaurantLocation.objects.all() >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, ...]> ``` - Mengambil Elemen Pertama dan Terakhir ```markdown! >>> qs.first() <RestaurantLocation: Baja Fish Tacos> >>> qs.last() <RestaurantLocation: Chronic Tacos> ``` - Mengambil Objek Berdasarkan Indeks ```markdown! >>> qs[1] <RestaurantLocation: Masakan Jawa> ``` - filter berdasarkan kategori ```markdown! >>> qs = RestaurantLocation.objects.filter(category='mexican') >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>]> >>> qs = RestaurantLocation.objects.filter(category__iexact='mexican') >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Chronic Tacos>]> ``` >`filter(category='mexican')` mencari data dengan kategori persis "mexican". > >`filter(category__iexact='mexican') `mencari data tanpa memperdulikan huruf besar/kecil. - Mengambil Objek berdasarkan Primary Key (PK) ```markdown! >>> RestaurantLocation.objects.get(pk=1) <RestaurantLocation: Baja Fish Tacos> ``` >Menggunakan .get() untuk mengambil satu item berdasarkan primary key (pk) >Mengambil satu objek dengan pk=1, jika tidak ditemukan akan error. - Menggunakan `get_object_or_404()` ```markdown! >>> from django.shortcuts import get_object_or_404 >>> obj = get_object_or_404(RestaurantLocation, pk=12000) ``` >Mengambil objek berdasarkan pk=12000. Jika tidak ditemukan, akan mengembalikan error 404. - Menggunakan `try-except` untuk Menangani Error ```markdown! >>> try: ... obj = get_object_or_404(RestaurantLocation, pk=12000) ... except: ... print("Not found") ... Not found ``` >Menghindari error dengan menangkap Http404 jika objek tidak ditemukan - Menggunakan `filter()` dan `exists()` ```markdown! >>> qs = RestaurantLocation.objects.filter(pk=12000) >>> qs.exists() False ``` >`.filter()` tidak akan menimbulkan error jika tidak ada data, berbeda dengan `.get()`. - Mengambil Objek Berdasarkan Slug ```markdown! >>> qs = RestaurantLocation.objects.filter(slug='baja-fish-tacos') >>> if qs.exists(): ... print(qs.first()) ... Baja Fish Tacos ``` #### 3. Saving Data the Hard + Wrong Way - buat file `forms.py` didalam direktori aplikasi restaurant ```markdown! code forms.py ``` - edit file `forms.py` ```markdown! from django import forms class RestaurantCreateForm(forms.Form): name = forms.CharField location = forms.CharField(required=False) category = forms.CharField(required=False) ``` - edit `views.py` ```markdown! import random from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView from .forms import RestaurantCreateForm from .models import RestaurantLocation def restaurant_createview(request): if request.method == "GET": print("get data") if request.method == "POST": #PUT print("post data") print(request.POST) title = request.POST.get("title") #request.POST("title") location = request.POST.get("location") category = request.POST.get("category") obj = RestaurantLocation.objects.create( name = title, location = location, category = category ) return HttpResponseRedirect("/restaurants/") template_name = 'restaurants/form.html' context = {} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user # def get_object(self, *args, **kwargs): # rest_id = self.kwargs.get('rest_id') # obj = get_object_or_404(RestaurantLocation, id=rest_id) #pk = rest_id # return obj ``` - buat file `form.html` didalam templates aplikasi restaurant `restaurants/templates/restaurants` ```markdown! code form.html ``` - isi file `form.html` ```markdown! {% extends "base.html" %} {% block head_title %}Add Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Add Restaurants</h1> <form method="POST"> {% csrf_token %} <input type='text' name='title' placeholder="Your Title"><br/> <input type='text' name='location' placeholder="Location"><br/> <input type='text' name='category' placeholder="Category"><br/> <button type="submit">Save</button> </form> {% endblock %} ``` - edit `utils.py` ```markdown! import random import string from django.utils.text import slugify DONT_USE = ['create'] def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits): return ''.join(random.choice(chars) for _ in range(size)) def unique_slug_generator(instance, new_slug=None): if new_slug is not None: slug = new_slug else: slug = slugify(instance.title) if slug in DONT_USE: new_slug = "{slug}-{randstr}".format( slug=slug, randstr=random_string_generator(size=4) ) return unique_slug_generator(instance, new_slug=new_slug) Klass = instance.__class__ qs_exists = Klass.objects.filter(slug=slug).exists() if qs_exists: new_slug = "{slug}-{randstr}".format( slug=slug, randstr=random_string_generator(size=4) ) return unique_slug_generator(instance, new_slug=new_slug) return slug ``` - hasil ![image](https://hackmd.io/_uploads/SJts-0K21e.png) ![image](https://hackmd.io/_uploads/Hkn3WRF2kx.png) #### 4. The Power of Django Forms - edit `views.py` ```markdown= import random from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView from .forms import RestaurantCreateForm from .models import RestaurantLocation def restaurant_createview(request): form = RestaurantCreateForm(request.POST or None) errors = None if form.is_valid(): obj = RestaurantLocation.objects.create( name = form.cleaned_data.get('name'), location = form.cleaned_data.get('location'), category = form.cleaned_data.get('category') ) return HttpResponseRedirect("/restaurants/") if form.errors: print(form.errors) errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user # def get_object(self, *args, **kwargs): # rest_id = self.kwargs.get('rest_id') # obj = get_object_or_404(RestaurantLocation, id=rest_id) #pk = rest_id # return obj ``` - edit `form.html` ```markdown= {% extends "base.html" %} {% block head_title %}Add Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Add Restaurants</h1> {% if errors %} {{ errors }} {% endif %} <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Save</button> </form> {% endblock %} <!-- <input type='text' name='name' placeholder="Your Title" reqired=reqired><br/> <input type='text' name='location' placeholder="Location"><br/> <input type='text' name='category' placeholder="Category"><br/> --> ``` - edit `forms.py` ```markdown= from django import forms class RestaurantCreateForm(forms.Form): name = forms.CharField() location = forms.CharField(required=False) category = forms.CharField(required=False) def clean_name(self): name = self.cleaned_data.get("name") if name == "Hello": raise forms.ValidationError("Not a valid name") return name ``` - hasil ![image](https://hackmd.io/_uploads/SkHCcAF3yg.png) ![image](https://hackmd.io/_uploads/Sy_JiCY2ye.png) #### 5. The Extra Power of Django Models Form - edit `forms.py` ```markdown= from django import forms from .models import RestaurantLocation class RestaurantCreateForm(forms.Form): name = forms.CharField() location = forms.CharField(required=False) category = forms.CharField(required=False) def clean_name(self): name = self.cleaned_data.get("name") if name == "Hello": raise forms.ValidationError("Not a valid name") return name class RestaurantLocationCreateForm(forms.ModelForm): class Meta: model = RestaurantLocation fields = [ 'name', 'location', 'category', ] def clean_name(self): name = self.cleaned_data.get("name") if name == "Hello": raise forms.ValidationError("Not a valid name") return name ``` - edit `views.py` ```markdown= import random from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView, CreateView from .forms import RestaurantCreateForm, RestaurantLocationCreateForm from .models import RestaurantLocation def restaurant_createview(request): form = RestaurantLocationCreateForm(request.POST or None) errors = None if form.is_valid(): # Customize # like a pre save form.save() #like a post save return HttpResponseRedirect("/restaurants/") if form.errors: print(form.errors) errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user class RestaurantCreateView(CreateView): form_class = RestaurantLocationCreateForm template_name = 'restaurants/form.html' success_url = "/restaurants/" ``` - edit `urls.py` ```markdown= from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_listview, RestaurantListView, RestaurantDetailView, RestaurantCreateView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), path('restaurants/create', RestaurantCreateView.as_view()), path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - edit form.html ```markdown= {% extends "base.html" %} {% block head_title %}Add Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Add Restaurants</h1> {% if form.errors %} {{ form.errors }} {% endif %} <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Save</button> </form> {% endblock %} ``` #### 6. Simple + Effective Validation - edit `forms.py` ```markdown= from django import forms from .models import RestaurantLocation from .validators import validate_category class RestaurantCreateForm(forms.Form): name = forms.CharField() location = forms.CharField(required=False) category = forms.CharField(required=False) def clean_name(self): name = self.cleaned_data.get("name") if name == "Hello": raise forms.ValidationError("Not a valid name") return name class RestaurantLocationCreateForm(forms.ModelForm): # email = forms.EmailField() # category = forms.CharField(required=False, validators=[validate_category]) class Meta: model = RestaurantLocation fields = [ 'name', 'location', 'category', ] def clean_name(self): name = self.cleaned_data.get("name") if name == "Hello": raise forms.ValidationError("Not a valid name") return name # def clean_email(self): # email = self.cleaned_data.get("email") # if ".edu" in email: # raise forms.ValidationError("We do not accept edu emails ") # return email ``` - edit `form.html` ```markdown= {% extends "base.html" %} {% block head_title %}Add Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Add Restaurants</h1> {% if form.errors.non_field_errors %} {{ form.errors }} {% endif %} <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Save</button> </form> {% endblock %} ``` - buat file baru didalam direktori aplikasi restaurant `validators.py` ```markdown! code validators.py ``` - isi file `validators.py` seperti dibawah ini ```markdown= from django.core.exceptions import ValidationError def validate_even(value): if value % 2 != 0: raise ValidationError( '%(value)s is not an even number', params={"value": value}, ) def validate_email(value): email = value if ".edu" in email: raise ValidationError("We do not accept edu emails ") CATEGORIES = ['Mexican', 'Asian', 'American', 'Whatever'] def validate_category(value): cat = value.capitalize() if not value in CATEGORIES and not cat in CATEGORIES: raise ValidationError(f"{value} not a valid category") ``` - edit `models.py` ```markdown= from django.db import models from django.db.models.signals import pre_save, post_save from .utils import unique_slug_generator from .validators import validate_category class RestaurantLocation(models.Model): name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category]) timestamp = models.DateTimeField(auto_now_add=True) update = models.DateTimeField(auto_now_add=True) slug = models.SlugField(unique=True, null=True, blank=True) def __str__(self): return self.name @property def title(self): return self.name #obj.title def rl_pre_save_receiver(sender, instance, *args, **kwargs): instance.category = instance.category.capitalize() if not instance.slug: instance.slug = unique_slug_generator(instance) # def rl_post_save_receiver(sender, instance, created, *args, **kwargs): # print('saved') # print(instance.timestamp) # if not instance.slug: # instance.slug = unique_slug_generator(instance) # instance.save() pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation) # post_save.connect(rl_post_save_receiver, sender=RestaurantLocation) ``` - hasil ![image](https://hackmd.io/_uploads/HybU3G92kg.png) ![image](https://hackmd.io/_uploads/BJlv2GchJg.png) #### 7. Letting Users Own Data - edit `models.py` ```markdown= from django.conf import settings from django.db import models from django.db.models.signals import pre_save, post_save from .utils import unique_slug_generator from .validators import validate_category User = settings.AUTH_USER_MODEL class RestaurantLocation(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE) # class_instance.model_set.all() name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category]) timestamp = models.DateTimeField(auto_now_add=True) update = models.DateTimeField(auto_now_add=True) slug = models.SlugField(unique=True, null=True, blank=True) def __str__(self): return self.name @property def title(self): return self.name #obj.title def rl_pre_save_receiver(sender, instance, *args, **kwargs): instance.category = instance.category.capitalize() if not instance.slug: instance.slug = unique_slug_generator(instance) # def rl_post_save_receiver(sender, instance, created, *args, **kwargs): # print('saved') # print(instance.timestamp) # if not instance.slug: # instance.slug = unique_slug_generator(instance) # instance.save() pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation) # post_save.connect(rl_post_save_receiver, sender=RestaurantLocation) ``` - lakukan migrasi dengan perintah `python manage.py makemigrations` dan `python manage.py migrate` ```markdown! (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py makemigrations It is impossible to add a non-nullable field 'owner' to restaurantlocation without specifying a default. This is because the database needs something to populate existing rows. Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit and manually define a default value in models.py. Select an option: 1 Please enter the default value as valid Python. The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value. Type 'exit' to exit this prompt >>> 1 Migrations for 'restaurants': restaurants\migrations\0012_restaurantlocation_owner.py + Add field owner to restaurantlocation (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, restaurants, sessions Running migrations: Applying restaurants.0012_restaurantlocation_owner... OK ``` - hasil ![image](https://hackmd.io/_uploads/BJfrKU53kg.png) - masuk ke shell django ```markdown! python manage.py shell ``` - mengimpor model User dari django ```markdown! >>> from django.contrib.auth import get_user_model >>> User = get_user_model() ``` - mengambil semua objek user yang ada di database ```markdown! >>> User.objects.all() <QuerySet [<User: ofcyu>]> ``` - Mengambil objek User dengan ID 1 dari database ```markdown! >>> ofcyu_user = User.objects.get(id=1) >>> ofcyu_user <User: ofcyu> ``` - Mengakses atribut username,email, password dari objek User ```markdown! >>> ofcyu_user.username 'ofcyu' >>> ofcyu_user.email '' >>> ofcyu_user.password 'pbkdf2_sha256$870000$T7Noh6wIdo7Rtgoqgl4bSc$f2fo7B0SeqMF1eD145FPSD03yZqnJxDwM68/B1sEU3o=' ``` - menggunakan Variabel User ```markdown! >>> obj = ofcyu_user >>> obj <User: ofcyu> ``` >Variabel obj menyimpan instance user dengan nama "ofcyu". - menggunakan instance user ```markdown! >>> instance = ofcyu_user >>> instance <User: ofcyu> ``` >Variabel instance juga menyimpan instance yang sama dengan obj. - Mengakses Data yang Dimiliki User ```markdown! instance.restaurantlocation_set.all() <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]> ``` - Memfilter Data Berdasarkan Kategori ```markdown! >>> instance.restaurantlocation_set.filter(category__iexact='mexican') <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Chipotle>, <RestaurantLocation: name>, <RestaurantLocation: Validate>]> ``` - exit shell lalu jalankan lagi shell baru ```markdown! >>> exit() (tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py shell ``` - Menggunakan `filter(owner__id=1)` ```markdown! >>> from restaurants.models import RestaurantLocation >>> RestaurantLocation.objects.filter(owner__id=1) <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]> ``` > Perintah ini mengambil semua objek dari model RestaurantLocation yang memiliki owner dengan ID 1. - Menggunakan filter(owner__username__iexact='ofcyu') ```markdown! >>> RestaurantLocation.objects.filter(owner__username__iexact='ofcyu') <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]> ``` > Perintah ini mencari semua objek RestaurantLocation yang dimiliki oleh pengguna dengan username ofcyu. - Menyimpan QuerySet ke Variabel ```markdown! >>> qs = RestaurantLocation.objects.filter(owner__username__iexact='ofcyu') >>> qs <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]> ``` > QuerySet hasil pencarian disimpan ke dalam variabel qs agar dapat digunakan kembali tanpa perlu menjalankan query ulang. - Mengambil Objek Pertama dalam QuerySet ```markdown! >>> obj = qs.first() >>> obj.owner <User: ofcyu> ``` > Mengambil objek pertama dari QuerySet menggunakan .first(). > > Mengecek siapa pemilik restoran dengan memanggil obj.owner. - Mendapatkan Kelas Model User ```markdown! >>> User = obj.owner.__class__ >>> User <class 'django.contrib.auth.models.User'> ``` - Melihat Semua User dalam Database ```markdown! >>> User.objects.all() <QuerySet [<User: ofcyu>]> ``` - Mengambil User Pertama dalam QuerySet ```markdown! >>> ofcyu_user = User.objects.all().first() ``` - Melihat Semua RestaurantLocation yang Dimiliki oleh User ```markdown! >>> ofcyu_user.restaurantlocation_set.all() <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]> ``` > `restaurantlocation_set.all()` digunakan karena terdapat relasi One-To-Many antara User dan RestaurantLocation. - Menyimpan QuerySet ke Variabel Baru ```markdown! >>> new_qs = ofcyu_user.restaurantlocation_set.all() >>> new_obj = new_qs.first() >>> new_obj <RestaurantLocation: Baja Fish Tacos> ``` >` new_qs` menyimpan semua restoran yang dimiliki oleh ofcyu. > > `new_obj` mengambil restoran pertama dari QuerySet tersebut. - Mendapatkan Kelas Model RestaurantLocation ```markdown! >>> RK = new_obj.__class__ >>> RK.objects.all() <QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]> ``` > `RK` menyimpan kelas dari objek `RestaurantLocation`. > > `RK.objects.all()` menampilkan semua data restoran yang tersimpan dalam model `RestaurantLocation`. #### 8. Associate User to Form Data in FBV - ketika kita menabahkan item yang merupakan sebuah objek aplikasi maka akan error ![image](https://hackmd.io/_uploads/H1USF9c21g.png) ![image](https://hackmd.io/_uploads/r1ELtq52yx.png) - login ke django administration ![image](https://hackmd.io/_uploads/Hyhhq9chkg.png) - edit `urls.py` ```markdown from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_createview, restaurant_listview, RestaurantListView, RestaurantDetailView, RestaurantCreateView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), path('restaurants/create', restaurant_createview ),#RestaurantCreateView.as_view()), path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), path('contact/', TemplateView.as_view(template_name='contact.html')), ``` - edit `views.py` ```markdown import random from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView, CreateView from .forms import RestaurantCreateForm, RestaurantLocationCreateForm from .models import RestaurantLocation def restaurant_createview(request): form = RestaurantLocationCreateForm(request.POST or None) errors = None if form.is_valid(): if request.user.is_authenticated: instance = form.save(commit=False) # Customize # like a pre save instance.owner = request.user instance.save() #like a post save return HttpResponseRedirect("/restaurants/") else: return HttpResponseRedirect("/login/") if form.errors: errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user class RestaurantCreateView(CreateView): form_class = RestaurantLocationCreateForm template_name = 'restaurants/form.html' success_url = "/restaurants/" ``` - Hasil ![image](https://hackmd.io/_uploads/H1d1Xsqh1l.png) >sudah tidak error - Buka Ignito mode ![image](https://hackmd.io/_uploads/ByKJMo93ke.png) ![image](https://hackmd.io/_uploads/By9NMo9nye.png) ![image](https://hackmd.io/_uploads/BybW7jc3yg.png) ![image](https://hackmd.io/_uploads/Bkn-Xichkg.png) >jika create di mode ignito maka page akan tidak ditemukan #### 9. Associate User to Data in Class Based View - edit `views.py` ```markdown import random from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView, CreateView from .forms import RestaurantCreateForm, RestaurantLocationCreateForm from .models import RestaurantLocation def restaurant_createview(request): form = RestaurantLocationCreateForm(request.POST or None) errors = None if form.is_valid(): if request.user.is_authenticated: instance = form.save(commit=False) # Customize # like a pre save instance.owner = request.user instance.save() #like a post save return HttpResponseRedirect("/restaurants/") else: return HttpResponseRedirect("/login/") if form.errors: errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user class RestaurantCreateView(CreateView): form_class = RestaurantLocationCreateForm template_name = 'restaurants/form.html' success_url = "/restaurants/" def form_valid(self, form): instance = form.save(commit=False) instance.owner = self.request.user return super(RestaurantCreateView, self).form_valid(form) ``` - edit `urls.py` ```markdown! from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_createview, restaurant_listview, RestaurantListView, RestaurantDetailView, RestaurantCreateView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), path('restaurants/create', RestaurantCreateView.as_view()), path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` #### 10. Login required to view - edit `views.py` ```markdown! from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView, CreateView from .forms import RestaurantCreateForm, RestaurantLocationCreateForm from .models import RestaurantLocation @login_required() def restaurant_createview(request): form = RestaurantLocationCreateForm(request.POST or None) errors = None if form.is_valid(): if request.user.is_authenticated: instance = form.save(commit=False) # Customize # like a pre save instance.owner = request.user instance.save() #like a post save return HttpResponseRedirect("/restaurants/") else: return HttpResponseRedirect("/login/") if form.errors: errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user class RestaurantCreateView(LoginRequiredMixin, CreateView): form_class = RestaurantLocationCreateForm login_url = '/login/' template_name = 'restaurants/form.html' success_url = "/restaurants/" def form_valid(self, form): instance = form.save(commit=False) instance.owner = self.request.user return super(RestaurantCreateView, self).form_valid(form) ``` - edit `urls.py` ```markdown! from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from restaurants.views import ( restaurant_createview, restaurant_listview, RestaurantListView, RestaurantDetailView, RestaurantCreateView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path('restaurants/', RestaurantListView.as_view()), path('restaurants/create', RestaurantCreateView.as_view() ), #path('restaurants/create', restaurant_createview ),# path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - edit `base.py` cari `ROOT_URLCONF = 'cfehome.urls'` lalu tambahkan `LOGIN_URL = '/login/'` ```markdown! ROOT_URLCONF = 'cfehome.urls' LOGIN_URL = '/login/' ``` - hasil ketika browse lewat mode ignito ![image](https://hackmd.io/_uploads/rJLyk2c3yg.png) #### 11. Login View - edit `views.py` ```markdown! from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView, CreateView from .forms import RestaurantCreateForm, RestaurantLocationCreateForm from .models import RestaurantLocation @login_required() def restaurant_createview(request): form = RestaurantLocationCreateForm(request.POST or None) errors = None if form.is_valid(): if request.user.is_authenticated: instance = form.save(commit=False) # Customize # like a pre save instance.owner = request.user instance.save() #like a post save return HttpResponseRedirect("/restaurants/") else: return HttpResponseRedirect("/login/") if form.errors: errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user class RestaurantCreateView(LoginRequiredMixin, CreateView): form_class = RestaurantLocationCreateForm login_url = '/login/' template_name = 'restaurants/form.html' success_url = "/restaurants/" def form_valid(self, form): instance = form.save(commit=False) instance.owner = self.request.user return super(RestaurantCreateView, self).form_valid(form) ``` - edit `urls.py` ```markdown! from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from django.contrib.auth.views import LoginView from restaurants.views import ( restaurant_createview, restaurant_listview, RestaurantListView, RestaurantDetailView, RestaurantCreateView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html')), path("login/", LoginView.as_view(), name='login'), path('restaurants/', RestaurantListView.as_view()), path('restaurants/create', RestaurantCreateView.as_view() ), #path('restaurants/create', restaurant_createview ),# path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html')), path('contact/', TemplateView.as_view(template_name='contact.html')), ] ``` - buat folder registration ```markdown! mkdir /src/templates/registration && cd /src/templates/registration ``` - buat file baru `login.html` didalam direktori registration ```markdown! code login.html ``` - isi `login.html` ```markdown! {% extends "base.html" %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} {% if next %} {% if user.is_authenticated %} <p>Your account doesn't have access to this page. To proceed, please login with an account that has access.</p> {% else %} <p>Please login to see this page.</p> {% endif %} {% endif %} <form method="post" action="{% url 'login' %}"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="login"> <input type="hidden" name="next" value="{{ next }}"> </form> {# Assumes you set up the password_reset view in your URLconf #} {% endblock %} ``` - hasil di ignito ![image](https://hackmd.io/_uploads/rJR0a2qh1g.png) #### 12. Using Reverse to Shortcut URLS - edit `nav.html` ```markdown! <div class='container'> <h1>Mendoan.com</h1> <a href='{% url "home" %}'>Home</a> <a href='{% url "about" %}'>About</a> <a href='{% url "contact" %}'>Contact</a> <a href='{% url "restaurants" %}'>Restaurants</a> </div> ``` - edit `urls.html` ```markdown! from django.contrib import admin from django.urls import path, re_path from django.views.generic import TemplateView from django.contrib.auth.views import LoginView from restaurants.views import ( restaurant_createview, restaurant_listview, RestaurantListView, RestaurantDetailView, RestaurantCreateView ) urlpatterns = [ path('admin/', admin.site.urls), path('', TemplateView.as_view(template_name='home.html'), name='home'), path("login/", LoginView.as_view(), name='login'), path('restaurants/', RestaurantListView.as_view(), name='restaurants'), path('restaurants/create', RestaurantCreateView.as_view(), name='restaurants-create'), #path('restaurants/create', restaurant_createview ),# path('restaurants/<slug:slug>/', RestaurantDetailView.as_view(), name='restaurant-detail'), # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()), path('about/', TemplateView.as_view(template_name='about.html'), name='about'), path('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'), ] ``` - edit `restaurantlocation_list.html` ```markdown! {% extends "base.html" %} {% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %} {% block content %} <h1>Restaurant List</h1> <ul> {% for obj in object_list %} <li><a href='{{ obj.get_absolute_url }}'>{{ obj }}</a><br/> {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li> {% endfor %} </ul> {% endblock %} ``` - edit `views.py` ```markdown! from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Q from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import render, get_object_or_404 from django.views import View from django.views.generic import TemplateView, ListView, DetailView, CreateView from .forms import RestaurantCreateForm, RestaurantLocationCreateForm from .models import RestaurantLocation @login_required() def restaurant_createview(request): form = RestaurantLocationCreateForm(request.POST or None) errors = None if form.is_valid(): if request.user.is_authenticated: instance = form.save(commit=False) # Customize # like a pre save instance.owner = request.user instance.save() #like a post save return HttpResponseRedirect("/restaurants/") else: return HttpResponseRedirect("/login/") if form.errors: errors = form.errors template_name = 'restaurants/form.html' context = {"form": form, "errors":errors} return render(request, template_name, context) def restaurant_listview(request): template_name = 'restaurants/restaurants_list.html' queryset = RestaurantLocation.objects.all() context = { "object_list": queryset } return render(request, template_name, context) def restaurant_detailview(request, slug): template_name = 'restaurants/restaurantslocation_detail.html' obj = RestaurantLocation.objects.get(slug=slug) context = { "object": obj } return render(request, template_name, context) class RestaurantListView(ListView): def get_queryset(self): slug = self.kwargs.get("slug") if slug: queryset = RestaurantLocation.objects.filter( Q(category__iexact=slug) | Q(category__icontains=slug) ) else: queryset = RestaurantLocation.objects.all() return queryset class RestaurantDetailView(DetailView): queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user class RestaurantCreateView(LoginRequiredMixin, CreateView): form_class = RestaurantLocationCreateForm login_url = '/login/' template_name = 'restaurants/form.html' #success_url = "/restaurants/" def form_valid(self, form): instance = form.save(commit=False) instance.owner = self.request.user return super(RestaurantCreateView, self).form_valid(form) ``` - edit `models.py` ```markdown! from django.conf import settings from django.db import models from django.db.models.signals import pre_save, post_save from django.urls import reverse from .utils import unique_slug_generator from .validators import validate_category User = settings.AUTH_USER_MODEL class RestaurantLocation(models.Model): owner = models.ForeignKey(User, on_delete=models.CASCADE) # class_instance.model_set.all() name = models.CharField(max_length=120) location = models.CharField(max_length=120, null=True, blank=True) category = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category]) timestamp = models.DateTimeField(auto_now_add=True) update = models.DateTimeField(auto_now_add=True) slug = models.SlugField(unique=True, null=True, blank=True) def __str__(self): return self.name def get_absolute_url(self): # return f"/restaurants/{self.slug}" return reverse('restaurant-detail', kwargs={'slug': self.slug}) @property def title(self): return self.name #obj.title def rl_pre_save_receiver(sender, instance, *args, **kwargs): instance.category = instance.category.capitalize() if not instance.slug: instance.slug = unique_slug_generator(instance) # def rl_post_save_receiver(sender, instance, created, *args, **kwargs): # print('saved') # print(instance.timestamp) # if not instance.slug: # instance.slug = unique_slug_generator(instance) # instance.save() pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation) # post_save.connect(rl_post_save_receiver, sender=RestaurantLocation) ``` - hasil ![image](https://hackmd.io/_uploads/B1bnFgonJg.png)