---
# System prepended metadata

title: Django 1.11 | Python Web Development

---

---
title: HackMD Dark Theme
tags: theme
description: Use `{%hackmd theme-dark %}` syntax to include this theme.
---

<style>

html, body, .ui-content {
    background-color: #333;
    color: #ddd;
}

.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
    color: #ddd;
}

.markdown-body h1,
.markdown-body h2 {
    border-bottom-color: #ffffff69;
}

.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
    color: #fff;
}

.markdown-body img {
    background-color: transparent;
}

.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
    color: white;
    border-left: 2px solid white;
}

.expand-toggle:hover, 
.expand-toggle:focus, 
.back-to-top:hover, 
.back-to-top:focus, 
.go-to-bottom:hover, 
.go-to-bottom:focus {
    color: white;
}


.ui-toc-dropdown {
    background-color: #333;
}

.ui-toc-label.btn {
    background-color: #191919;
    color: white;
}

.ui-toc-dropdown .nav>li>a:focus, 
.ui-toc-dropdown .nav>li>a:hover {
    color: white;
    border-left: 1px solid white;
}

.markdown-body blockquote {
    color: #bcbcbc;
}

.markdown-body table tr {
    background-color: #5f5f5f;
}

.markdown-body table tr:nth-child(2n) {
    background-color: #4f4f4f;
}

.markdown-body code,
.markdown-body tt {
    color: #eee;
    background-color: rgba(230, 230, 230, 0.36);
}

a,
.open-files-container li.selected a {
    color: #5EB7E0;
}

</style>
Django 1.11 | Python Web Development

# 1. Welcome to Try Django 1.11

