--- 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**