---
# System prepended metadata

title: PYTHON WEB DEVELOPMENT

---

---
Judul: ' PYTHON WEBDEV '
disqus: hackmd
---
{%hackmd @themes/dracula %}

# PYTHON WEB DEVELOPMENT
# NETDEV 2024
**Oleh Muhammad Alfian Alfarizi** 

# Tentang Django
Django merupakan salah satu web frameworks yang berbasis pada python. Django merupakan sebuah inovasi alternatif dalam membuat aplikasi web dengan cepat dan efektif.Contoh aplikasi yang menggunakan Django diantaranya Pinterest, Instagram, Spotify, dan Dropbox.

# Mengapa Django?
- Menggunakan bahasa python yang mudah dimengerti.
- Pada pythonnya sendiri sudah ada packet handling yang memudahkan melakukan import modul dan package.
- Dapat melakukan development dengan cepat karena fungsi pada django sudah dihandle dan templatenya sudah tersedia, sehingga hanya butuh setup dan setting database sesuai keinginan.
- Mudah diterapkan untuk web sederhana maupun web yang kompleks

# Eksekusi Modul

# Modul 1 (Getting Started with Django)

**Install Semua tools yang diperlukan beserta dependensinya**
```bash!
alfiann@ubuntu:~ sudo apt install python3-virtualenv python3.8-venv python3-pip libreadline-gplv2-dev libncursesw5-dev libpq-dev python3-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev
```
![Screenshot (1880)](https://hackmd.io/_uploads/H114kHxkR.png)


**Memastikan python yang diinginkan sudah terinstall**
```bash!
alfiann@ubuntu:~ which python3
```
![Screenshot (1881)](https://hackmd.io/_uploads/rkIBJrg1A.png)

```bash!
alfiann@ubuntu:~ which pip
```
![Screenshot (1882)](https://hackmd.io/_uploads/r1wPyrx1A.png)


**Menampilkan paket python yang terinstall**
```bash!
alfiann@ubuntu:~ pip list
```
![Screenshot (1883)](https://hackmd.io/_uploads/Hy3u1Sgy0.png)

**Membuat directory untuk virtual environment**
```bash!
alfiann@ubuntu:~ mkdir Dev && cd Dev
```
![Uploading file..._csppijs40]()


**Membuat virtual environment**
```bash!
alfiann@ubuntu:~ python3 -m venv trydjango-1.11
```

**Masuk ke directory virtual environment**
```bash!
alfiann@ubuntu:~ cd trydjango-1.11/
```

**Aktivasi virtual environment**
```bash!
alfiann@ubuntu:~ source bin/activate
```

**Menampilkan paket python yang terinstall di Virtual Environment**
```bash!
(trydjango-1.11)$ pip list
```

![Screenshot (1884)](https://hackmd.io/_uploads/SkYs1Bg1C.png)

Python yang terinstall di dalam virtual environment berbeda dengan yang terinstall di luar venv

**Instalasi Django**
```bash!
(trydjango-1.11)$ pip install django==1.11.*
```
![Screenshot (1887)](https://hackmd.io/_uploads/BkcTJreJR.png)


**Install Wheel apabila belum di install(opsional)**
```bash!
(trydjango-1.11)$ pip install wheel
```
![Screenshot (1888)](https://hackmd.io/_uploads/Sk-elBxJR.png)


**Buat Projek baru pada direktori src**
```bash!
(trydjango-1.11)$ mkdir src && cd src
```
```bash!
(trydjango-1.11)$ django-admin startproject mywebsite .
```
![Screenshot (1890)](https://hackmd.io/_uploads/SJaMerl1R.png)

# Struktur direktory project pada Django
**Membuat direktori yang diperlukan**
```bash!
(trydjango-1.11)$ mkdir mywebsite/settings/
```
![Screenshot (1891)](https://hackmd.io/_uploads/rJd4lSgJA.png)

```bash!
(trydjango-1.11)$ touch mywebsite/settings/__init__.py
```
```bash!
(trydjango-1.11)$ touch mywebsite/settings/base.py
```
```bash!
(trydjango-1.11)$ touch mywebsite/settings/production.py
```
```bash!
(trydjango-1.11)$ touch mywebsite/settings/local.py
```


**Copy mywebsite/settings.py file ke mywebsite/settings/base.py file**
```bash!
(trydjango-1.11)$ cp mywebsite/settings.py mywebsite/settings/base.py
```
![Screenshot (1895)](https://hackmd.io/_uploads/BkdcxBgyA.png)


**Edit base.py file**
```bash!
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
```

**Edit file mywebsite/settings/–init–.py**
```bash!
from .base import *
from .production import *

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

**Install beberapa tools lain**
```bash!
(trydjango-1.11)$ pip install psycopg2 
```
![Screenshot (1896)](https://hackmd.io/_uploads/ryTWWHgJA.png)

```bash!
(trydjango-1.11)$ pip install gunicorn
```
![Screenshot (1897)](https://hackmd.io/_uploads/B1qm-Hgy0.png)

```bash!
(trydjango-1.11)$ pip install dj-database-url==0.4.2
```
![Screenshot (1898)](https://hackmd.io/_uploads/HkVH-rl1C.png)

```bash!
(trydjango-1.11)$ pip install django-crispy-forms==1.6.1
```
![Screenshot (1899)](https://hackmd.io/_uploads/Sy2vbreJA.png)

```bash!
(trydjango-1.11)$ pip install pillow
```
![Screenshot (1900)](https://hackmd.io/_uploads/r1PKbBg1A.png)

**Buat requirement.txt**
```bash!
(trydjango-1.11)$ pip freeze > requirements.txt
```
![Screenshot (1901)](https://hackmd.io/_uploads/BytibSg1R.png)


**Jalankan migration & buat superuser**
```bash!
(trydjango-1.11)$ python manage.py migrate
```
![Screenshot (1902)](https://hackmd.io/_uploads/ByJ0ZBlkC.png)

```bash!
(trydjango-1.11)$ python manage.py createsuperuser
```
![Screenshot (1903)](https://hackmd.io/_uploads/HJlWMSx10.png)


**Jalankan server**
```bash!
(trydjango-1.11)$ python manage.py runserver
```
![Screenshot (1903)](https://hackmd.io/_uploads/ry3XfBlJA.png)


**Cek browser dengan 127.0.0.1:8080**
![Screenshot (1904)](https://hackmd.io/_uploads/HyeOKFARp.png)

**Copy base.py ke local.py and production.py pada settings direktori**
```bash!
(trydjango-1.11)$ cp mywebsite/settings/base.py mywebsite/settings/local.py
```
```bash!
(trydjango-1.11)$ cp mywebsite/settings/base.py mywebsite/settings/production.py
```
![Screenshot (1905)](https://hackmd.io/_uploads/r1GPMreyC.png)

**Edit file production.py**
```bash!
DEBUG = False
SECRET_KEY = "add random letter or number"
```

**Mulai Aplikasi Pertama**
```bash!
(trydjango-1.11)$ python manage.py startapp restaurants
```
![Screenshot (1908)](https://hackmd.io/_uploads/SkMe7HgyC.png)

# Struktur Direktori aplikasi Django

**Edit file views.py pada direktori restaurant**
```bash!
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	return HttpResponse("hello")
```

**Edit file urls.py pada direktori mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import home

urlpatterns = [
	url(r'^admin/', admin.site.urls),
	url(r'^$', home),
]
```
**Test pada browser**
![Screenshot (1909)](https://hackmd.io/_uploads/SkP3iKCRp.png)

# Modul 2 (HTML dan Django)

# Render with HTML
**Edit file views.py pada direktori restaurant**
```bash!
from django.http import HttpResponse
from django.shortcuts import render

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

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

# HTML using python variable

**Edit file views.py pada direktori restaurant**
```bash!
from django.http import HttpResponse
from django.shortcuts import render

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

    <head>
    <head/>
    <body>
    <h1>Hello world</h1>
    <p>This is {html_var} coming trough</p>
    </body>
    </html>
    """
	return HttpResponse(html_)
```
**Test Browser**
![Screenshot (1911)](https://hackmd.io/_uploads/Syfam9R0p.png)

# Render with templates

**Buat direktori baru dan hmtl file**
```bash!
(trydjango-1.11)$ mkdir templates/
```
```bash!
(trydjango-1.11)$ touch templates/base.html
```
![Screenshot (1913)](https://hackmd.io/_uploads/ByVO7Bx1A.png)

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

<head>
<head/>
<body>
<h1>
    Hello world
</h1>
<p>
    This is {html_var} coming trough
</p>
</body>
</html>
```

**Edit base.py pada direktori restaurant**
```bash!
TEMPLATES = [
	'DIRS' : [os.path.join(BASE_DIR,  'templates')],
]
```
**Edit views.py inside pada direktori restaurants**
```bash!
from django.http import HttpResponse
from django.shortcuts import render

def home(request):
	return render(request,  "base.html", {})
```

**Edit mywebsite/settings/–init–.py**
```bash!
from .base import *

#from .production import *

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

**Tes Browser**
![Screenshot (1915)](https://hackmd.io/_uploads/ByRk850CT.png)

# Render HTML templates using python variable

**Edit views.py dalam direktori restaurant**
```bash!
import random
from django.http import HttpResponse
from django.shortcuts import render

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

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

<head>
<head/>
<body>
<h1>
    Hello world
</h1>
<p>
    This is {{ html_var }} coming trough
</p>
<p>
    Random number is {{ num }}
</p>
</body>
</html>
```

**Tes Browser**
![Screenshot (1916)](https://hackmd.io/_uploads/B1tMWjC0T.png)

# Using context in Django templates

**Edit views.py dalam direktori restaurants**
```bash!
import random
from django.http import HttpResponse
from django.shortcuts import render

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

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

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

	    {{ some_item }}<br/>
    {% endfor %}
</p>
<p>
    Some item is {{ some_item }} 
</p>
</body>
</html>
```
**Tes Browser**
![Screenshot (1917)](https://hackmd.io/_uploads/SJqbGjAAa.png)

# Using template inheritance

**Buat file baru pada direktori template**
```bash!
(trydjango-1.11)$ touch templates/home.html
```
```bash!
(trydjango-1.11)$ touch templates/about.html
```
```bash!
(trydjango-1.11)$ touch templates/contact.html
```
![Screenshot (1918)](https://hackmd.io/_uploads/HkSc7HeyA.png)


**Edit urls.py dalam direktori mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import home, about, contact

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', home),
    url(r'^about/$', about),
    url(r'^contact/$', contact),
]
```

**Edit views.py dalam direktori restaurants**
```bash!
import random
from django.http import HttpResponse
from django.shortcuts import render

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

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

def contact(request):
    context = {
    }
    return render(request, "contact.html", context)
```
**Edit file templates/base.html**
```bash!
<!DOCTYPE html>
<html lang=en>

<head>
<title>{% block head_tittle %}SXD.com{% endblock head_tittle %}</title>
<head>
<body>
<h1>SXD.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>
```

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

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

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

{% endblock content %}
```

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

{% block content %}
<p>About</p>
{% endblock %}
```

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

{% block content %}
<p>Contact</p>
{% endblock %}
```

**Test Browser**
![Screenshot (1919)](https://hackmd.io/_uploads/BkcJVoAR6.png)

![Screenshot (1921)](https://hackmd.io/_uploads/HkjUEsCRp.png)

# Using template tag

**Buat direktori baru dan file baru dalam direktori**

```bash!
(trydjango-1.11)$ mkdir templates/snippets/
```
```bash!
(trydjango-1.11)$ touch templates/snippets/nav.html
```
```bash!
(trydjango-1.11)$ touch templates/snippets/css.html
```
```bash!
(trydjango-1.11)$ touch templates/snippets/js.html
```
```bash!
(trydjango-1.11)$ touch templates/snippets/sidebar.html
```
![Screenshot (1923)](https://hackmd.io/_uploads/SJun7HeJR.png)

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

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

<div class="container">
    {% block content %}{% endblock content %}
</div>
{% include 'snippets/js.html' %}
</body>
</html>
```

**Edit nav.html dalam direktori snippets**
```bash!
<div class="container">
<h1>SXD.com</h1>
<a href="/">Home</a>
<a href="/about/">About</a>
<a href="/contact/">Contact</a>
</div>
```

**Edit css.html dalam direktory snippets**
```bash!
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
```
**Edit js.html dalam direktory snippets**
```bash!
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
```

**Edit sidebar.html dalam direktori snippets**
```bash!
<ul>
    <li>item 1</li>
    <li>item 2</li>
    <li>item 3</li>
</ul>
```

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

{% block content %}
<h1>About</h1>
{% include 'snippets/sidebar.html' %}
{% endblock %}
```
**Tambahkan tag pada contact.html**
```bash!
{% extends "base.html" %}
{% block head_tittle %}Contact || {{ block.super }}{% endblock head_tittle %}

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

**Tes Browsing**
![Screenshot (1924)](https://hackmd.io/_uploads/HklCdjRRT.png)

# Class Based Views

**Edit views.py inside of restaurants directory**
```bash!
import random
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View

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

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

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

class ContactView(View):
    def get(self, request, *args, **kwargs):
        print(kwargs)
        context = {
        }
        return render(request, "contact.html", context)
```
**Edit urls.py dalam direktory mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import home, about, contact, ContactView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', home),
    url(r'^about/$', about),
    url(r'^contact/(?P<id>\d+)/$', ContactView.as_view()),
]
```

**Test Browser**
![Screenshot (1925)](https://hackmd.io/_uploads/ryFyXkyyA.png)
![Screenshot (1926)](https://hackmd.io/_uploads/Hkg-myJ1C.png)

# Template View

**Edit views.py dalam direktori restaurants**
```bash!
import random
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView

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

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

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

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

**Edit urls.py dalam direktori mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from restaurants.views import HomeView, AboutView, ContactView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', HomeView.as_view()),
    url(r'^about/$', AboutView.as_view()),
    url(r'^contact/$', ContactView.as_view()),
]
```

# Remembering Things

# Remembering things with Models

**Buat Superuser baru**
```bash!
(trydjango-1.11)$ python manage.py createsuperuser
```
![Screenshot (1930)](https://hackmd.io/_uploads/SkYQ4rekA.png)

**Akses Django pada browser**
![Screenshot (1932)](https://hackmd.io/_uploads/r1qs2yJ1A.png)
![Screenshot (1933)](https://hackmd.io/_uploads/ByqA2yyy0.png)

**Edit base.py pada direktori settings**
```bash!
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'restaurants',
]
```

**Edit models.py pada direktori restaurants**
```bash!
from django.db import models

class Restaurant(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
```

**Lakukan Migrate restaurant models**
```bash!
(trydjango-1.11)$ python manage.py makemigrations
```
```bash!
(trydjango-1.11)$ python manage.py migrate
```

**Edit admin.py pada direktori restaurants**
```bash!
from django.contrib import admin
from .models import Restaurant

admin.site.register(Restaurant)
```

**Tes Browser**
![Screenshot (1936)](https://hackmd.io/_uploads/BkzyWlJ10.png)

**Edit nama modul pada file models.py**
```bash!
from django.db import models

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

**Edit nama modul pada file admin.py**
```bash!
from django.contrib import admin
from .models import RestaurantLocation

admin.site.register(RestaurantLocation)
```

**Jalankan migrate setelah mengganti nama modul**
```bash!
(trydjango-1.11)$ python manage.py makemigrations
```
```bash!
(trydjango-1.11)$ python manage.py migrate
```
![Screenshot (1939)](https://hackmd.io/_uploads/B1k0bgyk0.png)

# More on Models field
**Edit models.py pada direktori restaurants**
```bash!
from django.db import models

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

**Jalankan Migrate**
```bash!
(trydjango-1.11)$ python manage.py makemigrations
You are trying to add a non-nullable field 'timestamp' to restaurantlocation without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> timezone.now
Migrations for 'restaurants':
  restaurants/migrations/0003_auto_20240313_0626.py
    - Add field category to restaurantlocation
    - Add field timestamp to restaurantlocation
```

```bash!
(trydjango-1.11)$ python manage.py migrate
```

**Tes Browser**
![Screenshot (1940)](https://hackmd.io/_uploads/ry15Qx1k0.png)


# Displaying saved data

**Edit views.py pada direktori restaurants**
```bash!
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView
from .models import RestaurantLocation

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

**Edit urls.py pada direktori mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import restaurants_listview

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', restaurants_listview),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```

**Tambahkan folder and file baru**
```bash!
(trydjango-1.11)$ mkdir -p restaurants/templates/restaurants
```
```bash!
(trydjango-1.11)$ touch restaurants/templates/restaurants/restaurants_list.html
```

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

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

**Edit models.py pada direktori restaurants**
```bash!
from django.db import models

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

    def __str__(self):
        return self.name
```
**Jalankan Migrate**
```bash!
(trydjango-1.11)$ python manage.py makemigrations
```
```bash!
(trydjango-1.11)$ python manage.py migrate
```
![Screenshot (1950)](https://hackmd.io/_uploads/SyHYX2X1R.png)

**Tes Browser**
![Screenshot (1949)](https://hackmd.io/_uploads/ByjZmnQJR.png)

-------------------------------------------------------------
# Understanding QuerySets

**Akses Django Shell**
```bash!
(trydjango-1.11)$ python manage.py shell
Python 3.8.10 (default, Nov 22 2023, 10:22:35) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
```
**Coba tampilkan data dalam database**
```bash!
>>> from restaurants.models import RestaurantLocation
>>> RestaurantLocation.objects.all()
<QuerySet [<RestaurantLocation: Restoran Sumber Dosa>]>
>>> for obj in RestaurantLocation.objects.all():
...     print(obj.name)
... 
Restoran Bapak Gatot
```
![Screenshot (1952)](https://hackmd.io/_uploads/H1sw427yA.png)

**Update Querysets**
```bash!
>>> qs = RestaurantLocation.objects.all()
>>> qs.filter(category__iexact='Restoran Sumber Dosa')
<QuerySet [<RestaurantLocation: djefry>]>
>>> qs.update(category='Rumah Makan Mewah')
```
![Screenshot (1953)](https://hackmd.io/_uploads/S1XxSn7yC.png)

**Coba untuk menambahkan restoran baru via shell**
![Screenshot (1954)](https://hackmd.io/_uploads/S1P7S27kR.png)

**Filtering Data**
```bash!
>>> qs = RestaurantLocation.objects.filter(category__iexact='Warteg').exclude(name__icontains='Podomoro')
>>> qs.count()
1
>>> qs
<QuerySet [<RestaurantLocation: Bandung>]>
```
![Screenshot (1955)](https://hackmd.io/_uploads/BJW2B2QJA.png)

**Keluar dari Shell**
```bash!
>>> exit()
(trydjango-1.11)$
```
--------------
# Generic List View

**Edit views.py dalam direktori restaurants**
```bash!
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView, ListView
from .models import RestaurantLocation

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

class RestaurantListView(ListView):

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

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

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

**Ubah nama restaurant_list.html**
```bash!
(trydjango-1.11)$ mv restaurants/tempalates/restaurants/restaurant_list.html restaurants/tempalates/restaurants/restaurantlocation_list.html 
```
![Screenshot (1957)](https://hackmd.io/_uploads/rkroDnX1C.png)

--------------
# Restaurant Profile Detail

**Edit views.py dalam direktori restaurants**
```bash!
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView
from .models import RestaurantLocation

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

class RestaurantListView(ListView):

    def get_queryset(self):
        slug = self.kwargs.get("slug")
        if slug:
            queryset = RestaurantLocation.objects.filter(
                Q(category__iexact=slug) |
                Q(category__icontains=slug)
            )
        else:
            queryset = RestaurantLocation.objects.all()
        return queryset
    
class RestaurantDetailView(DetailView):
    queryset = RestaurantLocation.objects.all()
    
    def get_object(self, *args, **kwargs):
        rest_id = self.kwargs.get('rest_id')
        obj = get_object_or_404(RestaurantLocation, id=rest_id)
        return obj
```

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

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/(?P<rest_id>\w+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```

**Buat file baru pada restaurant template**
```bash!
(trydjango-1.11)$ touch restaurants/templates/restaurants/restaurantlocation_detail.html
```

**Edit file restaurantlocation_detail.html**
```bash!
{% extends "base.html" %}

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

{% block content %}
<h1>{{ object.name }} <small> {{ object.category }}</small></h1>
<p>{{ object.location }}</p>
<p>{{ object.timestamp }}, Updated {{ object.update|timesince }} ago</p>
{% endblock %}
```
![Screenshot (1959)](https://hackmd.io/_uploads/rkPku3myR.png)

-----------------------
# SlugField & Unique Slug Generator

**Edit Models.py**
```bash!
from django.db import models

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

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

**Jalankan Migrate**
```bash!
(trydjango-1.11)$ python manage.py makemigrations
```
```bash!
(trydjango-1.11)$ python manage.py migrate
```

**File baru pada direktori restaurants**
```bash!
(trydjango-1.11)$ touch restaurants/utils.py
```

**Edit utils.py pada direktori restaurants**
```bash!
import random
import string
from django.utils.text import slugify

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

def unique_slug_generator(instance, new_slug=None):
    if new_slug is not None:
        slug = new_slug
    else:
        slug = slugify(instance.title)

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

-----------------------------
# Signal for Unique Slug

**Edit models.py pada direktori restaurants**
```bash!
from django.db import models
from django.db.models.signals import pre_save, post_save
from .utils import unique_slug_generator

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

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

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

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

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

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

**Tes Browser**
![Screenshot (1973)](https://hackmd.io/_uploads/H1JVlbEyC.png)

---------------------------
# Forms, View, Model, and more

# Slug as URL Params

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

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

**Edit views.py pada direktori restaurants**
```bash!
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView
from .models import RestaurantLocation

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

class RestaurantListView(ListView):

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

**Edit restaurantlocation_list.html pada direktori restaurants/template**
```bash!
{% extends "base.html" %}
{% block head_tittle %}Restaurants | {{ block.super }}{% endblock head_tittle %}
{% block content %}
<h1>Restaurants List</h1>

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

**Tes Broser**
![Screenshot (1962)](https://hackmd.io/_uploads/Bk50qnmyA.png)

**Akses 127.0.0.1:8000/restaurants**
![Screenshot (1964)](https://hackmd.io/_uploads/HJ3bj3QkR.png)

------------------------------------
# Saving the Data Hard & Wrong Way

**Buat file forms.py pada direktori restaurants**
```bash!
from django import forms

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

**Edit views.py**
```bash!
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .forms import RestaurantCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    if request.method == "GET":
        print("get data")
    if request.method == "POST":
        print("post data")
        print(request.POST)
        title = request.POST.get("title")
        location = request.POST.get("location")
        category = request.POST.get("category")
        obj = RestaurantLocation.objects.create(
            title=title,
            location=location,
            category=category
        )
        return HttpResponseRedirect("/restaurants/")
    template_name = 'restaurants/form.html'
    context = {}
    return render(request, template_name, context)

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

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

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

**Buat File form.html di dalam direktori restaurant**
```bash!
{% extends "base.html" %}

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

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

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

{% endblock %}
```

**Edit urls.py pada direktory my website**
```bash!
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/create/$', RestaurantListView.as_view()),
    url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```
**Edit utils.py pada direktori restaurants**
```bash!
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
```
**Tes Browser**
![Screenshot (1977)](https://hackmd.io/_uploads/BJSmE7O10.png)

![Screenshot (1978)](https://hackmd.io/_uploads/Bkb4EmuJ0.png)

-----------------------

# The Power of Django Forms

**Edit file views.py**
```bash!
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .forms import RestaurantCreateForm
from .models import RestaurantLocation

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

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

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

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

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

**Edit file forms.html pada direktori templates restaurant**
```bash!
{% extends "base.html" %}

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

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

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

{% endblock %}
```

**Edit file form.py pada direktori src restaurants**
```bash!
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
```
**Tes Browser**
![Screenshot (1979)](https://hackmd.io/_uploads/rJG8N7uJR.png)


-----------------------------------
# The Extra Power of Django Model Forms

**Edit file forms.py didalam direktori restaurants**
```bash!
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 file form.html didalam direktori templas restaurants**
```bash!
 {% extends "base.html" %}

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

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

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

{% endblock %}
```

**Edit file views.py didalam direktori restaurants**
```bash!
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView
from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    form = RestaurantCreateForm(request.POST or None)
    errors = None
    if form.is_valid():
        form.save()
        return HttpResponseRedirect("/restaurants/")
    if form.errors:
        errors = form.errors

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

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

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

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

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

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

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html')),
    url(r'^restaurants/$', RestaurantListView.as_view()),
    url(r'^restaurants/create/$', RestaurantCreateView.as_view()),
    url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
    url(r'^about/$', TemplateView.as_view(template_name='about.html')),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html')),
]
```

**Tes Browser**
![Screenshot (1980)](https://hackmd.io/_uploads/BkCuVQOyA.png)

--------------------------------
# Simple Effective Validation

**Edit file forms.py di direktori restaurants**
```bash!
from django import forms
from .models import RestaurantLocation
from .validators import validate_category

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

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

class RestaurantLocationCreateForm(forms.ModelForm):
    category = forms.CharField(required=False, validators=[validate_category])
    class Meta:
        model = RestaurantLocation
        fields = [
            'name',
            'location',
            'category',
        ]

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

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

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

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

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

{% endblock %}
```

**Buat file validators.py di direktori restaurants**
```bash!
from django.core.exceptions import ValidationError

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

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

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

def validate_category(value):
    cat = value.capitalize()
    if not value in CATEGORIES and not cat in CATEGORIES:
        raise ValidationError(f"{value} not a valid category")
```

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

# Create your models here.
from django.db import models
from django.db.models.signals import pre_save, post_save
from .utils import unique_slug_generator
from .validators import validate_category
# Create your models here.
class RestaurantLocation(models.Model):
    name        = models.CharField(max_length=120)
    location    = models.CharField(max_length=120, null=True, blank=True)
    category    = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category])
    timestamp   = models.DateTimeField(auto_now=True)
    update      = models.DateTimeField(auto_now_add=True)
    slug        = models.SlugField(unique=True, null=True, blank=True)

    def __str__(self):
        return self.name

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

def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

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

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

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



**Jalankan Migrate**
```bash!
(trydjango-1.11)$ python manage.py makemigrations
```
```bash!
(trydjango-1.11)$ python manage.py migrate
```
**Akses lagi dan tambahkan name**
![Screenshot (1984)](https://hackmd.io/_uploads/HyFpEXO1R.png)

--------------
# Letting User own Data

**Edit models.py pada direktori restaurants**
```bash!
from django.db import models

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

User = settings.AUTH_USER_MODEL

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

    def __str__(self):
        return self.name

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

def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

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

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

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

**Jalankan perintah berikut**
```bash!
(trydjango-1.11)$ python manage.py shell
```
```bash!
>>>from django.contrib.auth import get_user_model
>>>User = get_user_model()
>>>User.objects.all()
>>>cfe_user = User.objects.get(id=1)
>>>cfe_user
>>>cfe_user.username
>>>cfe_user.email
>>>cfe_user.password
>>>cfe_user.is_active
>>>cfe_user.is_staff
>>>cfe_user
>>>obj = cfe_user
obj
>>>instance = cfe_user
instance
>>>instance.restaurantlocation_set.all()
instance.restaurantlocation_set.filter(category__iexact='Mexican')
>>>User.objects.all()
>>>User.objects.get(id=2)
>>>jm_user = User.objects.get(id=2)
>>>jm_user.restaurantlocation_set.all()
```

-----------------
# Associate User to Form Data in FBV

**Edit file views.py pada direktori restaurants**
```bash!
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView
from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

def restaurant_createview(request):
    form = RestaurantCreateForm(request.POST or None)
    errors = None
    if form.is_valid():
        if request.user.is_authenticated():
            instance = form.save(commit=False)
            instance.owner = request.user
            instance.save()
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors

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

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

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

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

class RestaurantCreateView(CreateView):
    form_class = RestaurantLocationCreateForm
    template_name = 'restaurants/form.html'
    success_url = "/restaurants"
```
**Edit file urls.py pada direktori mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from restaurants.views import (
    restaurant_createview,
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

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

------------------------
# Login Required to View

**Edit file base.py pada direktori mywebsite/settings**
```bash!
ROOT_URLCONF = 'mywebsite.urls'
LOGIN_URL = '/login/'
```

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

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

**Edit file views.py pada direktori restaurants**
```bash!
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.views import View
from django.views.generic import TemplateView, ListView, DetailView, CreateView
from .forms import RestaurantCreateForm, RestaurantLocationCreateForm
from .models import RestaurantLocation

@login_required(login_url='/login/')
def restaurant_createview(request):
    form = RestaurantCreateForm(request.POST or None)
    errors = None
    if form.is_valid():
        if request.user.is_authenticated():
            instance = form.save(commit=False)
            instance.owner = request.user
            instance.save()
            return HttpResponseRedirect("/restaurants/")
        else:
            return HttpResponseRedirect("/login/")
    if form.errors:
        errors = form.errors

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

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

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

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

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

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

**Tes Browser dengan metode Incognito**
![Screenshot (1991)](https://hackmd.io/_uploads/HJ0brfDJC.png)

![Screenshot (1990)](https://hackmd.io/_uploads/HyRfBGPyR.png)

-------------------
# Login View

**Buat file template login.html pada direktori registration**
```bash!
(trydjango-1.11) ubuntu@ubunu2004:~/Dev/trydjango-1.11/src$ cd templates/
```
```bash!
(trydjango-1.11) ubuntu@ubunu2004:~/Dev/trydjango-1.11/src/templates$ mkdir registration
```
```bash!
(trydjango-1.11) ubuntu@ubunu2004:~/Dev/trydjango-1.11/src/templates$ cd registration/
```
```bash!
(trydjango-1.11) ubuntu@ubunu2004:~/Dev/trydjango-1.11/src/templates/registration$ touch login.html
```
![Screenshot (1994)](https://hackmd.io/_uploads/r1z2rGv1A.png)

**Edit file login.html**
```bash!
{% extends "base.html" %}

{% block content %}

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

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

<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>
</table>

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

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

{% endblock %}
```
**Edit file urls.py pada direktori mywebsite**
```bash!
from django.conf.urls import url
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from restaurants.views import (
    restaurant_createview,
    restaurants_listview,
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

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

**Tes Browser ke 127.0.0.1:8000/restaurant/create di browser incognito. Nantinya diarahkan ke page login, setelah login, baru bisa menambahkan restaurant**

![Screenshot (2006)](https://hackmd.io/_uploads/Hku7LWu1R.png)

![Screenshot (2007)](https://hackmd.io/_uploads/HJSVUWOyA.png)


----------------------
# Using Reverse to Shortcut URLS

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

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

**Buat file urls.py pada direktori restaurants**
```bash!
---
from django.conf.urls import url
from .views import (
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    url(r'^$', RestaurantListView.as_view(), name='list'),  
    url(r'^create/$', RestaurantCreateView.as_view(), name='create'),  
    url(r'^(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view(), name='detail'),  
]
```

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

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
    url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^restaurants/', include('restaurants.urls', namespace='restaurants')),
    url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```

**Edit file nav.html pada direktori templates/snippets**
```bash!
<div class="container">
<h1>mcxxkim.com</h1>
<a href='{% url "home" %}'>Home</a>
<a href='{% url "about" %}'>About</a>
<a href='{% url "contact" %}'>Contact</a>
<a href='{% url "restaurants:list" %}'>Restaurants</a>
</div>
```

**Edit file models.py di dalam direktori restaurants**
```bash!
from django.db import models

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

User = settings.AUTH_USER_MODEL

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

    def __str__(self):
        return self.name

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

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

def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

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

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

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

**Akses 127.0.0.1:8000 pada browser dengan akses satu persatu navbar (Home, About. Contact, Restaurant)**

![Screenshot (2008)](https://hackmd.io/_uploads/ByBhI-O1A.png)

-------------------
# Menu Items App

**Buat menus**
```bash!
from django.conf import settings
from django.db import models
from restaurants.models import RestaurantLocation

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

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

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

    def get_excludes(self):
        return self.excludes.split(",")
```

**Edit file base.py di dalam direktori mywebsite/settings. Tambahkan menus di bagian INSTALLED_APPS**
```bash!
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'menus',
    'restaurants',
]
```

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

admin.site.register(Item)
```

**Jalankan Migrate**
```bash!
python manage.py makemigrations
```
```bash!
python manage.py migrate
```

**Indikator keberhasilannya adalah ketika Add Items berhasil**

------------------
# Menu Items Views

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

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

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

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

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

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

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

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

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

**Buat file forms.py di dalam direktori menus dan edit filenya**
```bash!
from django import forms
from .models import Item

class ItemForm(forms.ModelForm):
    class Meta:
       model = Item
       fields = [
           'restaurant',
           'name',
           'contents',
           'excludes',
           'public',
        ]
```


----------------------
# Limiting Form Field to QuerySet
```bash!
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from .forms import ItemForm
from .models import Item

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

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

class ItemCreateView(CreateView):
    template_name = 'form.html'
    form_class = ItemForm

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.user = self.request.user
        return super(ItemCreateView, self).form_valid(form)

    def get_form_kwargs(self):
        kwargs = super(ItemCreateView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

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

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

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

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

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

**Edit file forms.py pada direktori menus**
```bash!
from django import forms
from .models import Item
from restaurants.models import RestaurantLocation

class ItemForm(forms.ModelForm):
    class Meta:
       model = Item
       fields = [
           'restaurant',
           'name',
           'contents',
           'excludes',
           'public',
        ]

    def __init__(self, user=None, *args, **kwargs):
        print(user)
        print(kwargs)
        super(ItemForm, self).__init__(*args,**kwargs)
        self.fields['restaurant'].queryset = RestaurantLocation.objects.filter(owner=user).exclude(item__isnull=False)
```

**Edit file urls.py pada direktori menus**
```bash!
from django.conf.urls import url

from .views import (
     ItemCreateView,
     ItemDetailView,
     ItemListView,
     ItemUpdateView,
)
urlpatterns = [
     url(r'^create/$', ItemCreateView.as_view(), name='create'),
     url(r'^(?P<pk>\d+)/edit/$', ItemUpdateView.as_view(), name='update'),
     url(r'^(?P<pk>\d+)/$', ItemDetailView.as_view(), name='detail'),
     url(r'$', ItemListView.as_view(), name='List'),
]
```

**Tes Broser 127.0.0.1:8000/items/1/edit di browser maka anda berhasil**

-----------------
# Personalize Items

**Edit file views.py pada direktori restaurants**
```bash!
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, UpdateView

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


class RestaurantListView(LoginRequiredMixin, ListView):
    def get_queryset(self):
        return RestaurantLocation.objects.filter(owner=self.request.user)


class RestaurantDetailView(LoginRequiredMixin, DetailView):
     def get_queryset(self):
        return RestaurantLocation.objects.filter(owner=self.request.user)

class RestaurantCreateView(LoginRequiredMixin, CreateView):
    form_class = RestaurantLocationCreateForm
    login_url = '/login/'
    template_name = '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)

    def get_context_data(self, *args, **kwargs):
        context = super(RestaurantCreateView, self).get_context_data(*args, **kwargs)
        context['title'] = 'Add Restaurant'
        return context


class RestaurantUpdateView(LoginRequiredMixin, UpdateView):
    form_class = RestaurantLocationCreateForm
    login_url = '/login/'
    template_name = 'restaurants/detail-update.html'
    #success_url = "/restaurants/"

    def get_context_data(self, *args, **kwargs):
        context = super(RestaurantUpdateView, self).get_context_data(*args, **kwargs)
        name = self.get_object().name
        context['title'] = f'Update Restaurant: {name}'
        return context

    def get_queryset(self):
        return RestaurantLocation.objects.filter(owner=self.request.user)
```

**Edit file urls.py pada direktori restaurants**
```bash!
from django.conf.urls import url
from .views import (
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView,
    RestaurantUpdateView
)

urlpatterns = [
    url(r'^create/$', RestaurantCreateView.as_view(), name='create'),
#    url(r'^(?P<slug>[\w-]+)/edit/$', RestaurantUpdateView.as_view(), name='edit'),
    url(r'^(?P<slug>[\w-]+)/$', RestaurantUpdateView.as_view(), name='detail'),
    url(r'$', RestaurantListView.as_view(), name='list'),
]
```

**Edit file urls.py di dalam direktori menus**
```bash!
from django.conf.urls import url

from .views import (
     ItemCreateView,
     ItemDetailView,
     ItemListView,
     ItemUpdateView,
)
urlpatterns = [
     url(r'^create/$', ItemCreateView.as_view(), name='create'),
#     url(r'^(?P<pk>\d+)/edit/$', ItemUpdateView.as_view(), name='edit'),
     url(r'^(?P<pk>\d+)/$', ItemUpdateView.as_view(), name='detail'),
     url(r'$', ItemListView.as_view(), name='list'),
]
```

**Edit nav.html pada direktori templates/snippets**
```bash!
<div class="container">
<h1>mcxxkim.com</h1>
<a href='{% url "home" %}'>Home</a>
<a href='{% url "about" %}'>About</a>
<a href='{% url "contact" %}'>Contact</a>
{% if request.user.is_authenticated %}
<a href='{% url "restaurants:list" %}'>Restaurants</a>
<a href='{% url "menus:list" %}'>Menu Items</a>
{% endif %}
</div>
```
**Buat file baru**
```bash!
touch restaurants/templates/restaurants/form.html
```
```bash!
touch restaurants/templates/restaurants/detail-update.html
```
```bash!
touch templates/menus/detail-update.html
```
```bash!
touch templates/snippets/form-snippets.html
```

**Edit form.html**
```bash!
{% extends "base.html" %}

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

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

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

{% endblock %}
```

**Edit detail-update.html**
```bash!
{% extends "base.html" %}

{% block head_title %}{{ form.instance.title }} | Restaurants | {{ block.super }}{% end>

{% block content %}
<h1>{{ form.instance.title }} <small> {{ form.instance.category }}</small></h1>
<p>{{ form.instance.location }}</p>
<p>{{ form.instance.timestamp }}, Updated {{ form.instance.updated|timesince }} ago</p>

<hr/>
<h3>Make Changes</h3>
{% include 'snippets/form-snippets.html' with form=form %}


</div>
</div>
{% endblock %}
```

**Edit form-snippets.html**
```bash!
{% if form.errors.non_field_errors %}
{{ form.errors.non_field_errors }}
{% endif %}
<form method='POST'> {% csrf_token %}
    {{ form.as_p }}
    <button type='submit'>Save</button>
</form>
```

**Tes Browser. Apabila bisa mengubah atau menambah items pada restaurant artinya konfigurasi sudah berhasil**

--------------------
# User Profil View

**Jalankan Profil**
```bash!
python manage.py startapp profiles
```

**Edit views.py pada direktori profiles**
```bash!
from django.contrib.auth import get_user_model
from django.http import Http404
from django.shortcuts import render, get_object_or_404
from django.views.generic import DetailView

User = get_user_model()

class ProfileDetailView(DetailView):
    template_name = 'profiles/user.html'

    def get_object(self):
        username = self.kwargs.get("username")
        if username is None:
            raise Http404
        return get_object_or_404(User, username__iexact=username, is_active=True)
```

**Buat file urls.py pada direktori profiles dan edit filenya**
```bash!
touch profiles/urls.py
```
```bash!
nano profiles/urls.py
```
```bash!
from django.conf.urls import url
from .views import ProfileDetailView

urlpatterns = [
    url(r'^(?P<username>[\w-]+)/$', ProfileDetailView.as_view(), name='detail'),
]
```

**Edit file urls.py pada direktori mywebsite**
```bash!
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from restaurants.views import (
    RestaurantListView,
    RestaurantDetailView,
    RestaurantCreateView
)

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
    url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^u/', include('profiles.urls', namespace='profiles')),
    url(r'^items/', include('menus.urls', namespace='menus')),
    url(r'^restaurants/', include('restaurants.urls', namespace='restaurants')),
    url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```

**Edit file base.py pada direktori mywebsite/settings**
```bash!
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'menus',
    'profile',
    'restaurants',
]
```

**Buat direktori /profiles di dalam templates, dan buat file baru dalam direktori templates/profiles bernama user.html dan edit.**
```bash!
mkdir templates/profiles
```
```bash!
touch templates/profiles/user.html
```
```bash!
nano templates/profiles/user.html
```

```bash!
{% extends "base.html" %}
{% block head_title %}{{ user.username }} | {{ block.super }}{% endblock head_title %}
{% block content %}
<h1>{{ user.username }}</h1>
<hr/>
{% if user.restaurantlocation_set.all %}
{% for rest in user.restaurantlocation_set.all %}
<li><b>{{ rest.title }}</b> {{ rest.location }} | {{ rest.category }}</li>
<p>
    <b>Items</b>
    <ul>
        {% for item in rest.item_set.all %}
        <li style='margin-bottom: 15px;'>{{ item.name }}<br/>
            {% for ing in item.get_contents %}
            <span style='padding: 2px 4px; margin-right:4px; background-color: #ccc;'>{{ ing }}</sp>
            {% endfor %}
        </li>
        {% endfor %}
    </ul>
</p>
{% endfor %}
{% else %}
<p class='lead'>No Items Found</p>
{% endif %}
{% endblock %}
```

**Edit file models.py pada direktori restaurants**
```bash!
from django.db import models

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

User = settings.AUTH_USER_MODEL

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

    def __str__(self):
        return self.name

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

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

def rl_pre_save_receiver(sender, instance, *args, **kwargs):
    instance.category = instance.category.capitalize()
    if not instance.slug:
        instance.slug = unique_slug_generator(instance)

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

pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)

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

**Tes Browser menggunakan user anda, apabila berhasil maka konfigurasi anda berhasil dan page yang ditampilkan sesuai**

-------------------------
# Style Profile with Bootstrap

**Edit file user.html pada direktori templates/profiles**
```bash!
{% extends "base.html" %}
{% block head_title %}{{ user.username }} | {{ block.super }}{% endblock head_title %}
{% block content %}
<h1>{{ user.username }}</h1>
<hr/>
{% if user.restaurantlocation_set.all %}
{% for rest in user.restaurantlocation_set.all %}
<div class='row'>
    <div class='col-sm-12'>
        <div class='thumbnail'>
            <h4>{{ rest.title }}</h4>
            <p>{{ rest.location }} | {{ rest.category }}</p>
            <ul>
                {% for item in rest.item_set.all %}
                <li style='margin-bottom: 15px;'>
                    <strong>{{ item.name }}</strong><br/>
                    <span>{{ item.contents }}</span>
                </li>
                {% endfor %}
            </ul>
            <hr/>
        </div>
    </div>
</div>
{% endfor %}
{% else %}
<p class='lead'>No Items Found</p>
{% endif %}
{% endblock %}
```

**Tes Browser. Tampilan akan lebih rapi dan jika itu terjadi maka konfigurasi berhasil**

-------------------------
# Adding a Robust Search

**Edit views.py pada direktori profiles**
```bash!
from django.contrib.auth import get_user_model
from django.http import Http404
from django.shortcuts import render, get_object_or_404
from django.views.generic import DetailView
from menus.models import Item
from restaurants.models import RestaurantLocation

User = get_user_model()

class ProfileDetailView(DetailView):
    template_name = 'profiles/user.html'

    def get_object(self):
        username = self.kwargs.get("username")
        if username is None:
            raise Http404
        return get_object_or_404(User, username__iexact=username, is_active=True)

    def get_context_data(self, **kwargs):
        context = super(ProfileDetailView, self).get_context_data(**kwargs)
        user = context['user']
        query = self.request.GET.get('q')
        items_exist = Item.objects.filter(user=user).exists()
        qs = RestaurantLocation.objects.filter(owner=user)
        if query:
            qs = qs.filter(name__icontains=query)
        if items_exist and qs.exists():
            context['locations'] = qs
        return context
```

**Edit file user.html pada direktori templates/profiles**
```bash!
{% extends "base.html" %}
{% block head_title %}{{ user.username }} | {{ block.super }}{% endblock head_title %}
{% block content %}
<h1>{{ user.username }}</h1>
<form class="form-inline mb-3" method="GET" action="">
  <div class="form-group">
    <input type="text" class="form-control mr-2" placeholder="Search..." name="q" value="{{ request.GET.q }}">
    <button type="submit" class="btn btn-primary">Search</button>
  </div>
</form>
<hr/>
{% if user.restaurantlocation_set.all %}
{% for rest in user.restaurantlocation_set.all %}
<div class='row'>
    <div class='col-sm-12'>
        <div class='thumbnail'>
            <h4>{{ rest.title }}</h4>
            <p>{{ rest.location }} | {{ rest.category }}</p>
            <ul>
                {% for item in rest.item_set.all %}
                <li style='margin-bottom: 15px;'>
                    <strong>{{ item.name }}</strong><br/>
                    <span>{{ item.contents }}</span>
                </li>
                {% endfor %}
            </ul>
            <hr/>
        </div>
    </div>
</div>
{% endfor %}
{% else %}
<p class='lead'>No Items Found</p>
{% endif %}
{% endblock %}
```

**Tes Browser. Jika muncul kolom pencarian dan bisa filter maka konfigurasi berhasil**

---------------------------
# Handling User dan Followers

# Follow Users

**Edit file models.py pada direktori profiles**
```bash!
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save

User = settings.AUTH_USER_MODEL

class Profile(models.Model):
    user              = models.OneToOneField(User) # user.profile
    followers         = models.ManyToManyField(User, related_name='is_following', blank=True) # user.is_following.all()
    #following         = models.ManyToManyField(User, related_name='following', blank=True) # user.following.all()
    activated         = models.BooleanField(default=False)
    timestamp         = models.DateTimeField(auto_now_add=True)
    updated           = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.username

def post_save_user_receiver(sender, instance, created, *args, **kwargs):
    if created:
        profile, is_created = Profile.objects.get_or_create(user=instance)
        default_user_profile = Profile.objects.get_or_create(user__id=1)[0] #user__username=
        default_user_profile.followers.add(instance)
        #profile.followers.add(default_user_profile.user)
        #profile.followers.add(2)

post_save.connect(post_save_user_receiver, sender=User)
```

**Edit file admin.py di dalam direktori profiles**
```bash!
from django.contrib import admin

from .models import Profile

admin.site.register(Profile)
```

**Edit file admin.py di dalam direktori profiles**
```bash!
from django.contrib import admin

from .models import Profile

admin.site.register(Profile)
```

**Jalankan Migrate**
```bash!
python manage.py makemigrations
```
```bash!
python manage.py migrate
```
```bash!
python manage.py runserver
```

**Tes Browser. Coba akses apakah section profiles sudah ada lalu pada bagian profiles terdapat followers. Jika ada maka aktifkan dan konfigurasi berhasil**

-------------------------
# Follow Button Form
```bash!
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import DetailView, View
from menus.models import Item
from restaurants.models import RestaurantLocation
from .models import Profile

User = get_user_model()

class ProfileFollowToggle(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        user_to_toggle = request.POST.get("username")
        profile_ = Profile.objects.get(user__username__iexact=user_to_toggle)
        user = request.user
        if user in profile_.followers.all():
            profile_.followers.remove(user)
        else:
            profile_.followers.add(user)

        return redirect(f"/u/{profile_.user.username}/")

class ProfileDetailView(DetailView):
    template_name = 'profiles/user.html'
    model = User  # Menggunakan model User sebagai model untuk detail view

    def get_object(self):
        username = self.kwargs.get("username")
        if username is None:
            raise Http404
        return get_object_or_404(User, username__iexact=username, is_active=True)

    def get_context_data(self, **kwargs):
        context = super(ProfileDetailView, self).get_context_data(**kwargs)
        user = context['user']
        is_following = False
        if user.profile in self.request.user.is_following.all():
            is_following = True
        context['is_following'] = is_following
        query = self.request.GET.get('q')
        items_exist = Item.objects.filter(user=user).exists()
        qs = RestaurantLocation.objects.filter(owner=user)
        if items_exist and qs.exists():
            context['locations'] = qs
        return context
```

**Edit file models.py pada direktori profiles**
```bash!
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save

User = settings.AUTH_USER_MODEL

class ProfileManager(models.Manager):
    def toggle_follow(self, request_user, username_to_toggle):
        profile_ = Profile.objects.get(user__username__iexact=username_to_toggle)
        user = request_user
        is_following = False
        if user in profile_.followers.all():
            profile_.followers.remove(user)
        else:
            profile_.followers.add(user)
            is_following = True
        return profile_, is_following

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    followers = models.ManyToManyField(User, related_name='is_following', blank=True)
    activated = models.BooleanField(default=False)
    timestamp = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    objects = ProfileManager()

    def __str__(self):
        return self.user.username

def post_save_user_receiver(sender, instance, created, *args, **kwargs):
    if created:
        profile, is_created = Profile.objects.get_or_create(user=instance)
        default_user_profile = Profile.objects.get_or_create(user__id=1)[0]
        default_user_profile.followers.add(instance)
        profile.followers.add(default_user_profile.user)  # Mengganti profile.followers.add(2) menjadi profile.followers.add(default_user_profile.user)

post_save.connect(post_save_user_receiver, sender=User)
```

**Edit file urls.py pada direktori mywebsite**
```bash!
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from profiles.views import ProfileFollowToggle

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
    url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^profile-follow/$', ProfileFollowToggle.as_view(), name='follow'),
    url(r'^u/', include('profiles.urls', namespace='profiles')),
    url(r'^items/', include('menus.urls', namespace='menus')),
    url(r'^restaurants/', include('restaurants.urls', namespace='restaurants')),
    url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```

**Buat file follow_form.html di dalam direktori templates/profiles/snippet**

```bash!
mdkir templates/profiles/snippet
```
```bash!
touch templates/profiles/snippet.follow_form.html
```
```bash!
nano templates/profiles/snippet.follow_form.html
```

```bash!
{% if request.user.is_authenticated %}
<form class='form' method='POST' action='{% url "follow" %}'> {% csrf_token %}
<input type='hidden' name='username' value='{% if username %}{{ username }}{% else %}jmitchel3{% endif %}'/>
<button class='btn {% if is_following %}btn-default{% else %}btn-primary{% endif %}'>{% if is_following %}Unfollow{% else %}Follow{% endif %}</button>
</form>
{% endif %}
```
**Tes Browser 127.0.0.1:8000/u/(nama user) dan menggunakan django admin**

-------------------------
# Following Home Page Feed

**Edit views.py pada direktori menus**
```bash!
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.views.generic import View, ListView, DetailView, CreateView, UpdateView
from .forms import ItemForm
from .models import Item


class HomeView(View):
    def get(self, request, *args, **kwargs):
        if not request.user.is_authenticated():
            object_list = Item.objects.filter(public=True).order_by('-timestamp')
            return render(request, "home.html", {"object_list": object_list})

        user = request.user
        is_following_user_ids = [x.user.id for x in user.is_following.all()]
        qs = Item.objects.filter(user__id__in=is_following_user_ids, public=True).order_by("-updated")[:3]
        return render(request, "menus/home-feed.html", {'object_list': qs})

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

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

class ItemCreateView(CreateView):
    template_name = 'form.html'
    form_class = ItemForm

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.user = self.request.user
        return super(ItemCreateView, self).form_valid(form)

    def get_form_kwargs(self):
        kwargs = super(ItemCreateView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

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

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

class ItemUpdateView(LoginRequiredMixin, UpdateView):
    template_name = 'menus/detail-update.html'
    form_class = ItemForm

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

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

**Edit urls.py di dalam direktori mywebsite**
```bash!
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from menus.views import HomeView
from profiles.views import ProfileFollowToggle

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', HomeView.as_view(), name='home'),
    url(r'^login/$', LoginView.as_view(), name='login'),
    url(r'^profile-follow/$', ProfileFollowToggle.as_view(), name='follow'),
    url(r'^u/', include('profiles.urls', namespace='profiles')),
    url(r'^items/', include('menus.urls', namespace='menus')),
    url(r'^restaurants/', include('restaurants.urls', namespace='restaurants')),
    url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
    url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```

**Buat direktori menus di dalam restaurants/templates dan baut file baru bernama home-feed.html di dalam direktori restaurants/templates/menus, serta edit file home-feed.html**
```bash!
{% extends 'base.html' %}

{% block content %}
  {% for obj in object_list %}
    <div class='row'>
      <div class='col-sm-12'>
        <div class='thumbnail'>
          <h3><a href='{% url "profiles:detail" username=obj.user.username %}'>{{ obj.user.username }}</a></h3>
          <h4>{{ obj.name }}</h4>
          <p>{{ obj.restaurant.title }} | {{ obj.restaurant.location }} | {{ obj.restaurant.category }}</p>
          <p>
            <ul>
              <li style='margin-bottom: 15px;'><b>{{ item.name }}</b><br/>
                {% for ing in obj.get_contents %}
                  <a href='{{ request.path }}?q={{ ing }}'>{{ ing }}</a>
                {% endfor %}
              </li>
            </ul>
          </p>
        </div>
      </div>
    </div>
  {% endfor %}
{% endblock %}
```

**Tes Browser 127.0.0.1:8000 dengan akun yang sudah terlogin, dan akan mendirect ke landing page**































