---
# System prepended metadata

title: DJANGO PART 2

---

# DJANGO PART 2
##### personalize items
Materi ini berfokus pada Personalisasi Data, di mana setiap user hanya bisa melihat dan mengelola data milik mereka sendiri. Selain itu, dilakukan efisiensi template dengan menggabungkan halaman Detail dan Update menjadi satu tampilan, serta penggunaan Snippet untuk form agar kodingan lebih bersih

* edit file views.py
```
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)
```
* membuat file form_snippet.html di template/snippet
```
<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save Changes</button>
</form>
```
* edit file item_detail.html
```
{% extends "base.html" %}

{% block isi %}
<h1>{{ object.name }}</h1>
<p>Restaurant: {{ object.restaurant }}</p>

<h3>Current Contents:</h3>
<ul>
    {% for item in object.get_contents %}<li>{{ item }}</li>{% endfor %}
</ul>

<hr/>
<h3>Make Changes</h3>
{% include 'snippets/form_snippet.html' with form=form %}
{% endblock isi %}
```
* update file nav.html
```
<div class='container'>
    <h1>Webaing.com</h1>
    <a href='{% url "home" %}'>Home</a>
    {% if request.user.is_authenticated %}
        <a href='{% url "restaurant:list" %}'>Restaurants</a>
        <a href='{% url "menus:list" %}'>Menus</a>
        <a href='{% url "logout" %}'>Logout</a>
    {% else %}
        <a href='{% url "login" %}'>Login</a>
    {% endif %}
</div>
```
* hasilnya ketika kita ke halaman home namun belum login, menu restoran dan items tidak muncul ![image](https://hackmd.io/_uploads/rkWi5pVvWx.png)
##### User Profil View
Materi ini membahas pembuatan aplikasi baru bernama Profiles yang berfungsi untuk menampilkan profil publik user
* membuat app baru bernama profiles
```
(venv) C:\Dev\trydjango1-11\src\projekaing>python manage.py startapp profiles
```
* edit file views.py
```
from django.contrib.auth import get_user_model
from django.http import Http404
from django.shortcuts import 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)
```
* edit file base.py
```
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'menus',
    'restaurant',
    'profiles',
]
```
* buat file urls.py di profile
![image](https://hackmd.io/_uploads/S1mxgAEv-e.png)
* update urls.py utama
```
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html'), name='home'),
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'), # HARUS ADA INI
    path('about/', TemplateView.as_view(template_name='about.html'), name='about'),
    path('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'),
    path('items/', include('menus.urls', namespace='menus')),
    path('restaurant/', include('restaurant.urls', namespace='restaurant')),
    path('u/', include('profiles.urls', namespace='profiles')),
]
```
* membuat tampilan user di template/profiles dengan nama user.html
![image](https://hackmd.io/_uploads/SyZFxANP-g.png)
* hasil akhirnya sebagai berikut : ![image](https://hackmd.io/_uploads/rJ6pZRVPZx.png)
##### Style Profile with Bootstrap
Materi ini berfokus pada pembenahan tampilan menggunakan Bootstrap 3
* edit user.html
```
{% extends "base.html" %}

{% block isi %}
<h1>{{ object.username }}</h1>
<hr/>

<div class="row">
    <div class="col-sm-12">
        {% for rest in object.restaurantlocation_set.all %}
        <div class="thumbnail" style="margin-bottom: 15px;">
            <div class="caption">
                <h4>{{ rest.name }}</h4>
                <p>{{ rest.location }} | {{ rest.category }}</p>
                
                <p><b>Menu Items:</b></p>
                <ul>
                    {% for item in rest.item_set.all %}
                        <li style="margin-bottom: 10px;">
                            <b>{{ item.name }}</b> <br/>
                            {{ item.contents }}
                        </li>
                    {% endfor %}
                </ul>
            </div>
        </div>
        {% empty %}
        <p class="lead">No restaurants found.</p>
        {% endfor %}
    </div>
</div>

{% endblock isi %}
```
* hasilnya tampilannya akan lebih rapi![image](https://hackmd.io/_uploads/rknc7AEPZl.png)

##### Adding a Robust Search
Materi ini membahas pembuatan fitur Pencarian di halaman profil menggunakan Custom Model Manager dan QuerySet. Fitur ini memungkinkan pengunjung mencari kata kunci tertentu dan secara otomatis memfilter daftar restoran serta menu yang ditampilkan pada profil user tersebut
* edit views.py di profiles
```
from django.contrib.auth import get_user_model
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.views.generic import DetailView
from menus.models import Item
from restaurant.models import RestaurantLocation

User = get_user_model()

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

    def get_object(self):
        username = self.kwargs.get("username")
        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')
        qs = RestaurantLocation.objects.filter(owner=user)
        if query:
            qs = qs.filter(name__icontains=query)
        context['locations'] = qs
        return context
```
* edit file user.html
```
{% extends "base.html" %}

{% block isi %}
<div class="row">
    <div class="col-sm-6 col-sm-offset-3">
        <h1>Profile: {{ object.username }}</h1>
        
        <form method="GET" action="." class="form-inline" style="margin-bottom: 20px;">
            <input type="text" name="q" class="form-control" placeholder="Search..." value="{{ request.GET.q }}">
            <button type="submit" class="btn btn-default">Search</button>
        </form>
        <hr/>

        {% for rest in locations %}
            <div class="thumbnail" style="padding: 15px; margin-bottom: 20px;">
                <div class="caption">
                    <h4>{{ rest.name }} <small><a href="?q={{ rest.location }}">{{ rest.location }}</a></small></h4>
                    <p>Category: <a href="?q={{ rest.category }}">{{ rest.category }}</a></p>
                    
                    <p><b>Menu Items:</b></p>
                    <ul>
                        {% for item in rest.item_set.all %}
                            <li style="margin-bottom: 10px;">
                                <b>{{ item.name }}</b> <br/>
                                {% for ing in item.get_contents %}
                                    <a href="?q={{ ing }}" class="badge">#{{ ing }}</a>
                                {% endfor %}
                            </li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
        {% empty %}
            <p class="lead text-center">No items or restaurants found.</p>
        {% endfor %}
    </div>
</div>
{% endblock isi %}
```
* hasilnya akan muncul filter pencarian di profiles![image](https://hackmd.io/_uploads/rkJVOAVDWl.png)
---
# Section 5: Handling User dan Followers
##### Follow Users
part ini membahas pembuatan sistem Follower dan Following dengan cara membuat model Profile untuk setiap user
* edit file models.py
```
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, 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)

    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.filter(id=1).first()
        if default_user_profile:
            default_user_profile.followers.add(instance)

post_save.connect(post_save_user_receiver, sender=User)
```
* edit file admin.py
```
from django.contrib import admin

from .models import Profile

admin.site.register(Profile)
```
* hasilnya dapat dilihat di tampilan admin![image](https://hackmd.io/_uploads/rkPloCNP-g.png)
##### Follow Button Form
part ini berisikan cara membuat tombol follow dan menangani logika follow
* edit file models.py
```
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)

    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.filter(id=1).first()
        if default_user_profile:
            default_user_profile.followers.add(instance)

post_save.connect(post_save_user_receiver, sender=User)
```
* edit file views.py
```
from django.contrib.auth import get_user_model
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.views.generic import DetailView
from menus.models import Item
from restaurant.models import RestaurantLocation
from django.shortcuts import redirect
from django.views import View
from .models import Profile
from django.contrib.auth.mixins import LoginRequiredMixin


User = get_user_model()

class ProfileFollowToggle(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        username_to_toggle = request.POST.get("username")
        profile_, is_following = Profile.objects.toggle_follow(request.user, username_to_toggle)
        return redirect(f"/u/{profile_.user.username}/")
    
class ProfileDetailView(DetailView):
    template_name = 'profiles/user.html'

    def get_object(self):
        username = self.kwargs.get("username")
        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 self.request.user.is_authenticated:
            if user.profile in self.request.user.is_following.all():
                is_following = True
        context['is_following'] = is_following
        query = self.request.GET.get('q')
        qs = RestaurantLocation.objects.filter(owner=user)
        if query:
            qs = qs.filter(name__icontains=query)
        context['locations'] = qs
        return context
```
* edit file urls.py utama
```
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView, LogoutView
from profiles.views import ProfileFollowToggle

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html'), name='home'),
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('profile-follow/', ProfileFollowToggle.as_view(), name='follow'),
    path('about/', TemplateView.as_view(template_name='about.html'), name='about'),
    path('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'),
    path('items/', include('menus.urls', namespace='menus')),
    path('restaurant/', include('restaurant.urls', namespace='restaurant')),
    path('u/', include('profiles.urls', namespace='profiles')),
]
```
* membuat snippet form di profiles/snippets/follow_form.html
```
{% if request.user.is_authenticated %}
    {% if request.user != object %} <form method='POST' action='{% url "follow" %}'> {% csrf_token %}
            <input type='hidden' name='username' value='{{ username }}' />
            <button class='btn {% if is_following %}btn-default{% else %}btn-primary{% endif %}'>
                {% if is_following %}Unfollow{% else %}Follow{% endif %}
            </button>
        </form>
    {% endif %}
{% endif %}
```
* edit di user.html
```
{% extends "base.html" %}

{% block isi %}
<div class="row">
    <div class="col-sm-6 col-sm-offset-3">
        <h1>Profile: {{ object.username }}</h1>
        {% include 'snippets/follow_form.html' with username=object.username is_following=is_following %}
        <hr/>
        
        <form method="GET" action="." class="form-inline" style="margin-bottom: 20px;">
            <input type="text" name="q" class="form-control" placeholder="Search..." value="{{ request.GET.q }}">
            <button type="submit" class="btn btn-default">Search</button>
        </form>

        {% for rest in locations %}
            <div class="thumbnail" style="padding: 15px; margin-bottom: 20px;">
                <div class="caption">
                    <h4>{{ rest.name }} <small><a href="?q={{ rest.location }}">{{ rest.location }}</a></small></h4>
                    <p>Category: <a href="?q={{ rest.category }}">{{ rest.category }}</a></p>
                    
                    <p><b>Menu Items:</b></p>
                    <ul>
                        {% for item in rest.item_set.all %}
                            <li style="margin-bottom: 10px;">
                                <b>{{ item.name }}</b> <br/>
                                {% for ing in item.get_contents %}
                                    <a href="?q={{ ing }}" class="badge">#{{ ing }}</a>
                                {% endfor %}
                            </li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
        {% empty %}
            <p class="lead text-center">No items or restaurants found.</p>
        {% endfor %}
    </div>
</div>
{% endblock isi %}
```
* hasilnya akan muncul tombol follow![image](https://hackmd.io/_uploads/HkUHVySDWl.png)
##### Following Home Page Feed
part ini membuat Home Page yang isinya feed dari orang-orang yang  difollow
* edit file views.py pada menus
```
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 file template feed home di menus/templates/menus/home-feed.html
```
{% extends "base.html" %}

{% block isi %}
<div class="row">
    <div class="col-sm-6 col-sm-offset-3">
        <h1 class="text-center">User Following Feed</h1>
        <hr/>
        {% for object in object_list %}
            <div class="thumbnail" style="padding: 20px;">
                <p><a href="{% url 'profiles:detail' username=object.user.username %}"><b>@{{ object.user.username }}</b></a></p>
                
                <h3>{{ object.name }}</h3>
                
                <p><b>Restaurant:</b> {{ object.restaurant.title }} ({{ object.restaurant.location }})</p>
                
                <p><b>Contents:</b></p>
                <ul>
                    {% for ing in object.get_contents %}
                        <li>{{ ing }}</li>
                    {% endfor %}
                </ul>
                <p class="text-muted"><small>Updated: {{ object.updated|timesince }} ago</small></p>
            </div>
        {% empty %}
            <p class="text-center">You are not following anyone or no public items found.</p>
        {% endfor %}
    </div>
</div>
{% endblock isi %}
```
* update urls.html
```
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView, LogoutView
from profiles.views import ProfileFollowToggle
from menus.views import HomeView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', TemplateView.as_view(template_name='home.html'), name='home'),
    path('login/', LoginView.as_view(), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('profile-follow/', ProfileFollowToggle.as_view(), name='follow'),
    path('about/', TemplateView.as_view(template_name='about.html'), name='about'),
    path('contact/', TemplateView.as_view(template_name='contact.html'), name='contact'),
    path('items/', include('menus.urls', namespace='menus')),
    path('restaurant/', include('restaurant.urls', namespace='restaurant')),
    path('u/', include('profiles.urls', namespace='profiles')),
    path('', HomeView.as_view(), name='home'),
]
```

