---
# System prepended metadata

title: DJANGO

---

**DJANGO**

##### Apa Itu Django?
Django adalah framework web berbasis Python yang membantu membuat website dengan cepat dan mudah.

Framework ini menyediakan banyak alat dan fitur siap pakai, sehingga kita tidak perlu membuat semuanya dari awal.

##### Struktur Dasar Django
1. Project: Proyek utama yang berisi seluruh aplikasi dan pengaturan.
2. App: Bagian kecil dari proyek yang memiliki fungsi spesifik (contoh: aplikasi untuk user, blog, dll).
3. Model: Menentukan struktur database .
4. View: Logika yang mengatur apa yang akan ditampilkan di halaman web.
5. Template: File HTML yang menentukan tampilan halaman web.
6. URL: Menghubungkan alamat website dengan view yang sesuai.

##### Kelebihan Django
1. Cepat Dibangun: Banyak fitur siap pakai, jadi kita bisa fokus ke fitur unik website kita.
2. Aman: Punya sistem keamanan bawaan buat ngelindungi dari serangan umum kayak SQL injection. 
3. Skalabel: Cocok buat proyek kecil sampai besar (contohnya, Instagram pake Django).
4. Komunitas Besar: Banyak dokumentasi dan dukungan dari komunitas.
5. Mudah Dipelajari: Kalau udah ngerti Python, belajar Django jadi lebih gampang.

##### Kenapa Pake Django?
1. Buat bikin website dengan cepat, aman, dan efisien tanpa perlu mulai dari nol.
2. Cocok buat pemula karena banyak alat bantu dan dokumentasi yang jelas.
3. Pas banget buat proyek yang butuh database dan backend yang kompleks.

## Section 1 : Welcome to Try Django 1.11