**Django**
![image](https://hackmd.io/_uploads/rk3lN0YA6.png)
Django 1.11 adalah versi spesifik dari framework web Django. Django adalah sebuah framework web open-
yang ditulis dalam bahasa pemrograman Python. Ini menyediakan seperangkat alat dan fitur yang kuat untuk membangun aplikasi web dengan cepat dan efisien.

**Django Cycle Time**
![image](https://hackmd.io/_uploads/SJFpNRtR6.png)
Framework Django bisa dibilang sudah cukup senior, dikarenakan pertama rilis pada tahun 2003 oleh 2 orang web developer yaitu Adrian Holovaty dan Simon Willison. 

**Kenapa harus belajar Django Framework?**
1. Komplit dan Proses coding cepat
Django framework adalah kerangka kerja yang punya banyak library, API, dan modul. Apapun kebutuhan 
, baik sekadar membuat website sederhana sampai aplikasi web berfitur kompleks, semuanya bisa dilayani oleh Django.
Django ditulis menggunakan bahasa pemrograman Python. Otomatis, semua fitur yang ada di Python juga bisa dipasang di Django framework tanpa ada masalah kompatibilitas sama sekali. Itulah mengapa, Django disebut sebagai framework yang komplit. Django juga menggunakan arsitektur MTV(Model, Template, View) yang membantu proses menulis coding Anda. Sebab, MTV menyediakan template script bawaan sekaligus merapikan struktur website.

2. Keamanan Terjaga
Framework Django dibelaki dengan perlindungan yang sangat baik, dimana jika ada kesalahan dalam codingan, biasanya yang tersimpan adalah salinanannya bukan aslinya. Sementara data aslinya masih berada didalam Database
![image](https://hackmd.io/_uploads/HJia8CtRa.png)
Juga ada sistem keamanan Cryptographic juga dimana mengubah password menjadi karakter acak yang sulit dimengerti sehingga sulit dibobol.

3. Mudah untuk dirawat dan di kembangkan
Django menerapkan prinsip DRY atau Don’t Repeat Yourself. Artinya, Anda cukup menulis satu atau beberapa baris kode untuk membuat sebuah perintah. Lalu, perintah tersebut bisa diterapkan di bagian website yang lain.
Dengan begitu, Anda terhindar dari menulis kode yang sama berulang-ulang. Dan lagi, hasil akhir coding jadi semakin ringkas. Sehingga, website Django lebih mudah dirawat dan diperbaiki apabila terjadi error sewaktu-waktu.
![image](https://hackmd.io/_uploads/HkbiPRY0p.png)
Selain DRY, Django framework juga memakai prinsip modular di arsitekturnya. Lebih jelasnya, setiap komponen bersifat independen dan dapat dibongkar pasang tanpa menyebabkan error pada komponen lain.

4. Portabel dan Fleksibel
Dikarenakan Django berbasis Python, maka otomatis Django bisa dijalankan di berbagai platform. Django juga identik dipakai untuk membuat website dan bisa ditempatkan di server hosting baik Linux, Windows ataupun MacOs.
Django juga sangat fleksibel dikarenakan semua jenis website bisa dibangun dengan framework Django seperti CMS (Content Management System), Website berita, Sosial Media bahkan Ensiklopedia online. Dan juga bisa menyesuaikan kedalam beberapa format yang dapat menampilkan konten website seperti; HTML, JSON, XML, dan RSS Feeds.

# 2. Getting Started with Django
**1. Install Django**
Alat yang kita butuhkan untuk membuat proyek Django adalah:

* Python
* Virtualenv
* Pip

**Python** adalah bahasa yang akan menjadi dasar dari Django yang akan diinstal nantinya.

**Virtualenv** adalah alat yang berfungsi untuk membuat lingkungan virtual terisolasi. Kita harus menggunakan virtualenv karena tidak akan bentrok dengan proyek lain.

**Pip** adalah alat yang akan kita gunakan untuk manajemen paket Python. Ini juga termasuk menginstal Django.

**1. Install Python**
![Screenshot (1207)](https://hackmd.io/_uploads/By9YikcCp.png)

**2. Memastikan python sudah diinstal dan cek versinya**
![Screenshot (1208)](https://hackmd.io/_uploads/HJyJnk5CT.png)

**3. Install Django 5.0.3**
![Screenshot (1209)](https://hackmd.io/_uploads/rJhW2J5CT.png)

**4. Upgrade paket Pip Python**
![Screenshot (1210)](https://hackmd.io/_uploads/B1qEnk5RT.png)

**5. Buat direktori project**
![Screenshot (1211)](https://hackmd.io/_uploads/rkQ-T1qCT.png)

**6. Buat nama myproject untuk project Django** 
![Screenshot (1212)](https://hackmd.io/_uploads/SJm5py9A6.png)

**7. Pindah ke direktori myproject dan jalankan server Django**
![Screenshot (1213)](https://hackmd.io/_uploads/HyG10J5AT.png)

**8. Akses server Django dengan http://127.0.0.1:8000/**
![Screenshot (1214)](https://hackmd.io/_uploads/HJbZ0ycRT.png)

**Install Django 1.11 di Ubuntu**

**1. Install semua tools dan dependencies yang dibutuhkan**
sudo apt install python3.8 python3-virtualenv python3.8-venv python3-pip libreadline-gplv2-dev libncursesw5-dev libpq-dev python3-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev
![Screenshot (1226)](https://hackmd.io/_uploads/ryGtmsq0T.png)

**2. Pastikan Python dan tools sudah terinsall**
which python3
which pip
![Screenshot (1227)](https://hackmd.io/_uploads/Sk2iQoqCT.png)

**3. cek python package**
pip list
![Screenshot (1228)](https://hackmd.io/_uploads/ByY-4j90p.png)

**4. Buat direktori Development untuk Virtual Env**
mkdir Dev && cd Dev
![Screenshot (1229)](https://hackmd.io/_uploads/By2KVo9Ra.png)

**5. Buat Virtual Env**
python3 -m venv trydjango-1.11
![Screenshot (1230)](https://hackmd.io/_uploads/BJdxBo50T.png)

**6. Mengaktifkan Virtual Env yang sebelumnya sudah dibuat**
cd trydjango-1.11/
source bin/activate
![Screenshot (1231)](https://hackmd.io/_uploads/SyA4Soq0a.png)

**7. Cek Python package didalam Virtual Env**
pip list
![Screenshot (1233)](https://hackmd.io/_uploads/BJldBoq0p.png)

**8. Install Django 1.11**
pip install django==1.11.*
![Screenshot (1250)](https://hackmd.io/_uploads/ByYhGT50T.png)

**9. Install Wheel (Format Distribusi Python) jika belum terinstall (opsional)**
pip install wheel
![Screenshot (1235)](https://hackmd.io/_uploads/Hk2u8o5CT.png)

**10. Buat project baru yaitu mywebsite didalam src direktori**
mkdir src && cd src
django-admin startproject mywebsite .
![Screenshot (1244)](https://hackmd.io/_uploads/rkALShqAa.png)

Perintah **startproject** digunakan untuk membuat sebuah proyek
**mywebsite** adalah nama direktori proyek.

**11. cek semua project file dengan menggunakan tree**
(kamu bisa install tree jika belum install)
![Screenshot (1237)](https://hackmd.io/_uploads/SkKYvo9CT.png)

**2. Struktur Direktori Django**
**mywebsite/** adalah direktori utama yang berisi semua file dari proyek. Nama direktori ini dapat diganti dengan apa pun, karena tidak akan menjadi masalah bagi Django.

**manage.py** adalah program untuk mengelola proyek Django. Kita sering menjalankan manage.py ketika kita ingin melakukan sesuatu dengan proyek, misalnya: menjalankan server, melakukan migrasi, membuat sesuatu, dll.

**mywebsite/init.py** adalah file kosong yang menyatakan bahwa direktori ini adalah sebuah paket Python.

**mywebsite/settings.py** adalah tempat kita mengonfigurasi proyek.

**mywebsite/urls.py** adalah tempat kita mendeklarasikan URL.

**mywebsite/wsgi.py** adalah titik masuk untuk yang kompatibel dengan WSGI.

**1. Buat direktori baru didalam direktori mywebsite**
mkdir mywebsite/settings/
![Screenshot (1238)](https://hackmd.io/_uploads/B1vFticCa.png)

**2. Buat 4 baru didalam settings direktori**
touch mywebsite/settings/__ini__.py
touch mywebsite/settings/base.py
touch mywebsite/settings/production.py
![Screenshot (1239)](https://hackmd.io/_uploads/BkPCYsqRa.png)

**3. Copy mywebsite/settings.py file to mywebsite/settings/base.py file**
cp mywebsite/settings.py mywebsite/settings/base.py
![Screenshot (1245)](https://hackmd.io/_uploads/S17AB2506.png)

**4. Edit file base.py (pastikan isi file sesuai dgn ini)**
`BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))`
![Screenshot (1246)](https://hackmd.io/_uploads/BJUIL2q0a.png)

**5. Edit mywebsite/settings/__init__.py file**
```
from .base import *
from .production import *

try:
   from .local import *
except:
   pass
```
![Screenshot (1247)](https://hackmd.io/_uploads/HJ36Lhc0p.png)

**6. Install beberapa instalasi yang lain**
pip install psycopg2 
pip install gunicorn
pip install dj-database-url==0.4.2
pip install django-crispy-forms==1.6.1
pip install pillow
![Screenshot (1248)](https://hackmd.io/_uploads/SyJVw39CT.png)

**7. Buat requirement.txt di direktori src**
pip freeze > requirements.txt
![Screenshot (1252)](https://hackmd.io/_uploads/HybkS6506.png)

**8. Jalankan migration & createsuperuser**
python manage.py migrate
python manage.py createsuperuser
![Screenshot (1251)](https://hackmd.io/_uploads/r1LfrT9A6.png)

**9. Jalankan server**
python manage.py runserver
![Screenshot (1253)](https://hackmd.io/_uploads/rJ37Iac0a.png)
"Akses menggunakan browser ubuntu dengan 127.0.0.1:8000"

**10. Copy base.py ke local.py dan production.py di dalam direktori settinngs**
![Screenshot (1254)](https://hackmd.io/_uploads/r19RIT9R6.png)

**11. Edit file production.py**
(pastikan sesuai dengan ini)
```
DEBUG = False
SECRET_KEY = "add random letter or number"
```
![Screenshot (1255)](https://hackmd.io/_uploads/ryFJWej0a.png)

**12. Start apps pertama**
python manage.py startapp restaurants
tree restaurants/
![Screenshot (1256)](https://hackmd.io/_uploads/HJxKyC5C6.png)

**3. Struktur Direktori Aplikasi Django**
File **init__.py** memberitahu Python untuk memperlakukan direktori tersebut sebagai paket Python.
**admin.py** berisi pengaturan untuk halaman admin Django.
**apps.py** berisi pengaturan untuk konfigurasi aplikasi.
**models.py** berisi serangkaian kelas yang diubah oleh ORM Django menjadi tabel database.
**test.py** berisi kelas-kelas pengujian.
**views.py** berisi fungsi dan kelas yang menangani data yang ditampilkan dalam template HTML.
Folder migrations/ untuk menyimpan file migrasi dan model untuk menulis logika bisnis.

**1. Edit views.py didalam direktori restaurants**
```
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	return HttpResponse("hello")
```
![Screenshot (1257)](https://hackmd.io/_uploads/Bk2rfgsRT.png)

**2. Edit urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import home

urlpatterns = [
	url(r'^admin/', admin.site.urls),
	url(r'^$', home),
]
```
![Screenshot (1258)](https://hackmd.io/_uploads/rkbaGgoCa.png)

**3. Akses server dengan browser ubuntu**
python manage.py runserver
![Screenshot (1259)](https://hackmd.io/_uploads/BJ1TQlo0p.png)

# HTML & Django

**1. Rendering with HTML**
Dalam Django, view bertanggung jawab menghasilkan halaman web berdasarkan URL yang diminta. Kita bisa mengirimkan lebih dari sekadar teks sederhana, seperti "hello", dengan mengirimkan kode HTML. Kode HTML ini merupakan instruksi untuk browser tentang bagaimana cara menampilkan halaman web. Penggunaan template sangat disarankan untuk memisahkan logika tampilan dari logika bisnis. Dengan template, kita bisa membuat halaman web yang lebih dinamis dan mudah dikelola, tanpa perlu khawatir tentang bagaimana cara membuat tampilannya. Ini memungkinkan kita fokus pada pengembangan fitur dan fungsionalitas aplikasi. Jadi, selanjutnya kita akan belajar bagaimana menggunakan template dalam view untuk membuat tampilan yang lebih kompleks.

**1. Edits restaurant/views.py**
```
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	html_ =  """<!DOCTYPE html>
    <html lang=en>

    <head>
    <head/>
    <body>
    <h1>Hello world</h1>
    <p>This is html coming trough</p>
    </body>
    </html>
    """
	return HttpResponse(html_)
```
![Screenshot (1260)](https://hackmd.io/_uploads/BJg9sbiC6.png)

**2. Akses server dengan browser ubuntu**
python manage.py runserver
![Screenshot (1261)](https://hackmd.io/_uploads/HykTjbo06.png)

**Rendering HTML with Python Variable (F string)**
**1. Edit restaurants/views.py**
```
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	html_var = 'f strings'
	html_ =  f"""<!DOCTYPE html>
    <html lang=en>

    <head>
    <head/>
    <body>
    <h1>Hello world</h1>
    <p>This is {html_var} coming trough</p>
    </body>
    </html>
    """
	return HttpResponse(html_)
```
**2. Buka server pada browser ubuntu**
![Screenshot (1263)](https://hackmd.io/_uploads/ry-ibc2RT.png)

**2. Render a Django Template**
saat kita ingin menampilkan halaman web dengan Django, kita menggunakan template. Dalam template, kita bisa menggunakan variabel untuk menyertakan data dari view ke halaman web. Kita juga bisa menggunakan kondisional untuk menampilkan atau menyembunyikan bagian tertentu berdasarkan kondisi, misalnya menampilkan atau menyembunyikan pesan selamat datang jika pengguna sudah login.

**1. Buat direktori baru dan HTML file**
mkdir templates/
touch templates/base.html
![Screenshot (1269)](https://hackmd.io/_uploads/BkPaCF3Ca.png)

**2. Edit file templates/base.html**
cd templates
sudo nano base.html
```
<!DOCTYPE html>
<html lang=en>

<head>
<head/>
<body>
<h1>
    Hello world
</h1>
<p>
    This is {html_var} coming trough
</p>
</body>
</html>
```
![Screenshot (1265)](https://hackmd.io/_uploads/SkdRZqhC6.png)

**3. Edit file base.py didalam direktori settings**
```
TEMPLATES = [
	'DIRS' : [os.path.join(BASE_DIR,  'templates')],
```
![Screenshot (1266)](https://hackmd.io/_uploads/BkSP5Y20p.png)

**4. Edit file views.py didalam direktori restaurant**
```
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	return render(request,  "base.html", {})
```
![Screenshot (1267)](https://hackmd.io/_uploads/BkhgitnRp.png)

**5. Edit file __init__.py didalam direktori mywebsite/settings**
```
from .base import *

#from .production import *

#try:
#from .local import *
#except:
#pass
```
![Screenshot (1268)](https://hackmd.io/_uploads/ByG_sYn06.png)

**6. Akses server dengan browser ubuntu**
![Screenshot (1270)](https://hackmd.io/_uploads/r1-lk52Cp.png)

**Rendering HTML templates dengan Python variable**
**1. Edit file views.py didalam direktori restaurant**
```
import random
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	num = random.randint(0, 100000000)
	return render(request,  "base.html", {"html_var": True, "num": num})
```
![Screenshot (1271)](https://hackmd.io/_uploads/SJ21xc20T.png)

**2. Edit file templates/base.html**
```
<!DOCTYPE html>
<html lang=en>

<head>
<head/>
<body>
<h1>
    Hello world
</h1>
<p>
    This is {{ html_var }} coming trough
</p>
<p>
    Random number is {{ num }}
</p>
</body>
</html>
```
![Screenshot (1272)](https://hackmd.io/_uploads/BJ_oe9h0a.png)

**3. Akses server dengan browser ubuntu**
![Screenshot (1273)](https://hackmd.io/_uploads/B1tAxqn0p.png)

**3. Using context in Django Templates**
Penggunaan konteks dalam template Django memungkinkan kita untuk menyertakan data dari view ke dalam halaman web. Ini memungkinkan kita untuk menampilkan informasi yang dinamis kepada pengguna, seperti judul artikel, daftar produk, atau pesan selamat datang.

Konteks dapat berisi variabel-variabel yang didefinisikan dalam view dan kemudian diakses dalam template untuk ditampilkan kepada pengguna.

Dengan menggunakan konteks, kita dapat membuat halaman web yang dinamis dan responsif terhadap perubahan data dalam aplikasi.

**1. Edit file views.py didalam direktori restaurant**
```
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 = {
		"num": num,
		"some_list": some_list
	}
	return render(request,  "base.html", context)
```
![Screenshot (1274)](https://hackmd.io/_uploads/HkmqQ5h0p.png)

**2. Edit file templates/base.html**
```
<!DOCTYPE html>
<html lang=en>

<head>
<head/>
<body>
<h1>
    Hello world
</h1>
<p>
    This is <code>{% verbatim %}{{ html_var }}{% endverbatim %}</code> coming trough
</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>
```
![Screenshot (1275)](https://hackmd.io/_uploads/Sy_xNcn06.png)

**3. Akses server dengan browser ubuntu dengan (if condition_bool_item is False)**
![Screenshot (1276)](https://hackmd.io/_uploads/SyaWr5hRp.png)

**4. Akses server dengan browser ubuntu dengan (if condition_bool_item is True)**
![Screenshot (1277)](https://hackmd.io/_uploads/H1J7S53RT.png)

**4. Using template inheritance**
Template inheritance adalah konsep dalam pengembangan web di mana kita dapat membuat template "induk" yang berisi elemen-elemen umum, seperti header, footer, atau navigasi. Kemudian, kita dapat membuat template "anak" yang mengambil bagian dari template induk dan menambahkan konten unik di setiap halaman.

Dengan menggunakan template inheritance, kita dapat menghindari pengulangan kode yang tidak perlu, membuat struktur template yang lebih bersih, dan memudahkan perubahan di masa depan.

**1. Buat file baru didalam direktori templates**
touch home.html
touch about.html
touch contact.html
![Screenshot (1278)](https://hackmd.io/_uploads/S11kw5hRp.png)

**2. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import home, about, contact

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', home),
    url(r'^about/$', about),
    url(r'^contact/$', contact),
]
```
![Screenshot (1279)](https://hackmd.io/_uploads/HkomDqnRp.png)

**3. Edit file views.py didalam direktori restaurants**
```
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 = {
        "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)
```
![Screenshot (1280)](https://hackmd.io/_uploads/BkTov5nCa.png)

**4. Edit file templates/base.html**
```
<!DOCTYPE html>
<html lang=en>

<head>
<title>{% block head_tittle %}Nicholai Dandy Nainggolan.org{% endblock head_tittle %}</title>
<head>
<body>
<h1>Nicholai Dandy Nainggolan.org</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>
```
![Screenshot (1286)](https://hackmd.io/_uploads/Bk-AF93C6.png)

**5. Edit file templates/home.html**
```
{% extends "base.html" %}
{% block content %}

<h1>
    Hello world
</h1>
<h3>
    {{ block.super }}
</h3>
<p>
    This is <code>{% verbatim %}{{ html_var }}{% endverbatim %}</code> coming trough
</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 %}
```
![Screenshot (1282)](https://hackmd.io/_uploads/rkLh_530a.png)

**6. Edit file templates/about.html**
```
{% extends "base.html" %}
{% block head_tittle %}About || {{ block.super }}{% endblock head_tittle %}

{% block content %}
<p>About</p>
{% endblock %}
```
![Screenshot (1283)](https://hackmd.io/_uploads/SJAgKqnCT.png)

**7. Edit file templates/contact.html **
```
{% extends "base.html" %}
{% block head_tittle %}Contact || {{ block.super }}{% endblock head_tittle %}

{% block content %}
<p>Contact</p>
{% endblock %}
```
![Screenshot (1284)](https://hackmd.io/_uploads/Skf4K9hAT.png)

**8. Akses server dengan browser ubuntu dan buka Home, About & Contact**
![Screenshot (1289)](https://hackmd.io/_uploads/rJUm9chRp.png)
![Screenshot (1287)](https://hackmd.io/_uploads/B1Um5qnAp.png)
![Screenshot (1288)](https://hackmd.io/_uploads/S1L7q9206.png)

# Include Template Tag
Include template tag adalah fitur yang memungkinkan kita untuk membagi-bagi template situs web menjadi bagian-bagian yang lebih kecil dan menyertakannya di dalam template utama.

**1. Buat direktori baru dan buat file didalam direktori templates**
mkdir snippets
touch nav.html
touch css.html
touch js.html
touch sidebar.html
![Screenshot (1305)](https://hackmd.io/_uploads/BktwBjp0p.png)

**2. Edit file templates/base.html**
```
<!DOCTYPE html>
<html lang=en>

<head>
<title>{% block head_tittle %}Nicholai Dandy Nainggolan.org{% endblock head_tittle %}</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>
```
![Screenshot (1296)](https://hackmd.io/_uploads/ryMN1o6C6.png)

**3. Edit file nav.html didalam direktori snippets**
```
<div class="container">
<h1>Nicholai Dandy Nainggolan.org</h1>
<a href="/">Home</a>
<a href="/about/">About</a>
<a href="/contact/">Contact</a>
</div>
```
![Screenshot (1297)](https://hackmd.io/_uploads/SyjsyipAa.png)

**4. Edit file css.html didalam direktori snippets ([Copy dari bootstrap cdn 3.3.7](https://blog.getbootstrap.com/2016/07/25/bootstrap-3-3-7-released/))**
```
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
```
![Screenshot (1298)](https://hackmd.io/_uploads/ryPexo6C6.png)

**5. Edit file js.html didalam direktori snippets ([Copy dari jquery core 3.2.1(minified)](https://code.jquery.com/jquery-3.2.1.min.js))**
```
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
```
![Screenshot (1299)](https://hackmd.io/_uploads/SyEf-sa0T.png)

**6. Edit file sidebar.html didalam direktori snippets**
![Screenshot (1300)](https://hackmd.io/_uploads/HkVOZo6RT.png)

**7. Tambahkan tag pada about.html dan contact.html**
```
{% extends "base.html" %}
{% block head_tittle %}About || {{ block.super }}{% endblock head_tittle %}

{% block content %}
<h1>About</h1>
{% include 'snippets/sidebar.html' %}
{% endblock %}
```
![Screenshot (1302)](https://hackmd.io/_uploads/SJAofs60p.png)

```
{% extends "base.html" %}
{% block head_tittle %}Contact || {{ block.super }}{% endblock head_tittle %}

{% block content %}
<h1>Contact</h1>
{% include 'snippets/sidebar.html' %}
{% endblock %}
```
![Screenshot (1303)](https://hackmd.io/_uploads/H1YRfjaCp.png)

8. Akses server dengan browser ubuntu dan lihat include tag pada about & contact
![Screenshot (1308)](https://hackmd.io/_uploads/S1ZRuo60a.png)
![Screenshot (1306)](https://hackmd.io/_uploads/rJbR_jpCa.png)
![Screenshot (1307)](https://hackmd.io/_uploads/By-0_jTRa.png)

# Class Based Views
Class-based views adalah cara yang lebih canggih untuk menangani URL dalam pengembangan situs web. Dengan class-based views, kita dapat menghindari pengulangan kode dan bekerja secara lebih efisien.

1. Edit file views.py didalam direktori restaurants
```
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 = {
        "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)
```
![Screenshot (1309)](https://hackmd.io/_uploads/rJCtP3TAp.png)
print(kwargs) akan memunculkan ID yang digunakan untuk membuka contact di terminal

**2. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import home, about, contact, ContactView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', home),
    url(r'^about/$', about),
    url(r'^contact/(?P<id>\d+)/$', ContactView.as_view()),
]
```
![Screenshot (1310)](https://hackmd.io/_uploads/ByMlunT0T.png)

3. Akses server dengan browser ubuntu
Jika kita mengakses tanpa ID
![Screenshot (1311)](https://hackmd.io/_uploads/SJRmqhT0T.png)

Jika kita mengakses dengan ID
![Screenshot (1312)](https://hackmd.io/_uploads/S1wr52aCa.png)

Didalam terminal, kita dapat melihat ID yang kita gunakan
![Screenshot (1313)](https://hackmd.io/_uploads/BJVu9n60p.png)

# Template View
Template view adalah cara untuk dengan mudah menentukan template yang akan digunakan untuk halaman web dalam pengembangan situs. Dengan menggunakan Template View, pengembangan situs web menjadi lebih efisien karena kita dapat dengan mudah menentukan template yang akan digunakan untuk setiap halaman tanpa perlu menulis ulang kode yang sama berulang kali.

Kita dapat menggunakan 2 metode disini, Metode yang pertama;

**1. Edit file views.py didalam direktori restaurant**
```
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 = False
        if condition_bool_item:
            num = random.randint(0, 100000000)
        context = {
            "num": num,
            "some_list": some_list
        }
        return context

class AboutView(TemplateView):
    template_name = 'about.html'

class ContactView(TemplateView):
    template_name = 'contact.html'
```
![Screenshot (1314)](https://hackmd.io/_uploads/rJiZn36R6.png)

**2. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import HomeView, AboutView, ContactView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', HomeView.as_view()),
    url(r'^about/$', AboutView.as_view()),
    url(r'^contact/$', ContactView.as_view()),
]
```
![Screenshot (1315)](https://hackmd.io/_uploads/H1IFn2T0T.png)

Metode kedua;

**1. Edit file views.py didalam direktori restaurants**
```
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 = False
        if condition_bool_item:
            num = random.randint(0, 100000000)
        context = {
            "num": num,
            "some_list": some_list
        }
        return context
```
![Screenshot (1316)](https://hackmd.io/_uploads/SJvjThaCp.png)

**2. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import HomeView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', HomeView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![Screenshot (1317)](https://hackmd.io/_uploads/HyoMR26Ra.png)

# 3. Remembering Things

# Remembering things with Models

**1. Buat superuser baru**
python manage.py createsuperuser
![Screenshot (1318)](https://hackmd.io/_uploads/BJlTEpaRT.png)

**2. Akses ke Django Admin dengan browser ubuntu**
Akses ke 127.0.0.1:8000/admin/ dengan superuser baru yang dibuat tadi
![Screenshot (1319)](https://hackmd.io/_uploads/HyhESTaC6.png)
![Screenshot (1320)](https://hackmd.io/_uploads/SyX_BTTAp.png)

Pada bagian Pengguna, data disimpan dalam file db.sqlite3. File ini dibuat ketika kita mulai menjalankan perintah migrasi. Selanjutnya, kita akan mencoba untuk menampilkan daftar restoran favorit kita dengan mengedit models.py pada direktori restaurants.

**3. Edit file base.py didalam direktori settings**
Pastikan isi file sesuai seperti dibawah
```
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'restaurants',
]
```
![Screenshot (1321)](https://hackmd.io/_uploads/Bk5cIaaAa.png)

**4. Edit file models.py didalam direktori restaurants**
```
from django.db import models

class Restaurant(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
```
![Screenshot (1322)](https://hackmd.io/_uploads/B1dQw66A6.png)

**5. Migrasi model restaurants**
python manage.py makemigrations
python manage.py migrate
![Screenshot (1323)](https://hackmd.io/_uploads/H1V6D6606.png)

**6. Edit file admin.py didalam direktori restaurants**
```
from django.contrib import admin
from .models import Restaurant

admin.site.register(Restaurant)
```
![Screenshot (1324)](https://hackmd.io/_uploads/HkRU_pp0p.png)

**7. Cek di browser ubuntu**
![Screenshot (1325)](https://hackmd.io/_uploads/ryxiu66Ra.png)

**8. Ubah nama model**
Edit nama model di file models.py dan admin.py
```
from django.db import models

class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
```
![Screenshot (1326)](https://hackmd.io/_uploads/rJerY6aRp.png)

```
from django.contrib import admin
from .models import RestaurantLocation

admin.site.register(RestaurantLocation)
```
![Screenshot (1327)](https://hackmd.io/_uploads/SyEOF6T0a.png)

**9. Jalankan migrasi setelah mengganti nama model**
python manage.py makemigrations
python manage.py migrate
python manage.py runservera
![Screenshot (1328)](https://hackmd.io/_uploads/B1jat6aAT.png)

**10. Cek di browser ubuntu**
![Screenshot (1330)](https://hackmd.io/_uploads/HyF-oppC6.png)
![Screenshot (1329)](https://hackmd.io/_uploads/HJtZjaaRp.png)
kamu bisa menambahkan lokasi restoran disini

# More on Model Fields

Kita bisa menambahkan field baru ke model dengan menuliskan kode di dalam file models.py. Setelah itu, kita harus menjalankan perintah makemigrations dan migrate agar perubahan tersebut diaplikasikan ke dalam database. Setelahnya, kita bisa melihat hasilnya di admin. Beberapa tipe field yang bisa digunakan antara lain CharField untuk teks, DateTimeField untuk tanggal dan waktu, serta DateField untuk tanggal saja. Field-field ini bisa disesuaikan dengan kebutuhan dan preference kita.

**1. Edit file models.py didalam direktori restaurants**
```
from django.db import models

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)
```
![Screenshot (1331)](https://hackmd.io/_uploads/SJK7z06Rp.png)

**2. Jalankan Migrasi**
```
python manage.py makemigrations
You are trying to add a non-nullable field 'timestamp' to restaurantlocation without a default; we can't do that (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 let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> timezone.now
Migrations for 'restaurants':
  restaurants/migrations/0003_auto_20240313_0626.py
    - Add field category to restaurantlocation
    - Add field timestamp to restaurantlocation
python manage.py migrate
```
![Screenshot (1332)](https://hackmd.io/_uploads/rJmxX0aAa.png)
![Screenshot (1333)](https://hackmd.io/_uploads/S18yQRpRp.png)

**3. Cek di browser ubuntu**
![Screenshot (1334)](https://hackmd.io/_uploads/Hynr7CpR6.png)

# Displaying Saved Data
Kita telah membuat sebuah view untuk menampilkan data yang disimpan dalam database. Pertama, kita membuat view berbasis fungsi yang disebut restaurant_list_view dan membuat sebuah template untuk menampilkan daftar restoran. Kemudian, kita menggunakan objek queryset dari model restaurant_location untuk mengambil data dari database dan menampilkannya dalam template. Terakhir, kita memperbarui tampilan agar lebih mudah dipahami dan menjelaskan langkah-langkah untuk menangani perubahan pada model.

**1. Edit file views.py didalam direktori restaurants**
```
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 restaurants_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)
```
![Screenshot (1335)](https://hackmd.io/_uploads/Hk2XE0TAa.png)

**2. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import restaurants_listview

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', restaurants_listview),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![Screenshot (1336)](https://hackmd.io/_uploads/r1c_NAp0a.png)

**3. Tambahkan folder baru dan file**
mkdir -p emplates/restaurants
touch restaurants_list.html
![Screenshot (1338)](https://hackmd.io/_uploads/HkTpSCTAa.png)

**4. Edit file restaurants_list.html**
```
{% extends "base.html" %}
{% block head_tittle %}Restaurants | {{ block.super }}{% endblock head_tittle %}
{% block content %}
<h1>Restaurants List</h1>

<ul>
{% for obj in object_list %}
	<li>{{ obj }} <br/>
		{{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.update }}</li>
{% endfor %}
</ul>
{% endblock %}
```
![Screenshot (1339)](https://hackmd.io/_uploads/B13EIAp0p.png)

**5. Edit file models.py didalam direktori restaurants**
```
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=True)
    update      = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name
```
![Screenshot (1341)](https://hackmd.io/_uploads/rkI0UC606.png)

**6. Cek di browser ubuntu dan akses ke 127.0.0.1:8000/restaurants dan pastikan bahwa ada data didalam restaurants admin**
![Screenshot (1343)](https://hackmd.io/_uploads/H12DzJAAT.png)

# Understanding QuerySets
Queery set adalah cara untuk mengambil data dari database. Ini adalah daftar item yang diambil dari database. Dengan menggunakan queery set, kita dapat melakukan berbagai operasi seperti filtering, pembaharuan, dan penciptaan objek baru dalam database. Dalam contoh di atas, kita menggunakan queery set untuk memfilter data berdasarkan kriteria tertentu, menghitung jumlah item, dan membuat objek baru dalam database. Penting untuk memahami cara kerja queery set karena ini adalah bagian penting dari pengembangan aplikasi Django.

**1. Akses ke Django shell**
python manage.py shell

**2. Munculkan data dari database**
![Screenshot (1344)](https://hackmd.io/_uploads/B17Tu1ACT.png)

**3. Filter and perbaharui queryset**
![Screenshot (1344.2)](https://hackmd.io/_uploads/rkEbFJRAp.png)

**4. Tambah lokasi restaurants via shell**
![Screenshot (1345)](https://hackmd.io/_uploads/rkQQt1A0p.png)

**5. Tambah lokasi restaurants dengan satu perintah**
![Screenshot (1346)](https://hackmd.io/_uploads/HkYrFyRA6.png)

**6. Filtering data with exclude**
![Screenshot (1347)](https://hackmd.io/_uploads/rJ7FtkAAT.png)

**7. Keluar dari shell**
exit() atau ctrl+D 

# Generic List View
Generic List View adalah fitur yang sangat berguna dalam pengembangan aplikasi web dengan Django. Dengan menggunakan Generic List View, kita dapat dengan mudah menampilkan daftar data dari model ke dalam tampilan tanpa harus menulis ulang kode yang sama berulang-ulang. Fitur ini memungkinkan kita untuk menghemat waktu dan usaha dalam mengembangkan aplikasi.

Dalam Generic List View, kita dapat menggunakan beberapa tampilan yang sudah disediakan oleh Django, seperti ListView, yang memungkinkan kita untuk menampilkan daftar data dari model dengan mudah. Kita hanya perlu menentukan model mana yang akan digunakan dan template mana yang akan digunakan untuk menampilkan data tersebut.

Selain itu, Generic List View juga mendukung fitur-fitur seperti filter dan pencarian data, sehingga kita dapat dengan mudah menampilkan data yang sesuai dengan kriteria tertentu. Fitur ini sangat berguna dalam mengembangkan aplikasi yang kompleks dengan banyak data.

Dengan menggunakan Generic List View, pengembang dapat mengurangi kode yang perlu ditulis dan mempercepat proses pengembangan aplikasi secara keseluruhan. Hal ini membuat pengembangan aplikasi menjadi lebih efisien dan mudah dipelajari bagi pemula sekalipun.

**1. Edit file views.py didalam direktori restaurants**
```
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 restaurants_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
```
![Screenshot (1348)](https://hackmd.io/_uploads/Hywa9JACa.png)

**2. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurants_listview,
    RestaurantListView,    
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/(?P<slug>\w+)/$', RestaurantListView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
```
![Screenshot (1349)](https://hackmd.io/_uploads/r1wwi1ACT.png)

**3. ganti name restaurant_list.html**
mv restaurants/tempalates/restaurants/restaurant_list.html restaurants/tempalates/restaurants/restaurantlocation_list.html 

# Restaurant profile detail

1. Edit file views.py didalam direktori restaurants
```
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 restaurants_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)
        return obj
```
![Screenshot (1350)](https://hackmd.io/_uploads/HJucTyC0a.png)

2. Edit file urls.py didalam direktori mywebsite
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,    
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/(?P<rest_id>\w+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![Screenshot (1351)](https://hackmd.io/_uploads/r1S-0yRAp.png)

**3. Buat file baru didalam direkotori restaurant template**
touch restaurantlocation_detail.html
![Screenshot (1352)](https://hackmd.io/_uploads/HyDoA1AA6.png)

**4. Edit file restaurantlocation_detail.html**
![Screenshot (1354)](https://hackmd.io/_uploads/BybfJlARa.png)

**5. Cek di browser ubuntu**
![Screenshot (1355)](https://hackmd.io/_uploads/ry2uJx00a.png)

# SlugField & Unique Slug Generator
Slugfield adalah bidang dalam model Django yang digunakan untuk menyimpan bagian dari URL yang unik dan mudah dibaca, yang biasanya didasarkan pada judul atau nama entitas dalam database. Unique Slug Generator adalah fungsi utilitas yang menghasilkan slug yang unik berdasarkan nama atau judul entitas. Slug ini digunakan sebagai bagian dari URL untuk mengidentifikasi entitas secara unik dalam aplikasi web. Dengan menggunakan Slugfield dan Unique Slug Generator, kita dapat membuat URL yang lebih mudah dimengerti dan diingat, serta memastikan bahwa setiap entitas dalam aplikasi memiliki identifikasi yang unik.

**1. Edit file models.py**
```
from django.db import models

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=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
```
![Screenshot (1356)](https://hackmd.io/_uploads/HyBuMgAAT.png)

**2. Migrasi model restaurant**
python manage.py makemigrations
python manage.py migrate
![Screenshot (1357)](https://hackmd.io/_uploads/Hyc0Me0AT.png)

**3. Buat file baru didalam direktori restaurants**
touch restaurants/utils.py

**4. Edit file utils.py didalam direktori restaurants**
```
import random
import string
from django.utils.text import slugify

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)

    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
```
![Screenshot (1358)](https://hackmd.io/_uploads/B18KmgCA6.png)

# Signal for Unique Slug
Signal pada Django digunakan untuk menanggapi peristiwa, seperti sebelum atau setelah objek disimpan. Dalam contoh ini, kita menggunakan signal sebelum penyimpanan (pre-save) untuk membuat slug yang unik secara otomatis berdasarkan judul atau nama objek. Hal ini memastikan setiap slug yang dibuat adalah unik. Dengan menggunakan signal, kita dapat membuat fitur tambahan dengan mudah dan terstruktur dalam aplikasi Django.

**1. Edit file models.py didalam direktori restaurants**
```
from django.db import models
from django.db.models.signals import pre_save, post_save
from .utils import unique_slug_generator

# 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=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

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)
```
![Screenshot (1359)](https://hackmd.io/_uploads/rkvzBeC06.png)

**2. Edit beberapa item di web admin Django**
![Screenshot (1360)](https://hackmd.io/_uploads/SyrASxCR6.png)
Bisa dilihat log pada terminal 
![Screenshot (1361)](https://hackmd.io/_uploads/rJzaUlART.png)

# 4. Forms, Views, Models and More

# Slugs as URL Params**
Tujuan dari semua langkah yang dilakukan adalah untuk membuat URL menjadi lebih kuat dan lebih terstruktur, serta meningkatkan pengalaman pengguna dalam menggunakan aplikasi. Dengan menggunakan slug, kita dapat membuat URL yang lebih mudah dipahami dan lebih ramah pengguna. Dengan menyesuaikan URL sesuai dengan judul atau nama objek, pengguna dapat dengan mudah mengidentifikasi konten yang mereka cari dan mengaksesnya dengan cepat. Selain itu, penggunaan regular expressions membantu dalam menentukan pola slug yang sesuai, tanpa perlu memahami secara detail cara kerja regular expressions. Dengan demikian, langkah-langkah ini membantu meningkatkan kualitas dan kegunaan aplikasi secara keseluruhan.

**1. Edit file urls.py**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,    
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![Screenshot (1367)](https://hackmd.io/_uploads/ryTwhyJyA.png)

**2. Edit file views.py**
```
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 restaurants_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()
```
![Screenshot (1368)](https://hackmd.io/_uploads/rJiOyg11C.png)

**3. Edit file restaurantlocation_list.html**
```
{% extends "base.html" %}
{% block head_tittle %}Restaurants | {{ block.super }}{% endblock head_tittle %}
{% block content %}
<h1>Restaurants 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.update }}</li>
{% endfor %}
</ul>
{% endblock %}
```
![Screenshot (1369)](https://hackmd.io/_uploads/B1JMlxJyA.png)

**3. Cek di browser dan edit slugs pada pada admin Django Change Restaurant Location**

# Saving Data The Hard & Wrong Way
Secara singkat, metode yang salah dan sulit untuk menyimpan data dalam Django adalah dengan langsung mengambil data dari formulir POST dan menyimpannya ke dalam database tanpa validasi. Hal ini berpotensi membuka celah keamanan, karena data tidak diverifikasi dan dapat mengakibatkan masalah seperti manipulasi data oleh pihak yang tidak sah. Metode yang salah ini tidak mengikuti praktik terbaik dalam pengembangan web, karena tidak melakukan validasi data yang masuk sebelum disimpan ke dalam database. Hal ini dapat menyebabkan kerentanan keamanan dan kerusakan data. Sebagai gantinya, disarankan untuk menggunakan formulir Django yang memungkinkan validasi data sebelum disimpan, serta menggunakan fitur-fitur keamanan bawaan Django seperti CSRF token untuk melindungi aplikasi dari serangan yang berpotensi merugikan.

**1. Buat file forms.py didalam direktori restaurants**
```
from django import forms

class RestaurantCreateForm(forms.Form):
        name = forms.CharField()
        location = forms.CharField(required=False)
        category = forms.CharField(required=False)
```
![Screenshot (1375)](https://hackmd.io/_uploads/SkyRXN1kA.png)

**2. Edit file views.py**
```
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic import 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":
        print("post data")
        print(request.POST)
        title = request.POST.get("title")
        location = request.POST.get("location")
        category = request.POST.get("category")
        obj = RestaurantLocation.objects.create(
            title=title,
            location=location,
            category=category
        )
        return HttpResponseRedirect("/restaurants/")
    template_name = 'restaurants/form.html'
    context = {}
    return render(request, template_name, context)

def restaurants_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()
```
![Screenshot (1385)](https://hackmd.io/_uploads/rkC-uBk1A.png)

**3. Buat file form.html didalam direktori template restaurants**
```
{% extends "base.html" %}

{% block head_title %}Add Restaurant | {{block.super}}{% endblock head_title %}

{% block content %}
<h1>Add Restaurant</h1>

<form>
        <input type='text' name='title' placeholder="Restaurant"><br/>
        <input type='text' name='location' placeholder="Location"><br/>
        <input type='text' name='category' placeholder="Category"><br/>
        <button type='submit'>Save</button>
</form>

{% endblock %}
```
![Screenshot (1384)](https://hackmd.io/_uploads/HJocPrykR.png)

**4. Edit file urls.py pada direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/create/$', RestaurantListView.as_view()),
    url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![Screenshot (1378)](https://hackmd.io/_uploads/S1psDEJyR.png)

**5. Edit file utils.py didalam direktory restaurants**
```
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
```
![Screenshot (1386)](https://hackmd.io/_uploads/SkUUOrykR.png)

**6. Akses 127.0.0.1:8000/restaurants/create**
![Screenshot (1387)](https://hackmd.io/_uploads/HJxndBk1C.png)

# The power of Django Forms
Penggunaan Django Forms memudahkan kita dalam memvalidasi dan mengelola data dari pengguna dalam aplikasi web. Dibandingkan dengan cara manual yang rumit dan rentan terhadap kesalahan, Django Forms menawarkan kemudahan dalam penggunaan dan kekuatan dalam validasi data.

Dengan Django Forms, kita dapat dengan mudah membuat formulir HTML yang terkait dengan model database kita. Formulir ini dapat secara otomatis menangani validasi data yang dikirimkan oleh pengguna, termasuk memeriksa apakah data yang dimasukkan sesuai dengan aturan yang ditetapkan.

Selain itu, Django Forms juga memungkinkan kita untuk menggunakan metode validasi kustom yang dapat disesuaikan sesuai dengan kebutuhan aplikasi kita. Dengan cara ini, kita dapat memastikan bahwa data yang dimasukkan oleh pengguna telah diverifikasi dengan benar sebelum disimpan ke dalam database.

Penggunaan Django Forms juga membantu dalam mengurangi kode yang berulang dan meningkatkan efisiensi dalam pengembangan aplikasi. Dengan menyederhanakan proses validasi dan pengelolaan data, Django Forms memungkinkan kita untuk fokus pada pengembangan fitur-fitur utama dari aplikasi web kita.

Dalam keseluruhan, penggunaan Django Forms merupakan salah satu fitur andalan Django yang dapat mempercepat dan menyederhanakan proses pengembangan aplikasi web, sambil memastikan keamanan dan keandalan data yang dimasukkan oleh pengguna.

**1. Edit file views.py**
```
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic import 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:
        errors = form.errors

    template_name = 'restaurants/form.html'
    context = {"form": form, "errors": errors}
    return render(request, template_name, context)

def restaurants_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()
```
![Screenshot (1393)](https://hackmd.io/_uploads/SkeLF8k10.png)

**2. Edit file form.html di direktori templates restaurants**
```
{% extends "base.html" %}

{% block head_title %}Add Restaurant | {{block.super}}{% endblock head_title %}

{% block content %}
<h1>Add Restaurant</h1>

{{ errors}}
<form method='POST'> {% csrf_token %}
      {{ form.as_p }}
      <button type='submit'>Save</button>
</form>

{% endblock %}
```
![Screenshot (1391)](https://hackmd.io/_uploads/B1O5uUkk0.png)

**3. Edit file form.py di direktori src restaurants**
```
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
```
![Screenshot (1392)](https://hackmd.io/_uploads/BktlFIJJC.png)

**4. Akses ke 127.0.0.1:8000/restaurants/create pada browser ubuntu dan coba add restaurant dengan Name: "Hello"(Jika Not a valid name, konfig berhasil)**
![Screenshot (1394)](https://hackmd.io/_uploads/rk46t8ky0.png)

# The Extra Power of Django Model Forms
Django menyediakan fitur yang kuat melalui Model Forms, yang memungkinkan Anda membuat formulir secara otomatis berdasarkan model Django Anda. Dengan Model Forms, Anda dapat membuat formulir dengan sangat efisien tanpa perlu menulis banyak kode. Ini memungkinkan validasi data langsung berdasarkan aturan yang Anda tetapkan dalam model Django Anda. Penggunaan Model Forms juga memungkinkan Anda untuk dengan mudah menangani operasi CRUD (Create, Read, Update, Delete) pada model Anda tanpa banyak usaha. Dengan memanfaatkan fitur-fitur ini, Anda dapat mengembangkan aplikasi Django dengan lebih cepat dan lebih efisien, sambil memastikan konsistensi data dan keselarasan dengan model Anda.

**1. Edit file forms.py didalam direktori restaurants**
```
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
```
![Screenshot (1399)](https://hackmd.io/_uploads/HJuy1fgJ0.png)

**2. Edit file form.html didalam direktori templas restaurants**
```
 {% extends "base.html" %}

{% block head_title %}Add Restaurant | {{block.super}}{% endblock head_title %}

{% block content %}
<h1>Add Restaurant</h1>

{% if form.errors %}
    {{ form.errors }}
{% endif %}
<form method='POST'> {% csrf_token %}
      {{ form.as_p }}
      <button type='submit'>Save</button>
</form>

{% endblock %}
```
![Screenshot (1400)](https://hackmd.io/_uploads/r1bH1zlyR.png)

**3. Edit file views.py didalam direktori restaurants**
```
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
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 = RestaurantCreateForm(request.POST or None)
    errors = None
    if form.is_valid():
        form.save()
        return HttpResponseRedirect("/restaurants/")
    if form.errors:
        errors = form.errors

    template_name = 'restaurants/form.html'
    context = {"form": form, "errors": errors}
    return render(request, template_name, context)

def restaurants_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()

class RestaurantCreateView(CreateView):
    form_class = RestaurantLocationCreateForm
    template_name = 'restaurants/form.html'
    success_url = "/restaurants"
```
![Screenshot (1401)](https://hackmd.io/_uploads/rJ_qkfg1A.png)

**4. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/create/$', RestaurantCreateView.as_view()),
    url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![Screenshot (1402)](https://hackmd.io/_uploads/r1fkgflJA.png)

**5. Akses ke 127.0.0.1:8000/restaurants/create dan coba daftar dan lihat hasilnya pada restaurants**
![Screenshot (1396)](https://hackmd.io/_uploads/ByY7gGg10.png)
![Screenshot (1397)](https://hackmd.io/_uploads/SkFUlGgkR.png)

**6. Coba daftar dengan nama Hello dan jika name invalid maka konfig berhasil**
![Screenshot (1398)](https://hackmd.io/_uploads/SyVcezxk0.png)

# Simple + Effective Validation
Validasi sederhana dan efektif adalah proses memastikan bahwa data yang dimasukkan ke dalam sistem sesuai dengan aturan yang ditetapkan. Dalam Django, Anda dapat melakukan validasi dengan mudah menggunakan fitur-fitur bawaan atau membuat validasi kustom sesuai kebutuhan. Validasi ini penting untuk memastikan keakuratan dan keandalan data dalam aplikasi Anda, serta mencegah kesalahan atau kerusakan data di masa depan. Dengan menggunakan metode validasi yang tepat, Anda dapat meningkatkan kualitas dan kinerja aplikasi Anda secara keseluruhan.

**1. Edit file forms.py di direktori restaurants**
```
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):
    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
```
![Screenshot (1406)](https://hackmd.io/_uploads/rJFOTMg1C.png)

**2. Edit file form.html di direktori templates restaurants**
```
 {% extends "base.html" %}

{% block head_title %}Add Restaurant | {{block.super}}{% endblock head_title %}

{% block content %}
<h1>Add Restaurant</h1>

{% if form.errors.non_field_errors %}
    {{ form.errors.non_field_errors }}
{% endif %}
<form method='POST'> {% csrf_token %}
      {{ form.as_p }}
      <button type='submit'>Save</button>
</form>

{% endblock %}
```
![Screenshot (1407)](https://hackmd.io/_uploads/SyDcRGxy0.png)

**3. Buat file validators.py di direktori restaurants**
sudo touch validators.py
```
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):
        enail = 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")
```
![Screenshot (1408)](https://hackmd.io/_uploads/BJjPemlJC.png)

**4. Edit file models.py didalam direktori restaurants**
```
from django.db import models

# Create your models here.
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
# 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, validators=[validate_category])
    timestamp   = models.DateTimeField(auto_now=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

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)
#    instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

#post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```
![Screenshot (1409)](https://hackmd.io/_uploads/BJYJbXl10.png)

**5. Akses server dengan 127.0.0.1:800/restaurants/create**
![Screenshot (1403)](https://hackmd.io/_uploads/H1BD-7xkR.png)

**6. Jalankan migrasi pada server**
python manage.py makemigrations
python manage.py migrate
python manage.py runserver

**7. Akses kembali dan coba tambahkan name kembali (harus berhasil ditambahkan)**
![Screenshot (1404)](https://hackmd.io/_uploads/rylyzQl1A.png)

**8. ETambah kembali dengan nama New name dan category asian**
![Screenshot (1405)](https://hackmd.io/_uploads/BkXEzmlJ0.png)

# Letting User own Data
Jadi, intinya adalah kita memberi pengguna kepemilikan atas data mereka sendiri, dan dengan menggunakan foreign key, kita dapat dengan mudah mengakses data tersebut berdasarkan pengguna tertentu.

**1. Edit file models.py didalam direktori Restaurants**
```
from django.db import models

# Create your models here.
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
# Create your models here.

User = settings.AUTH_USER_MODEL

class RestaurantLocation(models.Model):
    user        = models.ForeignKey(User)
    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=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

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)
#    instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

#post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```
![Screenshot (1410)](https://hackmd.io/_uploads/H1H9DSgkC.png)

**2. Jalankan shell dan jalankan perintah dibawah ini**
python manage.py shell
**Pastikan User kalian apa, didokumen ini user adalah cfe dan pastikan active**
Untuk cara lihatnya dengan masuk ke 127.0.0.1:8000/admin
![image](https://hackmd.io/_uploads/HkoM__xyR.png)

kemudian jalankan perintah pada shell:
from django.contrib.auth import get_user_model
User = get_user_model()
User.objects.all()
![image](https://hackmd.io/_uploads/rkivd_gk0.png)
cfe_user = User.objects.get(id=1)
cfe_user
cfe_user.username
cfe_user.email
cfe_user.password
cfe_user.is_active
cfe_user.is_staff
cfe_user
![image](https://hackmd.io/_uploads/S1MjOdxk0.png)
obj = cfe_user
obj 
instance = cfe_user
instance
instance.restaurantlocation_set.all()
instance.restaurantlocation_set.filter(category__iexact='Mexican')
![image](https://hackmd.io/_uploads/SkxxFdg1A.png)
User.objects.all()
User.objects.get(id=2)
jm_user = User.objects.get(id=2)
jm_user.restaurantlocation_set.all()
![image](https://hackmd.io/_uploads/S1cOKulJR.png)

Exit Shell dengan CTRL+D atau exit()
Masuk ke shell lagi

from restaurants.models import RestaurantLocation
RestaurantLocation.objects.filter(user__id=1)
RestaurantLocation.objects.filter(user__username__iexact='CFE')
qs = RestaurantLocation.objects.filter(user__username__iexact='CFE')
![image](https://hackmd.io/_uploads/B1MH5_gyA.png)
from restaurants.models import RestaurantLocation
RestaurantLocation.objects.filter(user__id=1)
RestaurantLocation.objects.filter(user__username__iexact='CFE')
qs = RestaurantLocation.objects.filter(user__username__iexact='CFE')
qs
obj = qs.first()
obj.owner
User = obj.owner.__class__
User
User.objects.all()
cfe_user = User.objects.all().first()
cfe_user.restaurantlocation_set.all()
new_qs = cfe_user.restaurantlocation_set.all()
new_obj = new_qs.first()
new_obj
RK = new_obj.__class__
RK.objects.all()

# Associate User to Form Data in FBV
Ketika menambahkan lokasi baru, penting untuk terhubungkan dengan pengguna yang memintanya. Dalam tampilan berbasis fungsi, pastikan pengguna telah masuk. Jika iya, simpan lokasi yang terkait dengan pengguna tersebut. Jika tidak, minta pengguna untuk masuk terlebih dahulu sebelum menambahkan lokasi. Ini memastikan bahwa setiap lokasi terhubung dengan pengguna yang benar.

**1. Edit file views.py didalam direktori restaurants**
```
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
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 = RestaurantCreateForm(request.POST or None)
    errors = None
    if form.is_valid():
        if request.user.is_authenticated():
            instance = form.save(commit=False)
            instance.owner = request.user
            instance.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 restaurants_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()

class RestaurantCreateView(CreateView):
    form_class = RestaurantLocationCreateForm
    template_name = 'restaurants/form.html'
    success_url = "/restaurants"
```
![image](https://hackmd.io/_uploads/SkZQsYg1C.png)

**2. Edit file urls.py didalam direkotori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurant_createview,
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/create/$', restaurant_createview ), #RestaurantCreateView.as_view()),
    url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![image](https://hackmd.io/_uploads/B1mIiFekA.png)

**3. Akses ke 127.0.0.1:8000/restaurants/create dengan browser ubuntu dan coba add restaurant**
![Screenshot (1411)](https://hackmd.io/_uploads/Sy-oitekR.png)

**4. Cek pada restaurants, maka akan muncul restaurants yang barusan ditambahkan**
![Screenshot (1412)](https://hackmd.io/_uploads/SkwAstg10.png)

**5. Akses ke 127.0.0.1:8000/restaurants/create dengan mode penyamaran pada browser ubuntu dan coba add restaurant**
![Screenshot (1413)](https://hackmd.io/_uploads/HJPznFek0.png)

**6. Coba save dan lihat apakah page not found (404)**
![Screenshot (1414)](https://hackmd.io/_uploads/SkMUnKek0.png)
"Jika page not found (404), artinya berhasil karena outputnya itu dialihkan ke url login"

# Login Required to View
Pada materi ini, dijelaskan tentang cara mengatur tampilan agar memerlukan pengguna untuk masuk sebelum dapat mengaksesnya. Terdapat dua cara utama untuk melakukan ini:

Penggunaan Decorator login_required pada Fungsi: Decorator ini memastikan bahwa pengguna harus masuk sebelum mengakses tampilan. Jika belum masuk, pengguna akan diarahkan ke halaman masuk.

Penggunaan Mixin LoginRequiredMixin pada Tampilan Kelas: Mixin ini menambahkan fungsionalitas yang memerlukan masuk pada tampilan berbasis kelas. Ini memastikan bahwa pengguna harus masuk sebelum dapat mengakses tampilan tersebut.

Selain itu, dijelaskan juga cara mengatur URL masuk default di pengaturan Django untuk menentukan halaman mana yang akan ditampilkan jika pengguna mencoba mengakses tampilan yang memerlukan masuk tanpa masuk terlebih dahulu.

**1. Edit file base.py didalam direktori mywebsite/settings**
tambahkan LOGIN_URL = '/login/''
![image](https://hackmd.io/_uploads/SkCbMqlJC.png)

**2. Edit file urls.py didalam direktori mywebsite**
sesuaikan seperti gambar dibawah ini
![image](https://hackmd.io/_uploads/HyRDM5ly0.png)

**3. Edit file views.py didalam direktori restaurants**
```
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(login_url='/login/')
def restaurant_createview(request):
    form = RestaurantCreateForm(request.POST or None)
    errors = None
    if form.is_valid():
        if request.user.is_authenticated():
            instance = form.save(commit=False)
            instance.owner = request.user
            instance.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 restaurants_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()

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)
```
![image](https://hackmd.io/_uploads/SkyWNqlkR.png)

**4. Akses ke 127.0.0.1:8000/restaurants/create dengan mode penyamaran pada browser ubuntu**
![Screenshot (1415)](https://hackmd.io/_uploads/HyqJLqx10.png)
![Screenshot (1416)](https://hackmd.io/_uploads/r1RJIcgyA.png)
"Bisa terlihat ketika urlnya kita ketikkan dan kemudian kita enter, urlnya berubah ke 127.0.0.1:8000/login/?next=restaurants/create yang dari awalnya hanya 127.0.0.1:8000/restaurants/create. Ini menandakan bahwa konfigurasi berhasil dikarenakan jika ingin menambahkan restaurants maka harus login terlebih dahulu"

# Login View
Jadi, dalam pengembangan web dengan Django, kita sering memerlukan halaman login untuk pengguna. Django menyediakan tampilan bawaan yang memudahkan proses ini. Begini cara kerjanya:

Login View: Django memiliki kelas bernama LoginView yang memudahkan kita menampilkan halaman login. Kita dapat mengimpornya dengan from django.contrib.auth.views import LoginView.

Menggunakan LoginView: Setelah diimpor, kita dapat langsung menggunakan LoginView dalam definisi URL kita. Misalnya, path('login/', LoginView.as_view(), name='login').

Template Login: Django menyertakan template bawaan untuk halaman login. Jadi, jika kita ingin menggunakan template default, kita hanya perlu membuat folder registration di dalam folder templates, kemudian membuat file login.html di dalamnya.

Konfigurasi URL: Pastikan kita mengonfigurasi URL dengan benar untuk mengarahkan ke tampilan login. Ini biasanya dilakukan dalam file urls.py aplikasi.

Dengan menggunakan LoginView, kita bisa dengan mudah menambahkan fungsionalitas login ke aplikasi Django kita tanpa harus menulis ulang kode yang rumit. Itu membantu kita menghemat waktu dan usaha dalam pengembangan aplikasi web.

**1. Buat direktori registration didalam mywebsite dan buat file login.html didalam direktori registration**
![image](https://hackmd.io/_uploads/rk442qb1R.png)


**2. Edit file login.html**
```
{% 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 %}
<table>
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>

{# Assumes you setup the password_reset view in your URLconf #}

{% endblock %}
```
![image](https://hackmd.io/_uploads/H1ofHiZ1R.png)

**3. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from restaurants.views import (
    restaurant_createview,
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/create/$', RestaurantCreateView.as_view()),
   # url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
![image](https://hackmd.io/_uploads/Sko3nqb1R.png)

**4. Akses 127.0.0.1:8000/login dengan browser ubuntu**
![Screenshot (1418)](https://hackmd.io/_uploads/ry0VHsWkA.png)

# Using Reverse to Shortcut URLS
Kita akan membahas penggunaan reverse untuk mempersingkat URL dalam Django. Ini berguna untuk membuat tautan yang terhubung dengan URL tanpa harus mengetahui URL secara spesifik.

1. Pola Nama: Kita memberi nama pada setiap URL dalam Django, seperti 'home', 'about', dan 'contact'. Ini memudahkan kita untuk merujuk URL dengan menggunakan nama mereka, daripada harus mengetik URL secara manual setiap kali.

2. Pola Reverse: Dalam views, kita menggunakan fungsi reverse untuk menghasilkan URL secara otomatis berdasarkan nama URL yang telah kita definisikan sebelumnya. Ini membuat kode lebih fleksibel dan mudah dipelihara.

3. Manfaat: Dengan menggunakan reverse, kita dapat menghindari pengulangan dan kesalahan dalam penulisan URL. Ini juga memungkinkan kita untuk dengan cepat mengubah atau memperbarui URL tanpa harus memodifikasi kode HTML secara manual.

Dengan menggunakan reverse, navigasi aplikasi web kita menjadi lebih efisien dan mudah dipelihara, serta membuat kode menjadi lebih terorganisir.

**1. Edit file restaurantlocation_list.html didalam direktori src/restaurants/templates/restaurants**
```
{% extends "base.html" %}
{% block head_title %}Restaurants | {{ block.super }}{% endblock head_title %}
{% block content %}
<h1>Restaurants List</h1>

<ul>
{% for obj in object_list %}
    <li><a href='{{ obj.get_absolute_url }}'>{{ obj }}</a><br/>
        <a href='/restaurants/{{ obj.slug }}/'>{{ obj }}</a><br/>
        {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }}
    </li>
{% endfor %}
</ul>
{% endblock %}
```
![image](https://hackmd.io/_uploads/rk_PK2-yC.png)

**2. Buat file urls.py didalam direktori src/restaurants**
touch urls.py
```
from django.conf.urls import url
from .views import (
    #restaurant_createview,
    #restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    #url(r'^admin/', admin.site.urls),
    #url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
    #url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^$', RestaurantListView.as_view(), name='list'),  # Perhatikan penghapusan tanda kutip ganda ekstra
    url(r'^create/$', RestaurantCreateView.as_view(), name='create'),  # Perhatikan penghapusan tanda kutip ganda ekstra dan tanda slash di depan
    url(r'^(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view(), name='detail'),  # Perhatikan penghapusan tanda kutip ganda ekstra dan tanda slash di depan
    #url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
    #url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```
![image](https://hackmd.io/_uploads/H1OZchWyA.png)

**3. edit file urls.py didalam direktori src/mywebsite**
```
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from restaurants.views import (
    restaurant_createview,
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
    url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^restaurants/', include('restaurants.urls', namespace='restaurants')),
    #url(r'^restaurants/$', RestaurantListView.as_view(), name='restaurants'),
    #url(r'^restaurants/create/$', RestaurantCreateView.as_view(), name='restaurants-creat'),
    #url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view(), name='restaur'),
    url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
```
![image](https://hackmd.io/_uploads/BJWZsh-yR.png)

**4. Edit file nav.html didalam direktori src/mywebsite/templates/snippets**
```
<div class="container">
<h1>Nicholai Dandy Nainggolan.org</h1>
<a href='{% url "home" %}'>Home</a>
<a href='{% url "about" %}'>About</a>
<a href='{% url "contact" %}'>Contact</a>
<a href='{% url "restaurants:list" %}'>Restaurants</a>
</div>
```
![image](https://hackmd.io/_uploads/BkCDj3-JA.png)

**5. Edit file models.py didalam direktori restaurants**
```
from django.db import models

# Create your models here.
from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save, post_save
from django.core.urlresolvers import reverse
from .utils import unique_slug_generator
from .validators import validate_category
# Create your models here.

User = settings.AUTH_USER_MODEL

class RestaurantLocation(models.Model):
    user        = models.ForeignKey(User)
    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=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('restaurants:detail', kwargs={'slug': self.slug})

    @property
    def title(self):
        return self.name

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)
#    instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

#post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```
![image](https://hackmd.io/_uploads/Bkpso3-k0.png)

**6. Akses 127.0.0.1:8000/restaurants pada browser ubuntu**
![Screenshot (1421)](https://hackmd.io/_uploads/rJTthn-yA.png)

Dan coba klik salah satu restaurants(contoh saya klik warung, dan lihat apakah pagenya loading atau tidak, jika tidak maka konfig berhasil karena hanya reverse saja)
![Screenshot (1422)](https://hackmd.io/_uploads/BymlTnWyR.png)

# Menu Items App
Kita akan membuat aplikasi baru yang bernama "menu items". Aplikasi ini akan membantu kita membuat daftar makanan favorit dari restoran-restoran yang kita sukai. Setiap item menu akan terkait dengan pengguna yang membuatnya dan restoran tempat item tersebut tersedia.

Pengguna akan bisa menambahkan nama item, isi dari item tersebut, dan mengatur apakah item tersebut ingin dibagikan secara publik atau tidak. Kemudian, pengguna dapat melihat daftar item-menu yang mereka buat dan membagikannya dengan teman-teman mereka.

Proses pembuatan aplikasi ini akan membantu kita memahami bagaimana cara membuat model untuk menyimpan data, menambahkan model ke dalam admin Django, dan membuat tampilan untuk menampilkan dan mengelola item-menu tersebut. Ini akan membantu kita memahami dasar-dasar pengembangan aplikasi web menggunakan Django

**1. Membuat menus melalui perintah**
python manage.py startapp menus
![image](https://hackmd.io/_uploads/SyFYgRWJA.png)

**2. Edit file models.py pada direktori src/menus**
```
from django.conf import settings
from django.db import models
from restaurants.models import RestaurantLocation

class Item(models.Model):
    user       = models.ForeignKey(settings.AUTH_USER_MODEL)
    restaurant = models.ForeignKey(RestaurantLocation)
    name       = models.CharField(max_length=120)
    contents   = models.TextField(help_text='Separate each item by comma')
    excludes   = models.TextField(blank=True, null=True,  help_text='Separate each item by comma')
    public     = models.BooleanField(default=True)
    timestamp  = models.DateTimeField(auto_now_add=True)
    updated    = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-updated', '-timestamp']

    def get_contents(self):
        return self.contents.split(",")

    def get_excludes(self):
        return self.excludes.split(",")
```
![image](https://hackmd.io/_uploads/rJt4D0bkR.png)

**3. Edit file base.py didalam direkori mywebsite/settings**
tambahkan 'menus', dibagian installed_apps = [z
![image](https://hackmd.io/_uploads/Bk9AwCZJ0.png)

**4. Jalankan migrasi dan jalankan server**
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
![image](https://hackmd.io/_uploads/Hy2PuCb10.png)

**5. Edit file admin.py pada direktori menus**
```
from django.contrib import admin
from .models import Item

admin.site.register(Item)
```
![image](https://hackmd.io/_uploads/HkL6dCW1A.png)

**6. Akses 127.0.0.1:8000/admin/ pada browser ubuntu**
![Screenshot (1423)](https://hackmd.io/_uploads/BkIftCZJR.png)

kemudian pilih Items pada bagian menus
![Screenshot (1424)](https://hackmd.io/_uploads/SJDBKAWkA.png)

kemudian add item dan kita sudah bisa menambahkan menu pada restaurants
![Screenshot (1425)](https://hackmd.io/_uploads/SyOOFRZkR.png)

# Menu Items Views
Kita telah membuat beberapa views untuk mengelola item pada aplikasi menu.

ItemListView: Menampilkan daftar item yang tersedia. Kita menggunakan queryset yang hanya memuat item yang dimiliki oleh pengguna yang sedang login.

ItemDetailView: Menampilkan detail dari suatu item. Kita menggunakan ID item untuk mengambil data tersebut.

ItemCreateView: Menampilkan form untuk membuat item baru. Setelah pengguna mengisi form dan mengirimkannya, item baru akan dibuat.

ItemUpdateView: Menampilkan form untuk memperbarui item yang sudah ada. Pengguna dapat mengubah data yang ingin diperbarui dan menyimpan perubahan tersebut.

Setiap view terhubung dengan template HTML yang sesuai, seperti item_list.html untuk ListView, item_detail.html untuk DetailView, dan item_form.html untuk CreateView dan UpdateView. Dalam template, kita menggunakan konteks data untuk menampilkan informasi yang diperlukan kepada pengguna, seperti daftar item atau detail item tertentu.

**1. Edit file views.py pada menus**
```
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from .forms import ItemForm
from .models import Item

class ItemListView(ListView):
    def get_queryset(self):
        return Item.objects.filter(user=self.request.user)

class ItemDetailView(DetailView):
    def get_queryset(self):
        return Item.objects.filter(user=self.request.user)

class ItemCreateView(CreateView):
    template_name = 'form.html'
    form_class = ItemForm
    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.user = self.request.user
        return super(ItemCreateView, self).form_valid(form)

    def get_queryset(self):
        return Item.objects.filter(user=self.request.user)

    def get_context_data(self, *args, **kwargs):
        context = super(ItemCreateView, self).get_context_data(*args, **kwargs)
        context['title'] = 'Create Item'
        return context

class ItemUpdateView(UpdateView):
    template_name = 'form.html'
    form_class = ItemForm

    def get_queryset(self):
        return Item.objects.filter(user=self.request.user)

    def get_context_data(self, *args, **kwargs):
        context = super(ItemUpdateView, self).get_context_data(*args, **kwargs)
        context['title'] = 'Update Item'
        return context
```
![image](https://hackmd.io/_uploads/BkNyfgGJ0.png)

**2. Buat file forms.py didalam direktori menus dan edit**
```
from django import forms
from .models import Item

class ItemForm(forms.ModelForm):
    class Meta:
       model = Item
       fields = [
           'restaurant',
           'name',
           'contents',
           'excludes',
           'public',
        ]
```
![image](https://hackmd.io/_uploads/ByjHflGyC.png)