1. Install Django di Windows
• Install python versi 3 di website resmi 
![Screenshot 2025-03-11 231838](https://hackmd.io/_uploads/SJrAMkAjye.png)
![image](https://hackmd.io/_uploads/Byg-rJAoJl.png)
![image](https://hackmd.io/_uploads/rkGMHk0s1g.png)


    • Buka CMD windows lalu buat direktori untuk project yang akan dibuat
    ```
    C:\Users\ofcyu>cd ..
    C:\Users>cd ..
    C:\>mkdir Dev && cd Dev
    C:\Dev>
    ```
    • install Virtual Environment 
    ```
    C:\Dev\tryjango1-11>pip install virtualenv
    
    C:\Dev\tryjango1-11>pip list
    Package      Version
    ------------ -------
    distlib      0.3.9
    filelock     3.17.0
    pip          24.3.1
    platformdirs 4.3.6
    virtualenv   20.29.3
    ```


• Membuat Environment
```
C:\Dev\tryjango1-11>virtualenv -p python3 .
```

```
C:\Dev\tryjango1-11>.\Scripts\activate
```   

• Install django latest version & start project
```
(tryjango1-11) C:\Dev\tryjango1-11>pip install Django==5.1.7
```

```
(tryjango1-11) C:\Dev\tryjango1-11>mkdir src && cd src

(tryjango1-11) C:\Dev\tryjango1-11\src>django-admin startproject cfehome .
```

• Create Settings Module
```
(tryjango1-11) C:\Dev\tryjango1-11\src\cfehome>mkdir settings && cd settings
```
```markdown
code __init__.py
```
`code` merupakan text editor yang langsung terhubung ke visual studio code
```markdown=
from .base import *

from .production import *

try:
    from .local import *
except:
    pass
```
```markdown
code base.py
```
```markdown=
"""
Django settings for cfehome project.

Generated by 'django-admin startproject' using Django 5.1.7.

For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-a_e3%5sjzt#r@b^j_m745sv@7gm#eor9g35q7b+3gp_ojd0zx7'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'cfehome.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'cfehome.wsgi.application'


# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

```

```markdown!
code production.py
```

```markdown!
code local.py
```
• install common tools
```markdown!
pip install psycopg2
pip install gunicorn dj-database-url
pip install django-crispy-forms
pip install pillow
```
Tools	Fungsi Utama
**psycopg2** : Menghubungkan Django dengan database PostgreSQL.
**gunicorn** : Server WSGI untuk menjalankan aplikasi Django di lingkungan produksi.
**dj-database-url**	: Memudahkan konfigurasi database menggunakan URL.
**django-crispy-forms**	: Membuat form Django lebih menarik dan responsif.
**pillow**	: Memproses dan memanipulasi gambar di aplikasi Django.
> **Penjelasan lebih lanjut mengenai Gunicorn**
> Gunicorn (Green Unicorn) adalah WSGI HTTP Server yang digunakan untuk menjalankan aplikasi web Python, seperti Django atau Flask, di lingkungan produksi. Gunicorn dirancang untuk menangani banyak permintaan (requests) secara bersamaan dan bekerja dengan baik di lingkungan deployment seperti Heroku, AWS, atau server Linux.
> 
> WSGI (Web Server Gateway Interface) adalah antarmuka standar antara server web (seperti Gunicorn) dan aplikasi web Python (seperti Django atau Flask). WSGI memungkinkan server web dan aplikasi Python untuk berkomunikasi dengan cara yang seragam.
> 
>Kenapa Butuh Gunicorn dan WSGI?
>- Tanpa Gunicorn: Django bisa berjalan di server pengembangan (development), tetapi tidak kuat menangani banyak pengguna sekaligus. 
> - Tanpa WSGI: Gunicorn dan Django tidak bisa berkomunikasi dengan baik.
> 
> Dengan Gunicorn dan WSGI:
> - Aplikasi Django bisa menangani banyak pengguna sekaligus.
> - Aplikasi Django bisa berjalan dengan aman dan cepat di lingkungan produksi.




- buat requirements.txt file
```markdown!
pip freeze > requirements.txt
```
```markdown=
asgiref==3.8.1
dj-database-url==2.3.0
Django==5.1.7
django-crispy-forms==2.3
gunicorn==23.0.0
packaging==24.2
pillow==11.1.0
psycopg2==2.9.10
sqlparse==0.5.3
typing_extensions==4.12.2
tzdata==2025.1
```

• Run migration & createsuperuser
```markdown!
python manage.py migrate
```
Fungsi: Menerapkan perubahan skema database (seperti membuat atau mengubah tabel) berdasarkan model Django ke database.

Kegunaan: Digunakan setelah membuat atau mengubah model untuk memperbarui struktur database.

```
python manage.py createsuperuser
```
Fungsi: Membuat akun superuser (admin) untuk mengakses Django admin panel.

Kegunaan: Digunakan untuk membuat akun admin yang dapat mengelola data melalui antarmuka admin Django.

## Section 2 : Create apps (HTML & Django)

```markdown=
1. Handles URLS
2. Return Response
3. Remember things

account -- user "django app"
videos -- 'dajngo app'
analytics
menu listings
```
#### 1. Create apps
```markdown!
python manage.py startapp restaurants
```
- Ini adalah isi dari direktori restaurant ketika kita membuat app baru
```
restaurants/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
│   └── __init__.py
├── models.py
├── tests.py
└── views.py~~
```
> models.py : Berfungsi sebagai tempat untuk mendefinisikan model database. Model adalah representasi tabel di database dan berisi field-field yang akan menyimpan data aplikasi Anda.
> 
> admin.py : Berfungsi sebagai tempat untuk mendaftarkan model ke admin panel Django. Ini memungkinkan Anda mengelola data model (seperti menambah, mengedit, atau menghapus) melalui antarmuka admin.
> 
> views.py : Berfungsi sebagai tempat untuk menulis logika aplikasi. View menerima request dari pengguna, memprosesnya, dan mengembalikan response (seperti halaman HTML atau data JSON).
> 
> urls.py (dibuat manual) : Berfungsi sebagai tempat untuk mendefinisikan URL routing aplikasi. File ini menghubungkan URL tertentu dengan view yang sesuai.
> 
> templates/ (dibuat manual) : Berfungsi sebagai tempat untuk menyimpan file template HTML. Template digunakan oleh view untuk merender tampilan yang akan dilihat oleh pengguna.
> 
> settings.py (di direktori proyek utama) : Berfungsi sebagai tempat untuk mengonfigurasi pengaturan proyek Django, seperti menambahkan aplikasi ke INSTALLED_APPS, mengatur database, dan konfigurasi lainnya.
    
- ubah `views.py`
```markdown=
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
# fuction based view
def home(request):
    return HttpResponse("hello")
    #return render(request, "home.html", {})#response
```

- ubah `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path

from restaurants.views import home

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home),
]
```

- jalankan server menggunakan perintah berikut 
```markdown!
python manage.py runserver
```

#### 2. Rendering HTML
Di tahap ini, kita belajar membuat function-based view di Django menggunakan HttpResponse untuk mengembalikan HTML dinamis dengan f-string, seperti This is {html_var} coming through. Ini adalah dasar sebelum beralih ke template HTML dan render.
- rubah `views.py` untuk membuat tampilan baru
```markdown=
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
# fuction based view
def home(request):
    html_var = 'f strings'
    html_ = f"""<!doctype html>
    <html lang="en" data-bs-theme="auto">

    <head>
    </head>
    <body>
    <h1>Hello World!</h1>
    <p>This is {html_var} coming through</p>
    </body>
    </html>
    """
    #fstrings
    return HttpResponse(html_)
    #return render(request, "home.html", {})#response
```

#### 3.  Render a Django Template
Di tahap ini, kita belajar menggunakan template HTML di Django dengan membuat direktori templates dan file base.html. Kita mengonfigurasi settings.py untuk mengenali direktori template dan mengedit views.py untuk menggunakan fungsi render guna mengirim data dinamis (seperti html_var dan num) ke template. Ini adalah langkah untuk memisahkan logika Python dari tampilan HTML, menggantikan penggunaan HttpResponse langsung.
- buat direktori baru didalam direktori src bernama `templates` lalu buat file baru `base.html`
![image](https://hackmd.io/_uploads/ryDxCIl3yx.png)
lalu isi `base.html` seperti berikut
```markdown=
<!doctype html>
    <html lang="en" >

    <head>
    </head>
    <body>
    <h1>Hello World!</h1>
    <p>
        This is {{ html_var }} coming through
    </p>
    <p>
        Random number is {{ num }}
    </p>
    </body>
    </html>
```
- dengan django kita bisa merubah template di `base.py` yang kita buat sebelumnya di direktori settings dengan merubah menjadi sebagai berikut pada bagian base dir, templates, dan database.
```markdown=
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

```
```markdown=
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')], 
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```
```markdown=
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
```

- Edit `views.py` ubah menjadi sebagai berikut
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
# fuction based view
def home_old(request):
    html_var = 'f strings'
    num = random.randint(0, 100000000)
    html_ = """<!doctype html>
    <html lang="en" data-bs-theme="auto">

    <head>
    </head>
    <body>
    <h1>Hello World!</h1>
    <p>This is {html_var} coming through</p>
    <p>This is a random number: {num} coming through</p>
    </body>
    </html>
    """
    #fstrings
    return HttpResponse(html_)

def home(request):
    num = random.randint(0, 100000000)
    return render(request, "base.html", {"html_var": True, "num": num })#response

```
- edit `init.py` yanga da di direktori settings menjadi sebagai berikut
```markdown=
from .base import *

# from .production import *

# try:
#     from .local import *
# except:
#     pass
```

#### 4. Context in Django Template
Di tahap ini, kita belajar tentang Context in Django Template. Kita mengirim data dari views.py ke template base.html menggunakan dictionary context. Data yang dikirim termasuk variabel boolean (bool_item), angka acak (num), dan list angka (some_list). Di template, kita menggunakan Django Template Language (DTL) seperti {% if %}, {% for %}, dan filter divisibleby untuk menampilkan data secara dinamis. Ini memungkinkan kita mengontrol tampilan HTML berdasarkan data yang dikirim dari view.
- lakukan perubahan pada `views.py`
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render

# Create your views here.
# fuction based view

def home(request):
    num = None
    some_list = [
        random.randint(0, 100000000), 
        random.randint(0, 100000000), 
        random.randint(0, 100000000)
        ]
    
    condition_bool_item = False
    if condition_bool_item:
        num = random.randint(0, 100000000)
    context = {
        "bool_item": False,
        "num": num,
        "some_list": some_list
    }
    return render(request, "base.html", context)
```

- lakukan perubahan pada `base.html`
```markdown=
<!doctype html>
    <html lang="en" >

    <head>
    </head>
    <body>
    <h1>Hello World!</h1>
    <p>
        This is <code> {% verbatim %}{{ html_var }} {% endverbatim %} </code> coming through
    </p>
    <p>
        {% if num is not None %}
        Random number is {{ num }}
        {% endif %}
    </p>
    <p>
        {% for some_item in some_list %}
            {% if some_item|divisibleby:"2" %}
                Even number
            {% endif %}

            {{ some_item }}<br/>
        {% endfor %}
    </p>
    <p>Some item is {{ some_item }} </p>
    </body>
    </html>
```
- Hasil ketika di test di browser
![image](https://hackmd.io/_uploads/HJ4qXzm3kl.png)


#### 5. Template Inheritance

- Tambahkan 2 views tambahan di file `views.py`
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render


def home(request):
    num = None
    some_list = [
        random.randint(0, 100000000), 
        random.randint(0, 100000000), 
        random.randint(0, 100000000)
        ]
    
    condition_bool_item = False
    if condition_bool_item:
        num = random.randint(0, 100000000)
    context = {
        "bool_item": False,
        "num": num,
        "some_list": some_list
    }
    return render(request, "home.html", context)

def about(request):

    context = {
    }
    return render(request, "about.html", context)

def contact(request):

    context = {
    }
    return render(request, "contact.html", context)
```
- Tambahkan template baru di `urls.py` untuk membuat halama baru
```markdown=
from django.contrib import admin
from django.urls import path

from restaurants.views import home, about, contact

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home),
    path('about/', about),
    path('contact/', contact),
]
```
- Ubah `base.py`
```markdown=
<!doctype html>
    <html lang="en" >

    <head>
    <title>{% block head_title %}Mendoan.com{% endblock head_title %}</title>
    </head>
    <body>
    <h1>Mendoan.com</h1>
    <a href='/'>Home</a>
    <a href='/about/'>About</a>
    <a href='/contact/'>Contact</a>

    <div class='container'>
    {% block content %}{% endblock content %}
    </div>

    </body>
    </html>
```

- Buat template baru di direktori templates 
`code home.html`
`code about.html` 
`code contact.html`

>Home
```markdown=
{% extends "base.html" %}
{% block head_title %}Welcome || {{ block.super }}{% endblock head_title %}

{% block content %}

<h1>Hello World!</h1>
<h3>{{ block.super }}</h3>
<p>
    This is <code> {% verbatim %}{{ html_var }} {% endverbatim %} </code> coming through
</p>
<p>
    {% if num is not None %}
    Random number is {{ num }}
    {% endif %}
</p>
<p>
    {% for some_item in some_list %}
        {% if some_item|divisibleby:"2" %}
            Even number
        {% endif %}

        {{ some_item }}<br/>

    {% endfor %}
</p>
<p>Some item is {{ some_item }} </p>

{% endblock content %}
```

>About
```markdown=
{% extends "base.html" %}

{% block head_title %}About || {{ block.super }}{% endblock head_title %}

{% block content %}
<h1>About</h1>
{% endblock %}
```
> contact
```markdown=
{% extends "base.html" %}

{% block head_title %}Contact || {{ block.super }}{% endblock head_title %}

{% block content %}
<h1>Content</h1>
{% endblock %}
```

- Hasil home
![image](https://hackmd.io/_uploads/Sy2QjnSh1l.png)

- Hasil About
![image](https://hackmd.io/_uploads/rkFIinBhJe.png)

- Hasil Contact
![image](https://hackmd.io/_uploads/ryQOjhBnyg.png)

#### 6. Include Template Tag
- buat direktori baru didalam direktori template 
```markdown!
mkdir snippets
```

- buat file html baru didalam direktori snippets
```markdown!
code nav.html
code css.html
code js.html
code sidebar.html
```

- edit `base.py`
```markdown=
<!doctype html>
    <html lang="en" >

    <head>
    <title>{% block head_title %}Mendoan.com{% endblock head_title %}</title>
    {% include 'snippets/css.html' %}
    </head>
    <body>
    {% include 'snippets/nav.html' %}

    <div class='container'>
    {% block content %}{% endblock content %}
    </div>


    {% include 'snippets/js.html' %}
    </body>
    </html>
```

- edit `nav.html`
```markdown=
<div class='container'>
<h1>Mendoan.com</h1>
<a href='/'>Home</a>
<a href='/about/'>About</a>
<a href='/contact/'>Contact</a>
</div>
```

- edit `css.html`
> didapatkan dari Bootstrap CDN version 3.3.7
```markdown=
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<!-- Optional theme -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
```

- edit `js.html`
>didapatkan dari Bootstrap CDN version 3.3.7
```markdown=
<!-- Latest compiled and minified JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
```
>copy link jquary core 3.7.1 minified
```markdown=
<script src='https://code.jquery.com/jquery-3.7.1.min.js'></script>
```

- edit `sidebar.html`
```markdown=
<ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>

</ul>
```

- tambahkan include tag di `about.html` dan `contact html`
```markdown=
{% extends "base.html" %}

{% block head_title %}About || {{ block.super }}{% endblock head_title %}

{% block content %}
<h1>About</h1>

{% include 'snippets/sidebar.html' %}

{% endblock %}
```

```markdown=
{% extends "base.html" %}

{% block head_title %}Contact || {{ block.super }}{% endblock head_title %}

{% block content %}
<h1>Content</h1>
{% include 'snippets/sidebar.html' %}
{% endblock %}
```

- hasil ketika di browse
![image](https://hackmd.io/_uploads/BJFbrarnJl.png)
![image](https://hackmd.io/_uploads/BJDGSTShkx.png)
![image](https://hackmd.io/_uploads/BJ3QB6Bnkg.png)

#### 7. Class based View
- edit `views.py` dengan menabahkan class Contactview
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View


def home(request):
    num = None
    some_list = [
        random.randint(0, 100000000), 
        random.randint(0, 100000000), 
        random.randint(0, 100000000)
        ]
    
    condition_bool_item = False
    if condition_bool_item:
        num = random.randint(0, 100000000)
    context = {
        "bool_item": False,
        "num": num,
        "some_list": some_list
    }
    return render(request, "home.html", context)

def about(request):

    context = {
    }
    return render(request, "about.html", context)

def contact(request):

    context = {
    }
    return render(request, "contact.html", context)

class ContactView(View):
    def get(self, request, *args, **kwargs):
        print(kwargs)
        context = {}
        return render(request, "contact.html", context)
```

- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path

from restaurants.views import home, about, contact, ContactView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home),
    path('about/', about),
    re_path('contact/(?P<id>\d+)', ContactView.as_view()),
```

hasilnya adalah ketika kita membuka aplikasi secara default maka akan error
![image](https://hackmd.io/_uploads/BkgygRr3Jl.png)

tetapi ketika kita menambahkan id maka aplikasi akan terbuka
![image](https://hackmd.io/_uploads/rkgAMlCr3kx.png)

dan akan menghasil output berikut di server
![image](https://hackmd.io/_uploads/SJ7SxCBhJe.png)

- Template View

cara pertama
- edit `views.py`
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView

class HomeView(TemplateView):
    template_name = 'home.html'

    def get_context_data(self, *args, **kwargs):
        context = super(HomeView, self).get_context_data(*args, **kwargs)
        num = None
        some_list = [
            random.randint(0, 100000000), 
            random.randint(0, 100000000), 
            random.randint(0, 100000000)
        ]
        condition_bool_item = True
        if condition_bool_item:
            num = random.randint(0, 100000000)
        context = {
            "bool_item": False,
            "num": num,
            "some_list": some_list
        }
        return context

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

class ContactView(TemplateView):
    template_name = 'contact.html'
```

- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path

from restaurants.views import HomeView, AboutView, ContactView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', HomeView.as_view()),
    path('about/', AboutView.as_view()),
    re_path('contact/', ContactView.as_view()),
]
```

Metode Kedua, memindahkan template `about.html` dan `contact.html` ke `urls.py`
>kita dapat melakukan hal yang sama kepada template `home.html` jika tidak terdapat context didalamnya
- edit `views.py`
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView

class HomeView(TemplateView):
    template_name = 'home.html'

    def get_context_data(self, *args, **kwargs):
        context = super(HomeView, self).get_context_data(*args, **kwargs)
        num = None
        some_list = [
            random.randint(0, 100000000), 
            random.randint(0, 100000000), 
            random.randint(0, 100000000)
        ]
        condition_bool_item = True
        if condition_bool_item:
            num = random.randint(0, 100000000)
        context = {
            "bool_item": False,
            "num": num,
            "some_list": some_list
        }
        return context

```
- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import HomeView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', HomeView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    re_path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

## Section 3 : Remembering Things

#### 1. Remembering things with models
- buat super user
```markdown!
python manage.py createsuperuser
```

- Akses django admin di browser `http:<ip>:8080/admin`
![image](https://hackmd.io/_uploads/S1hiUqLnJl.png)

- Masukan username dan password yang telah dibuat sebelumnya
![image](https://hackmd.io/_uploads/S1MZDcLhJg.png)

- edit `base.py`
```markdown=
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'restaurants',
]
```

- edit `models.py` yang ada di direktori restaurants
```markdown=
from django.db import models

# Create your models here.
class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
```

- migrate database
```markdown!
python manage.py makemigrations
python manage.py migrate
```
>makemigrations: Membuat catatan perubahan (seperti menulis daftar tugas).
> 
> migrate: Menjalankan perubahan tersebut (seperti melakukan tugas yang sudah ditulis).

- edit `admin.py`
```markdown=
from django.contrib import admin

# Register your models here.
from .models import RestaurantLocation

admin.site.register(RestaurantLocation)
```

- Hasil
![image](https://hackmd.io/_uploads/H1wmQOvhJl.png)
![image](https://hackmd.io/_uploads/rJJS7uDhyl.png)
![image](https://hackmd.io/_uploads/S1zUm_vhJg.png)

#### 2. More on Model Fields
- Edit `models.py` untuk menambahkan models baru
```markdown=
from django.db import models

# Create your models here.
class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True)
    timestamp   = models.DateTimeField(auto_now=False, auto_now_add=False)
```
>`category    = models.CharField(max_length=120, null=True, blank=False)`
>category : Nama field di database.
>models.CharField : Tipe data untuk teks (string).
>max_length=120 : Maksimal 120 karakter.
>null=True : Boleh kosong di database (disimpan sebagai NULL).
>blank=True : Boleh kosong saat input data di form Django.

- lakukan makemigrations & migrate

```markdown!
(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py makemigrations
Migrations for 'restaurants':
  restaurants\migrations\0005_alter_restaurantlocation_category.py
    ~ Alter field category on restaurantlocation

(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py makemigrations
It is impossible to add a non-nullable field 'timestamp' to restaurantlocation without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit and manually define a default value in models.py.
Select an option: 1
Please enter the default value as valid Python.
The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value.
Type 'exit' to exit this prompt
>>> timezone.now
Migrations for 'restaurants':
  restaurants\migrations\0006_restaurantlocation_timestamp.py
    + Add field timestamp to restaurantlocation

(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, restaurants, sessions
Running migrations:
  Applying restaurants.0005_alter_restaurantlocation_category... OK
  Applying restaurants.0006_restaurantlocation_timestamp... OK
```

- hasil
![image](https://hackmd.io/_uploads/Hy0Nqdv2kg.png)

#### 3. Displaying Saved Data

- edit `views.py`
```markdown=
import random
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView

from .models import RestaurantLocation

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)
```

- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import restaurant_listview

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', restaurant_listview),
    path('about/', TemplateView.as_view(template_name='about.html')),
    re_path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

- buat direktori templates didalam direktori aplikasi restaurant
```markdown!
C:\Dev\tryjango1-11\src\restaurants> mkdir templates && cd templates

C:\Dev\tryjango1-11\src\restaurants\templates>mkdir restaurants && cd restaurants
```

- buat file `restaurants_list.html` didalam direktori restaurants
```markdown!
code restaurants_list.html
```

- isi file `restaurants_list.html` seperti dibawah ini
```markdown=
{% extends "base.html" %}

{% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %}

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

<ul>
{% for obj in object_list %}
    <li>{{ obj }} <br/>
    {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li>
{% endfor %}
</ul>
{% endblock %}
```

- edit `models.py`

```markdown=
from django.db import models

# Create your models here.
class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True)
    timestamp   = models.DateTimeField(auto_now=False, auto_now_add=False)

    def __str__(self):
        return self.name
```

- Jalankan migrate
```markdown!
python manage.py makemigrations

python manage.py migrate
```

- hasil
![image](https://hackmd.io/_uploads/rk72X__2ye.png)

#### 4. Understading Querysets

- masuk ke shell django
```markdown!
(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py shell         

Python 3.13.2 (tags/v3.13.2:4f8bb39, Feb  4 2025, 15:23:48) [MSC v.1942 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
```
- Melihat isi database
```markdown!
>>> from restaurants.models import RestaurantLocation  
>>> RestaurantLocation.objects.all()
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>]>  

>>> for obj in RestaurantLocation.objects.all():
...     print(obj.name)
...
Baja Fish Tacos
Masakan Jawa
>>>
```

- filter dan mengupdate database
```markdown!
>>> qs = RestaurantLocation.objects.all()
>>> qs.filter(category='mexican')          
<QuerySet []>
>>> qs.filter(category__iexact='mexican') 
<QuerySet [<RestaurantLocation: Baja Fish Tacos>]>
>>> qs.update(category='American')
2
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>]>  
>>> qs.filter(category__iexact='mexican') 
<QuerySet []>
>>> qs.filter(category__iexact='American') 
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>]>  
>>>
```

- Membuat Restaurant Location menggunakan shell
```markdown!
>>> obj = RestaurantLocation()
>>> obj.name = "Pei Wei"
>>> obj.location = "Newport Beach"
>>> obj.category = "Asian Fusion"
>>> obj.save()
>>> qs = RestaurantLocation.objects.all() 
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>]>
```

- Membuat Restaurant Location dengan 1 baris perintah
```markdown!
>>> obj = RestaurantLocation.objects.create(name='Chronic Tacos', location='Corona Del Mar', category='Mexican')
>>> obj
<RestaurantLocation: Chronic Tacos>
>>> qs = RestaurantLocation.objects.all()
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>]>
>> qs.count()
4
```
- Keluar dari shell
```markdown!
>>> exit()
now exiting InteractiveConsole...

(tryjango1-11) C:\Dev\tryjango1-11\src>
```

#### 5. Generic list View
- edit `views.py`
```markdown=
import random
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView, ListView

from .models import RestaurantLocation

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset
```

- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_listview,
    RestaurantListView,

)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    re_path(r'^restaurants/(?P<slug>\w+)/$', RestaurantListView.as_view()),
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    re_path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

- ubah penamaan templates restaurant `restaurant_list.html` menjadi `restaurantlocation.html`
```markdown
rename restaurant_list.html restaurantlocation.html

```

#### 6. Restaurant Profile Detail

- edit `views.py`
```markdown
import random
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView

from .models import RestaurantLocation

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all()
    
    def get_object(self, *args, **kwargs):
        rest_id = self.kwargs.get('rest_id')
        obj = get_object_or_404(RestaurantLocation, id=rest_id) #pk = rest_id
        return obj
```

- edit `urls.py`
```markdown
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/<rest_id>/', RestaurantDetailView.as_view()),
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    re_path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

- buat file html baru di dalam templates restaurant
```markdown!
code restaurantlocation_detail.html
```

- isi file `restaurantlocation_detail.html` seperti sebagai berikut
```markdown=
{% extends "base.html" %}

{% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %}

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

<ul>
{% for obj in object_list %}
    <li>{{ obj }} <br/>
    {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li>
{% endfor %}
</ul>
{% endblock %}
```

- hasil
![image](https://hackmd.io/_uploads/B1iWSp_hJx.png)

#### 7. Slugfield and Uniqe Slug Generator

- edit `models.py`
```markdown=
from django.db import models

# Create your models here.
class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True)
    timestamp   = models.DateTimeField(auto_now_add=True)
    update      = models.DateTimeField(auto_now_add=True)
    slug        = models.SlugField(null=True, blank=True)

    def __str__(self):
        return self.name
    
    @property
    def title(self):
        return self.name #obj.title
```

- buat file `utils.py` di dalam direktori aplikasi restaurant

```markdown
code utils.py
```
- edit `utils.py` 
```markdown=
import random
import string
from django.utils.text import slugify
'''
random_string_generator is located here:
http://joincfe.com/blog/random-string-generator-in-python/
'''

def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

def unique_slug_generator(instance, new_slug=None):
    """
    This is for a Django project and it assumes your instance 
    has a model with a slug field and a title character (char) field.
    """
    if new_slug is not None:
        slug = new_slug
    else:
        slug = slugify(instance.title)

    Klass = instance.__class__
    qs_exists = Klass.objects.filter(slug=slug).exists()
    if qs_exists:
        new_slug = "{slug}-{randstr}".format(
                    slug=slug,
                    randstr=random_string_generator(size=4)
                )
        return unique_slug_generator(instance, new_slug=new_slug)
    return slug
```

- edit `restaurantlocation_detail.html`
```markdown=
{% extends "base.html" %}

{% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %}

{% block content %}
<h1>{{ object.title }} <small> {{ object.category }}</small></h1>
<p>{{ object.location}}</p>
<p>{{ object.timestamp }}, updated {{ opject.updated|timesince }} ago </p>
{% endblock %}
```

- lakukan migrate
```markdown
python manage.py makemigrations
python manage.py migrate
```

- jalankan shell django
```markdown
python manage.py shell
```

```markdown!
>>> from restaurants.models import RestaurantLocation
>>> obj = RestaurantLocation.objects.get(id=1)
>>> obj.name
'Baja Fish Tacos'
>>> obj.title
'Baja Fish Tacos'
>>> from restaurants.utils import unique_slug_generator
>>> print (unique_slug_generator(obj))
baja-fish-tacos
>>> exit()
```

#### 8. Signal for Uniqe Slug
- edit `models.py`
```markdown=
from django.db import models
from django.db.models.signals import pre_save, post_save

from .utils import unique_slug_generator


class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True)
    timestamp   = models.DateTimeField(auto_now_add=True)
    update      = models.DateTimeField(auto_now_add=True)
    slug        = models.SlugField(unique=True, null=True, blank=True)

    def __str__(self):
        return self.name
    
    @property
    def title(self):
        return self.name #obj.title

def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    print("saving...")
    print(instance.timestamp)
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

def rl_post_save_receiver(sender, instance, created, *args, **kwargs):
    print('saved')
    print(instance.timestamp)
    instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```

- lakukan perubahan item, lalu save and continue
![image](https://hackmd.io/_uploads/Sk_JtDK2kg.png)

- terminal akan menghasilkan output seperti dibawah ini
```markdown!
saving..
2025-03-19 17:24:25.820563+00:00
saved
2025-03-19 17:24:25.820563+00:00
[20/Mar/2025 17:04:25] "POST /admin/restaurants/restaurantlocation/3/change/ HTTP/1.1" 302 0
```

## Section 4 : Froms, Views, Models, and More

#### 1. Slug as Urls Params

- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

- edit `views.py`
```markdown=
import random
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView

from .models import RestaurantLocation

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all()
```

- edit `restaurantlocation_list.html`
```markdown=
{% extends "base.html" %}

{% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %}

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

<ul>
{% for obj in object_list %}
    <li><a href='/restaurants/{{ obj.slug }}'>{{ obj }}</a><br/>
            {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li>
{% endfor %}
</ul>
{% endblock %}
```

- maka hasilnya ketika kita membuka profile maka akan ada tag yang sesuai dengan profile restaurantnya
![image](https://hackmd.io/_uploads/ry-jXoKhJx.png)

#### 2. Get Single Items from DB
- masuk ke shell django
```markdown
python manage.py shell
```
- Melihat Semua Data
```markdown!
>>> from restaurants.models import RestaurantLocation
>>> qs = RestaurantLocation.objects.all()
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, ...]>
```
- Mengambil Elemen Pertama dan Terakhir
```markdown!
>>> qs.first()
<RestaurantLocation: Baja Fish Tacos>
>>> qs.last()  
<RestaurantLocation: Chronic Tacos>
```
- Mengambil Objek Berdasarkan Indeks
```markdown!
>>> qs[1]
<RestaurantLocation: Masakan Jawa>
```
- filter berdasarkan kategori
```markdown!
>>> qs = RestaurantLocation.objects.filter(category='mexican')
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>]>
>>> qs = RestaurantLocation.objects.filter(category__iexact='mexican')
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Chronic Tacos>]>
```
>`filter(category='mexican')` mencari data dengan kategori persis "mexican".
>
>`filter(category__iexact='mexican') `mencari data tanpa memperdulikan huruf besar/kecil.

- Mengambil Objek berdasarkan Primary Key (PK)
```markdown!
>>> RestaurantLocation.objects.get(pk=1)
<RestaurantLocation: Baja Fish Tacos>
```
>Menggunakan .get() untuk mengambil satu item berdasarkan primary key (pk)

>Mengambil satu objek dengan pk=1, jika tidak ditemukan akan error.

- Menggunakan `get_object_or_404()`
```markdown!
>>> from django.shortcuts import get_object_or_404
>>> obj = get_object_or_404(RestaurantLocation, pk=12000)
```
>Mengambil objek berdasarkan pk=12000. Jika tidak ditemukan, akan mengembalikan error 404.

- Menggunakan `try-except` untuk Menangani Error
```markdown!
>>> try:
...     obj = get_object_or_404(RestaurantLocation, pk=12000)
... except:
...     print("Not found")
...
Not found
```
>Menghindari error dengan menangkap Http404 jika objek tidak ditemukan

- Menggunakan `filter()` dan `exists()`
```markdown!
>>> qs = RestaurantLocation.objects.filter(pk=12000)
>>> qs.exists()
False
```
>`.filter()` tidak akan menimbulkan error jika tidak ada data, berbeda dengan `.get()`.

- Mengambil Objek Berdasarkan Slug
```markdown!
>>> qs = RestaurantLocation.objects.filter(slug='baja-fish-tacos')
>>> if qs.exists():
...     print(qs.first())
...
Baja Fish Tacos
```

#### 3. Saving Data the Hard + Wrong Way
- buat file `forms.py` didalam direktori aplikasi restaurant
```markdown!
code forms.py
```

- edit file `forms.py`
```markdown!
from django import forms


class RestaurantCreateForm(forms.Form):
    name        = forms.CharField
    location    = forms.CharField(required=False)
    category    = forms.CharField(required=False)
```

- edit `views.py`
```markdown!
import random
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView

from .forms import RestaurantCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    if request.method == "GET":
        print("get data")
    if request.method == "POST": #PUT
        print("post data")
        print(request.POST)
        title = request.POST.get("title") #request.POST("title")
        location = request.POST.get("location")
        category = request.POST.get("category")
        obj = RestaurantLocation.objects.create(
                name = title,
                location = location,
                category = category

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

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

    # def get_object(self, *args, **kwargs):
    #     rest_id = self.kwargs.get('rest_id')
    #     obj = get_object_or_404(RestaurantLocation, id=rest_id) #pk = rest_id
    #     return obj
```

- buat file `form.html` didalam templates aplikasi restaurant `restaurants/templates/restaurants`
```markdown!
code form.html
```

- isi file `form.html`
```markdown!
{% extends "base.html" %}

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

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

<form method="POST"> {% csrf_token %} 
    <input type='text' name='title' placeholder="Your Title"><br/>
    <input type='text' name='location' placeholder="Location"><br/>
    <input type='text' name='category' placeholder="Category"><br/>
    <button type="submit">Save</button>
</form>

{% endblock %}
```

- edit `utils.py`
```markdown!
import random
import string
from django.utils.text import slugify

DONT_USE = ['create']
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

def unique_slug_generator(instance, new_slug=None):

    if new_slug is not None:
        slug = new_slug
    else:
        slug = slugify(instance.title)
    if slug in DONT_USE:
        new_slug = "{slug}-{randstr}".format(
                    slug=slug,
                    randstr=random_string_generator(size=4)
                )
        return unique_slug_generator(instance, new_slug=new_slug)

    Klass = instance.__class__
    qs_exists = Klass.objects.filter(slug=slug).exists()
    if qs_exists:
        new_slug = "{slug}-{randstr}".format(
                    slug=slug,
                    randstr=random_string_generator(size=4)
                )
        return unique_slug_generator(instance, new_slug=new_slug)
    return slug
```

- hasil
![image](https://hackmd.io/_uploads/SJts-0K21e.png)
![image](https://hackmd.io/_uploads/Hkn3WRF2kx.png)

#### 4. The Power of Django Forms

- edit `views.py`
```markdown=
import random
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView

from .forms import RestaurantCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    form = RestaurantCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        obj = RestaurantLocation.objects.create(
                name = form.cleaned_data.get('name'),
                location = form.cleaned_data.get('location'),
                category = form.cleaned_data.get('category')

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

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

    # def get_object(self, *args, **kwargs):
    #     rest_id = self.kwargs.get('rest_id')
    #     obj = get_object_or_404(RestaurantLocation, id=rest_id) #pk = rest_id
    #     return obj
```

- edit `form.html`
```markdown=
{% extends "base.html" %}

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

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

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

{% endblock %}


    <!-- <input type='text' name='name' placeholder="Your Title" reqired=reqired><br/>
    <input type='text' name='location' placeholder="Location"><br/>
    <input type='text' name='category' placeholder="Category"><br/> -->
```

- edit `forms.py`
```markdown=
from django import forms


class RestaurantCreateForm(forms.Form):
    name        = forms.CharField()
    location    = forms.CharField(required=False)
    category    = forms.CharField(required=False)

    def clean_name(self):
        name = self.cleaned_data.get("name")
        if name == "Hello":
            raise forms.ValidationError("Not a valid name")
        return name
```

- hasil
![image](https://hackmd.io/_uploads/SkHCcAF3yg.png)
![image](https://hackmd.io/_uploads/Sy_JiCY2ye.png)

#### 5. The Extra Power of Django Models Form

- edit `forms.py`
```markdown=
from django import forms

from .models import RestaurantLocation

class RestaurantCreateForm(forms.Form):
    name        = forms.CharField()
    location    = forms.CharField(required=False)
    category    = forms.CharField(required=False)

    def clean_name(self):
        name = self.cleaned_data.get("name")
        if name == "Hello":
            raise forms.ValidationError("Not a valid name")
        return name
    
class RestaurantLocationCreateForm(forms.ModelForm):
    class Meta:
        model = RestaurantLocation
        fields = [
            'name',
            'location',
            'category',
        ]
        
    def clean_name(self):
        name = self.cleaned_data.get("name")
        if name == "Hello":
            raise forms.ValidationError("Not a valid name")
        return name
```

- edit `views.py`
```markdown=
import random
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView

from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    form = RestaurantLocationCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        # Customize
        # like a pre save
        form.save()
        #like a post save
        return HttpResponseRedirect("/restaurants/")
    if form.errors:
        print(form.errors)
        errors = form.errors
        
    template_name = 'restaurants/form.html'
    context = {"form": form, "errors":errors}
    return render(request, template_name, context)

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

class RestaurantCreateView(CreateView):
    form_class = RestaurantLocationCreateForm
    template_name = 'restaurants/form.html'
    success_url = "/restaurants/"
```

- edit `urls.py`
```markdown=
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
    
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/create', RestaurantCreateView.as_view()),
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

- edit form.html
```markdown=
{% extends "base.html" %}

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

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

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

{% endblock %}
```

#### 6. Simple + Effective Validation

- edit `forms.py`
```markdown=
from django import forms

from .models import RestaurantLocation
from .validators import validate_category

class RestaurantCreateForm(forms.Form):
    name        = forms.CharField()
    location    = forms.CharField(required=False)
    category    = forms.CharField(required=False)

    def clean_name(self):
        name = self.cleaned_data.get("name")
        if name == "Hello":
            raise forms.ValidationError("Not a valid name")
        return name
    
class RestaurantLocationCreateForm(forms.ModelForm):
    # email           = forms.EmailField()
    # category        = forms.CharField(required=False, validators=[validate_category])
    class Meta:
        model = RestaurantLocation
        fields = [
            'name',
            'location',
            'category',
        ]

    def clean_name(self):
        name = self.cleaned_data.get("name")
        if name == "Hello":
            raise forms.ValidationError("Not a valid name")
        return name
    
    # def clean_email(self):
    #     email = self.cleaned_data.get("email")
    #     if ".edu" in email:
    #         raise forms.ValidationError("We do not accept edu emails ")
    #     return email
```

- edit `form.html`
```markdown=
{% extends "base.html" %}

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

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

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

{% endblock %}
```

- buat file baru didalam direktori aplikasi restaurant `validators.py`
```markdown!
code validators.py
```

- isi file `validators.py` seperti dibawah ini
```markdown=
from django.core.exceptions import ValidationError

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError(
            '%(value)s is not an even number',
            params={"value": value},
        )
    

def validate_email(value):
        email = value
        if ".edu" in email:
            raise ValidationError("We do not accept edu emails ")
        

CATEGORIES = ['Mexican', 'Asian', 'American', 'Whatever']

def validate_category(value):
     cat = value.capitalize()
     if not value in CATEGORIES and not cat in CATEGORIES:
          raise ValidationError(f"{value} not a valid category")
```
- edit `models.py`
```markdown=
from django.db import models
from django.db.models.signals import pre_save, post_save

from .utils import unique_slug_generator
from .validators import validate_category

class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category])
    timestamp   = models.DateTimeField(auto_now_add=True)
    update      = models.DateTimeField(auto_now_add=True)
    slug        = models.SlugField(unique=True, null=True, blank=True)

    def __str__(self):
        return self.name
    
    @property
    def title(self):
        return self.name #obj.title
    
def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

# def rl_post_save_receiver(sender, instance, created, *args, **kwargs):
#     print('saved')
#     print(instance.timestamp)
#     if not instance.slug:
#         instance.slug = unique_slug_generator(instance)
#         instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

# post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```

- hasil
![image](https://hackmd.io/_uploads/HybU3G92kg.png)
![image](https://hackmd.io/_uploads/BJlv2GchJg.png)

#### 7. Letting Users Own Data

- edit `models.py`
```markdown=
from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save, post_save

from .utils import unique_slug_generator
from .validators import validate_category

User = settings.AUTH_USER_MODEL

class RestaurantLocation(models.Model):
    owner       = models.ForeignKey(User, on_delete=models.CASCADE) # class_instance.model_set.all()
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category])
    timestamp   = models.DateTimeField(auto_now_add=True)
    update      = models.DateTimeField(auto_now_add=True)
    slug        = models.SlugField(unique=True, null=True, blank=True)

    def __str__(self):
        return self.name
    
    @property
    def title(self):
        return self.name #obj.title
    
def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

# def rl_post_save_receiver(sender, instance, created, *args, **kwargs):
#     print('saved')
#     print(instance.timestamp)
#     if not instance.slug:
#         instance.slug = unique_slug_generator(instance)
#         instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

# post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```

- lakukan migrasi dengan perintah `python manage.py makemigrations` dan `python manage.py migrate`
```markdown!
(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py makemigrations
It is impossible to add a non-nullable field 'owner' to restaurantlocation without specifying a default. This is because the database needs something to populate existing rows.  
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit and manually define a default value in models.py.
Select an option: 1
Please enter the default value as valid Python.
The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value.
Type 'exit' to exit this prompt
>>> 1
Migrations for 'restaurants':
  restaurants\migrations\0012_restaurantlocation_owner.py
    + Add field owner to restaurantlocation

(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, restaurants, sessions
Running migrations:
  Applying restaurants.0012_restaurantlocation_owner... OK
```

- hasil
![image](https://hackmd.io/_uploads/BJfrKU53kg.png)

- masuk ke shell django
```markdown!
python manage.py shell
```

- mengimpor model User dari django
```markdown!
>>> from django.contrib.auth import get_user_model
>>> User = get_user_model()
```
- mengambil semua objek user yang ada di database
```markdown!
>>> User.objects.all()
<QuerySet [<User: ofcyu>]>
```

- Mengambil objek User dengan ID 1 dari database
```markdown!
>>> ofcyu_user = User.objects.get(id=1)
>>> ofcyu_user                         
<User: ofcyu>
```

- Mengakses atribut username,email, password dari objek User
```markdown!
>>> ofcyu_user.username
'ofcyu'
>>> ofcyu_user.email   
''
>>> ofcyu_user.password
'pbkdf2_sha256$870000$T7Noh6wIdo7Rtgoqgl4bSc$f2fo7B0SeqMF1eD145FPSD03yZqnJxDwM68/B1sEU3o='
```
- menggunakan Variabel User
```markdown!
>>> obj = ofcyu_user  
>>> obj
<User: ofcyu>
```
>Variabel obj menyimpan instance user dengan nama "ofcyu".
- menggunakan instance user
```markdown!
>>> instance = ofcyu_user  
>>> instance
<User: ofcyu>
```
>Variabel instance juga menyimpan instance yang sama dengan obj.
- Mengakses Data yang Dimiliki User
```markdown!
instance.restaurantlocation_set.all() 
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]>
```
- Memfilter Data Berdasarkan Kategori
```markdown!
>>> instance.restaurantlocation_set.filter(category__iexact='mexican')  
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Chipotle>, <RestaurantLocation: name>, <RestaurantLocation: Validate>]>
```
- exit shell lalu jalankan lagi shell baru
```markdown!
>>> exit()

(tryjango1-11) C:\Dev\tryjango1-11\src>python manage.py shell
```

- Menggunakan `filter(owner__id=1)`
```markdown!
>>> from restaurants.models import RestaurantLocation
>>> RestaurantLocation.objects.filter(owner__id=1)
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]>
```
> Perintah ini mengambil semua objek dari model RestaurantLocation yang memiliki owner dengan ID 1.

- Menggunakan filter(owner__username__iexact='ofcyu')
```markdown!
>>> RestaurantLocation.objects.filter(owner__username__iexact='ofcyu')
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]>
```
> Perintah ini mencari semua objek RestaurantLocation yang dimiliki oleh pengguna dengan username ofcyu.

- Menyimpan QuerySet ke Variabel
```markdown!
>>> qs = RestaurantLocation.objects.filter(owner__username__iexact='ofcyu')
>>> qs
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]>
```
> QuerySet hasil pencarian disimpan ke dalam variabel qs agar dapat digunakan kembali tanpa perlu menjalankan query ulang.

- Mengambil Objek Pertama dalam QuerySet
```markdown!
>>> obj = qs.first()
>>> obj.owner
<User: ofcyu>
```
> Mengambil objek pertama dari QuerySet menggunakan .first().
> 
> Mengecek siapa pemilik restoran dengan memanggil obj.owner.
- Mendapatkan Kelas Model User
```markdown!
>>> User = obj.owner.__class__
>>> User
<class 'django.contrib.auth.models.User'>
```
- Melihat Semua User dalam Database
```markdown!
>>> User.objects.all()
<QuerySet [<User: ofcyu>]>
```
- Mengambil User Pertama dalam QuerySet
```markdown!
>>> ofcyu_user = User.objects.all().first()
```
- Melihat Semua RestaurantLocation yang Dimiliki oleh User
```markdown!
>>> ofcyu_user.restaurantlocation_set.all()
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]>
```
> `restaurantlocation_set.all()` digunakan karena terdapat relasi One-To-Many antara User dan RestaurantLocation.
- Menyimpan QuerySet ke Variabel Baru
```markdown!
>>> new_qs = ofcyu_user.restaurantlocation_set.all()
>>> new_obj = new_qs.first()
>>> new_obj
<RestaurantLocation: Baja Fish Tacos>
```
>` new_qs` menyimpan semua restoran yang dimiliki oleh ofcyu.
> 
> `new_obj` mengambil restoran pertama dari QuerySet tersebut.
- Mendapatkan Kelas Model RestaurantLocation
```markdown!
>>> RK = new_obj.__class__
>>> RK.objects.all()
<QuerySet [<RestaurantLocation: Baja Fish Tacos>, <RestaurantLocation: Masakan Jawa>, <RestaurantLocation: Pei Wei>, <RestaurantLocation: Chronic Tacos>, <RestaurantLocation: Another new title>, <RestaurantLocation: Chipotle>, <RestaurantLocation: Sate>, <RestaurantLocation: test>, <RestaurantLocation: New car>, <RestaurantLocation: Another New Awesome Form>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: Created View>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: name>, <RestaurantLocation: new name>, <RestaurantLocation: Asian Item>, <RestaurantLocation: Validate>]>
```
> `RK` menyimpan kelas dari objek `RestaurantLocation`.
> 
> `RK.objects.all()` menampilkan semua data restoran yang tersimpan dalam model `RestaurantLocation`.

#### 8. Associate User to Form Data in FBV

- ketika kita menabahkan item yang merupakan sebuah objek aplikasi maka akan error
![image](https://hackmd.io/_uploads/H1USF9c21g.png)
![image](https://hackmd.io/_uploads/r1ELtq52yx.png)

- login ke django administration
![image](https://hackmd.io/_uploads/Hyhhq9chkg.png)

- edit `urls.py`
```markdown
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_createview,
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
    
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/create',  restaurant_createview ),#RestaurantCreateView.as_view()),
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    path('contact/', TemplateView.as_view(template_name='contact.html')),
```
- edit `views.py`
```markdown
import random
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView

from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    form = RestaurantLocationCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        if request.user.is_authenticated:
            instance = form.save(commit=False)
            # Customize
            # like a pre save
            instance.owner = request.user
            instance.save()
            #like a post save
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors
        
    template_name = 'restaurants/form.html'
    context = {"form": form, "errors":errors}
    return render(request, template_name, context)

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

class RestaurantCreateView(CreateView):
    form_class = RestaurantLocationCreateForm
    template_name = 'restaurants/form.html'
    success_url = "/restaurants/"
```
- Hasil
![image](https://hackmd.io/_uploads/H1d1Xsqh1l.png)
>sudah tidak error
- Buka Ignito mode
![image](https://hackmd.io/_uploads/ByKJMo93ke.png)
![image](https://hackmd.io/_uploads/By9NMo9nye.png)
![image](https://hackmd.io/_uploads/BybW7jc3yg.png)
![image](https://hackmd.io/_uploads/Bkn-Xichkg.png)
>jika create di mode ignito maka page akan tidak ditemukan

#### 9. Associate User to Data in Class Based View
- edit `views.py`
```markdown
import random
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView

from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    form = RestaurantLocationCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        if request.user.is_authenticated:
            instance = form.save(commit=False)
            # Customize
            # like a pre save
            instance.owner = request.user
            instance.save()
            #like a post save
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors
        
    template_name = 'restaurants/form.html'
    context = {"form": form, "errors":errors}
    return render(request, template_name, context)

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

class RestaurantCreateView(CreateView):
    form_class = RestaurantLocationCreateForm
    template_name = 'restaurants/form.html'
    success_url = "/restaurants/"

    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.owner = self.request.user           
        return super(RestaurantCreateView, self).form_valid(form)
```
- edit `urls.py`
```markdown!
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_createview,
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
    
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/create',  RestaurantCreateView.as_view()),
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

#### 10. Login required to view
- edit `views.py`
```markdown!
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView

from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

@login_required()
def restaurant_createview(request):
    form = RestaurantLocationCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        if request.user.is_authenticated:
            instance = form.save(commit=False)
            # Customize
            # like a pre save
            instance.owner = request.user
            instance.save()
            #like a post save
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors
        
    template_name = 'restaurants/form.html'
    context = {"form": form, "errors":errors}
    return render(request, template_name, context)

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

class RestaurantCreateView(LoginRequiredMixin, CreateView):
    form_class = RestaurantLocationCreateForm
    login_url = '/login/'
    template_name = 'restaurants/form.html'
    success_url = "/restaurants/"

    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.owner = self.request.user           
        return super(RestaurantCreateView, self).form_valid(form)
```
- edit `urls.py`
```markdown!
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from restaurants.views import (
    restaurant_createview,
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
    
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/create',  RestaurantCreateView.as_view() ),
    #path('restaurants/create',  restaurant_createview ),#
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```
- edit `base.py` cari `ROOT_URLCONF = 'cfehome.urls'` lalu tambahkan `LOGIN_URL = '/login/'`
```markdown!
ROOT_URLCONF = 'cfehome.urls'
LOGIN_URL = '/login/'
```
- hasil ketika browse lewat mode ignito
![image](https://hackmd.io/_uploads/rJLyk2c3yg.png)

#### 11. Login View

- edit `views.py`
```markdown!
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView

from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

@login_required()
def restaurant_createview(request):
    form = RestaurantLocationCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        if request.user.is_authenticated:
            instance = form.save(commit=False)
            # Customize
            # like a pre save
            instance.owner = request.user
            instance.save()
            #like a post save
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors
        
    template_name = 'restaurants/form.html'
    context = {"form": form, "errors":errors}
    return render(request, template_name, context)

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

class RestaurantCreateView(LoginRequiredMixin, CreateView):
    form_class = RestaurantLocationCreateForm
    login_url = '/login/'
    template_name = 'restaurants/form.html'
    success_url = "/restaurants/"

    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.owner = self.request.user         
        return super(RestaurantCreateView, self).form_valid(form)
```

- edit `urls.py`
```markdown!
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from django.contrib.auth.views import LoginView

from restaurants.views import (
    restaurant_createview,
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
    
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html')),
    path("login/", LoginView.as_view(), name='login'),
    path('restaurants/', RestaurantListView.as_view()),
    path('restaurants/create',  RestaurantCreateView.as_view() ),
    #path('restaurants/create',  restaurant_createview ),#
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view()), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html')),
    path('contact/', TemplateView.as_view(template_name='contact.html')),
]
```

- buat folder registration
```markdown!
mkdir /src/templates/registration && cd /src/templates/registration
```

- buat file baru `login.html` didalam direktori registration

```markdown!
code login.html
```

- isi `login.html`
```markdown!
{% extends "base.html" %}

{% block content %}

{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
    {% if user.is_authenticated %}
    <p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
    {% else %}
    <p>Please login to see this page.</p>
    {% endif %}
{% endif %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}
{{ form.as_p }}

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

{# Assumes you set up the password_reset view in your URLconf #}

{% endblock %}
```
- hasil di ignito
![image](https://hackmd.io/_uploads/rJR0a2qh1g.png)

#### 12. Using Reverse to Shortcut URLS

- edit `nav.html`
```markdown!
<div class='container'>
<h1>Mendoan.com</h1>
<a href='{% url "home" %}'>Home</a>
<a href='{% url "about" %}'>About</a>
<a href='{% url "contact" %}'>Contact</a>
<a href='{% url "restaurants" %}'>Restaurants</a>
</div>
```
- edit `urls.html`
```markdown!
from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

from django.contrib.auth.views import LoginView

from restaurants.views import (
    restaurant_createview,
    restaurant_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
    
)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html'), name='home'),
    path("login/", LoginView.as_view(), name='login'),
    path('restaurants/', RestaurantListView.as_view(), name='restaurants'),
    path('restaurants/create',  RestaurantCreateView.as_view(), name='restaurants-create'),
    #path('restaurants/create',  restaurant_createview ),#
    path('restaurants/<slug:slug>/', RestaurantDetailView.as_view(), name='restaurant-detail'), 
    # path('restaurants/asian/', AsianFusionRestaurantListView.as_view()),
    path('about/', TemplateView.as_view(template_name='about.html'), name='about'),
    path('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```
- edit `restaurantlocation_list.html`
```markdown!
{% extends "base.html" %}

{% block head_title %}Restaurants | {{ block.super}}{% endblock head_title %}

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

<ul>
{% for obj in object_list %}
    <li><a href='{{ obj.get_absolute_url }}'>{{ obj }}</a><br/>
    {{ obj.name }} {{ obj.location }} {{ obj.category }} {{ obj.timestamp }} {{ obj.updated }}</li>
{% endfor %}
</ul>
{% endblock %}
```

- edit `views.py`
```markdown!
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView

from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

@login_required()
def restaurant_createview(request):
    form = RestaurantLocationCreateForm(request.POST or None)   
    errors = None
    if form.is_valid():
        if request.user.is_authenticated:
            instance = form.save(commit=False)
            # Customize
            # like a pre save
            instance.owner = request.user
            instance.save()
            #like a post save
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors
        
    template_name = 'restaurants/form.html'
    context = {"form": form, "errors":errors}
    return render(request, template_name, context)

def restaurant_listview(request):
    template_name = 'restaurants/restaurants_list.html'
    queryset = RestaurantLocation.objects.all()
    context = {
        "object_list": queryset
    }
    return render(request, template_name, context)

def restaurant_detailview(request, slug):
    template_name = 'restaurants/restaurantslocation_detail.html'
    obj = RestaurantLocation.objects.get(slug=slug)
    context = {
        "object": obj
    }
    return render(request, template_name, context)

class RestaurantListView(ListView):
    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug: 
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)     
                )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset

class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all() #filter(category__iexact='asian') # filter by user

class RestaurantCreateView(LoginRequiredMixin, CreateView):
    form_class = RestaurantLocationCreateForm
    login_url = '/login/'
    template_name = 'restaurants/form.html'
    #success_url = "/restaurants/"

    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.owner = self.request.user         
        return super(RestaurantCreateView, self).form_valid(form)
```

- edit `models.py`
```markdown!
from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save, post_save
from django.urls import reverse

from .utils import unique_slug_generator
from .validators import validate_category

User = settings.AUTH_USER_MODEL

class RestaurantLocation(models.Model):
    owner       = models.ForeignKey(User, on_delete=models.CASCADE) # class_instance.model_set.all()
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category])
    timestamp   = models.DateTimeField(auto_now_add=True)
    update      = models.DateTimeField(auto_now_add=True)
    slug        = models.SlugField(unique=True, null=True, blank=True)

    def __str__(self):
        return self.name
    
    def get_absolute_url(self):
        # return f"/restaurants/{self.slug}"
        return reverse('restaurant-detail', kwargs={'slug': self.slug})

    @property
    def title(self):
        return self.name #obj.title
    
def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

# def rl_post_save_receiver(sender, instance, created, *args, **kwargs):
#     print('saved')
#     print(instance.timestamp)
#     if not instance.slug:
#         instance.slug = unique_slug_generator(instance)
#         instance.save()

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

# post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```

- hasil
![image](https://hackmd.io/_uploads/B1bnFgonJg.png)
