---
title: HackMD Dark Theme
tags: theme
description: Use `{%hackmd theme-dark %}` syntax to include this theme.
---
<style>
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
color: #ddd;
}
.markdown-body h1,
.markdown-body h2 {
border-bottom-color: #ffffff69;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #fff;
}
.markdown-body img {
background-color: transparent;
}
.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
color: white;
border-left: 2px solid white;
}
.expand-toggle:hover,
.expand-toggle:focus,
.back-to-top:hover,
.back-to-top:focus,
.go-to-bottom:hover,
.go-to-bottom:focus {
color: white;
}
.ui-toc-dropdown {
background-color: #333;
}
.ui-toc-label.btn {
background-color: #191919;
color: white;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
color: white;
border-left: 1px solid white;
}
.markdown-body blockquote {
color: #bcbcbc;
}
.markdown-body table tr {
background-color: #5f5f5f;
}
.markdown-body table tr:nth-child(2n) {
background-color: #4f4f4f;
}
.markdown-body code,
.markdown-body tt {
color: #eee;
background-color: rgba(230, 230, 230, 0.36);
}
a,
.open-files-container li.selected a {
color: #5EB7E0;
}
</style>
**3. Edit file models.py didalam direktori menus**
```
from django.conf import settings
from django.db import models
from django.core.urlresolvers import reverse
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)
def get_absolute_url(self):
return reverse('menus:detail', kwargs={'pk': self.pk})
class Meta:
ordering = ['-updated', '-timestamp']
def get_contents(self):
return self.contents.split(",")
def get_excludes(self):
return self.excludes.split(",")
```

**4. Edit file urls.py didalam direktori menus**
```
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+)/$', ItemDetailView.as_view(), name='detail'),
url(r'$', ItemListView.as_view(), name='List'),
]
```

**5. Edit file urls.py didalam direktori src/mywebsite**
```
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView
from restaurants.views import (
restaurant_createview,
restaurants_listview,
RestaurantListView,
RestaurantDetailView,
RestaurantCreateView
)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'),
url(r'^login/$', LoginView.as_view(), name='login'),
url(r'^items/', include('menus.urls', namespace='menus')),
url(r'^restaurants/', include('restaurants.urls', namespace='restaurants')),
#url(r'^restaurants/$', RestaurantListView.as_view(), name='restaurants'),
#url(r'^restaurants/create/$', RestaurantCreateView.as_view(), name='restaurants-creat'),
#url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view(), name='restaur'),
url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```

**6. Buat direktori menus pada src/restaurants/templates dan buat juga untuk file item_list.html kemudian edit filenya**
```
{% extends "base.html" %}
{% block head_title %}Menu Items | {{ block.super }}{% endblock head_title %}
{% block content %}
<h1>Menu Items</h1>
<ul>
{% for obj in object_list %}
<li><a href='{{ obj.get_absolute_url }}'>{{ obj.name }}</a><br/>
<a href='/restaurants/{{ obj.slug }}/'>{{ obj }}</a><br/>
{% if obj.contents %}
<p>Contents:</p>
<ul>
{% for item in obj.get_contents %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endif %}
{% if obj.excludes %}
<p>Excludes:</p>
<ul>
{% for item in obj.get_excludes %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}
```

**8. Kemudian buat juga file item_detail.html dan edit (masih didalam direktori yang sama)**
```
{% extends "base.html" %}
{% block head_title %}Menu Items | {{ block.super }}{% endblock head_title %}
{% block content %}
<h1>{{ object.name }}</h1>
<p>Location: {{ object.restaurant.name }}</p>
{% if object.contents %}
<p>Contents:</p>
<ul>
{% for item in object.get_contents %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endif %}
{% if object.excludes %}
<p>Excludes:</p>
<ul>
{% for item in object.get_excludes %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}
```

**9. Kemudian buka di browser ubuntu**

coba change item dari Django Admin

kemudian liat pada items page

kemudian coba update items dengan web page tanpa Django Admin

# Limiting Form Field to QuerySet
Kita membatasi opsi yang tersedia dalam form dengan menggunakan queryset. Ini memungkinkan kita untuk menampilkan hanya entitas yang sesuai dengan kriteria tertentu dalam form, seperti item yang dimiliki oleh pengguna yang saat ini masuk. Dengan cara ini, pengguna hanya dapat memilih opsi yang relevan untuk mereka. Ini meningkatkan pengalaman pengguna dan membantu memastikan data yang konsisten dan relevan.
**1. Edit file views.py didalam direktori menus**
```
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
```

**2. Edit file forms.py didalam direktori menus**
```
from django import forms
from restaurants.models import RestaurantLocation
from .models import Item
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(user=user).exclude(item__isnull=False)
```

**3. Edit file urls.py didalam direktori menus**
```
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'),
]
```

**4. Akses 127.0.0.1:8000/items/1/edit di browser ubuntu**

# Personalize Items
Kita akan membuat fitur di aplikasi yang memungkinkan pengguna untuk melihat dan mengelola item-menu dan restoran mereka sendiri. Hal ini akan memastikan bahwa setiap pengguna hanya melihat informasi yang terkait dengan akun mereka.
**1. Edit file views.py didalam direktori restaurants**
```
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from .forms import RestaurantLocationCreateForm
from .models import RestaurantLocation
class RestaurantListView(LoginRequiredMixin, ListView):
def get_queryset(self):
return RestaurantLocation.objects.filter(user=self.request.user)
class RestaurantDetailView(LoginRequiredMixin, DetailView):
def get_queryset(self):
return RestaurantLocation.objects.filter(user=self.request.user)
class RestaurantCreateView(LoginRequiredMixin, CreateView):
form_class = RestaurantLocationCreateForm
login_url = '/login/'
template_name = 'restaurants/form.html'
success_url = "/restaurants"
def form_valid(self, form):
instance = form.save(commit=False)
instance.owner = self.request.user
return super(RestaurantCreateView, self).form_valid(form)
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):
model = RestaurantLocation # Menetapkan model untuk UpdateView
form_class = RestaurantLocationCreateForm
login_url = '/login/'
template_name = 'restaurants/form.html'
# success_url = "/restaurants"
def get_queryset(self): # Mendefinisikan queryset untuk UpdateView
return RestaurantLocation.objects.filter(user=self.request.user)
def get_context_data(self, *args, **kwargs):
context = super(RestaurantUpdateView, self).get_context_data(*args, **kwargs)
context['title'] = 'Update Restaurant'
return context
```

**2. Edit file urls.py didalam direktori restaurants**
```
from django.conf.urls import url
from .views import (
RestaurantListView,
RestaurantDetailView,
RestaurantCreateView,
RestaurantUpdateView
)
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'^create/$', RestaurantCreateView.as_view(), name='create'), # Perhatikan penghapusan tanda kutip ganda ekstra dan tanda slash di depan
url(r'^(?P<slug>[\w-]+)/$', RestaurantUpdateView.as_view(), name='edit'),
url(r'^(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view(), name='detail'), # Perhatikan penghapusan tanda kutip ganda ekstra dan tanda slash di depan
url(r'$', RestaurantListView.as_view(), name='list'),
#url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'),
#url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'),
]
```

**3. Edit file nav.html didalam direktori src/mywebsite/templates/snippets**
```
<div class="container">
<h1>Nicholai Dandy Nainggolan.org</h1>
<a href='{% url "home" %}'>Home</a>
<a href='{% url "about" %}'>About</a>
<a href='{% url "contact" %}'>Contact</a>
{% if request.user.is_authenticated %}
<a href='{% url "restaurants:list" %}'>Restaurants</a>
{% endif %}
</div>
```

**4. Buat direktori restautants didalam direktori mywebsite/templates/ dan buat file form.html didalam direktori restaurants**

**5. Edit file form.html**
```
{% 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 %}
```

6. Akses ke 127.0.01:8000/restaurants pada browser ubuntu
 klik Add disamping restaurants

# User Profile View
Anda membuat fitur baru di aplikasi Anda yang disebut "Profiles" untuk membuat profil publik pengguna. Fitur ini memungkinkan pengguna untuk melihat daftar restoran dan item makanan yang disukai oleh pengguna lain. Untuk melakukannya, Anda membuat kelas view ProfileDetailView yang mengambil detail view dari Django. Setelah itu, Anda menyiapkan tampilan profil dengan menampilkan daftar restoran dan item makanan yang disukai oleh pengguna, dengan sedikit penyesuaian tata letak dan gaya. Dengan ini, pengguna dapat berbagi preferensi makanan mereka dengan orang lain.
**1. Buat project baru bernama profiles**

**2. Edit file views.py dalam direktori profiles**
```
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)
```

**3. Edit file urls.py didalam direktori profiles**
```
from django.conf.urls import url
from .views import ProfileDetailView
urlpatterns = [
url(r'^(?P<username>[\w-]+)/$', ProfileDetailView.as_view(), name='detail'),
]
```

**4. Edit file didalam direktori mywebsite/settings**

**5. Buat direktori baru didalam mywebsite/templates dan buat file baru didalam direktori yang baru dibuat dengan nama user.html**

Edit file user.html
```
{% 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 }}</span>
{% endfor %}
</li>
{% endfor %}
</ul>
</p>
{% endfor %}
{% else %}
<p class='lead'>No Items Found</p>
{% endif %}
{% endblock %}
```

**6. Edit file models.py dalam direktori restaurants**
```
from django.db import models
# Create your models here.
from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save, post_save
from django.core.urlresolvers import reverse
from .utils import unique_slug_generator
from .validators import validate_category
# Create your models here.
User = settings.AUTH_USER_MODEL
class RestaurantLocation(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=120)
location = models.CharField(max_length=120, null=True, blank=True)
category = models.CharField(max_length=120, null=True, blank=True, validators=[validate_category])
timestamp = models.DateTimeField(auto_now=True)
update = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True, null=True, blank=True)
def __str__(self):
return self.name
def get_absolute_url(self):
# return f"/restaurants/{self.slug}"
return reverse('restaurants:detail', kwargs={'slug': self.slug})
@property
def title(self):
return self.name
def rl_pre_save_receiver(sender, instance, *args, **kwargs):
instance.category = instance.category.capitalize()
if not instance.slug:
instance.slug = unique_slug_generator(instance)
#def rl_post_save_receiver(sender, instance, created, *args, **kwargs):
# print('saved')
# print(instance.timestamp)
# instance.save()
pre_save.connect(rl_pre_save_receiver, sender=RestaurantLocation)
#post_save.connect(rl_post_save_receiver, sender=RestaurantLocation)
```

**7. Cek di browser ubuntu dengan 127.0.0.1:8000/u/(user)**

# Style Profile with Bootstrap
**1. Edit file user.html didalam direktori mywebsite/templates/profiles**
```
{% 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 %}
```

**2. Cek di browser ubuntu dengan 127.0.0.1:8000/u/(user)**

# Adding a Robust Search
"Adding a robust search" adalah proses menambahkan fitur pencarian yang kuat atau canggih ke dalam sebuah aplikasi web. Ini melibatkan pengembangan sistem pencarian yang dapat melakukan pencarian yang kompleks dan akurat, termasuk kemampuan untuk mencari berdasarkan kriteria yang beragam dan filter yang diterapkan. Tujuannya adalah untuk memberikan pengguna pengalaman pencarian yang lebih baik dan efisien dalam menemukan informasi yang mereka perlukan. Dalam konteks pengembangan web dengan Django
**1. Edit file views.py didalam direktori profiles**
```
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
```

**2. Edit file user.html pada direktori mywebsite/templates/profiles**
```
{% 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 %}
```

**3. Akses ke 127.0.0.1:8000/u/(user) pada browser ubuntu**

Lakukan pencarian (contoh pada gambar melakukan pencarian warung)

# Handling User & Followers
**1. Follow Users**
Bagaimana cara membuat fitur yang memungkinkan pengguna untuk mengikuti pengguna lain di situs web. Fitur ini memungkinkan pengguna untuk melihat profil pengguna lain, mengikuti mereka, dan melihat pembaruan terkait aktivitas pengguna yang diikuti.
**1. Edit file models.py didalam direktori profiles**
```
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) # user.profile
followers = models.ManyToManyField(User, related_name='followers', blank=True)
following = models.ManyToManyField(User, related_name='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)
post_save.connect(post_save_user_receiver, sender=User)
```

**2. Edit file admin.py didalam direktori profiles**
```
from django.contrib import admin
from .models import Profile
admin.site.register(Profile)
```

**3. Lakukan migrasi & jalankan server**
python manage.py makemigrations
python manage,py migrate
python manage.py runserver

**4. Cek pada browser ubuntu dengan membuka Django admin & cek Profiles sudah ada**

**5. Cek pada bagian profiles dan pilih salah satu user & lihat bahwa Followers dan Following ada**

# Follow Button Form
**1. Edit file views.py didalam direktori profiles**
```
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):
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'
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 self.request.user.is_authenticated: # Periksa apakah pengguna sedang login
if user.profile in self.request.user.profile.followers.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(user=user)
if items_exist and qs.exists():
context['locations'] = qs
return context
```

**2. Edit file models.py didalam direkori profiles**
```
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.profile in profile.followers.all():
profile.followers.remove(user.profile)
is_following = False
else:
profile.followers.add(user.profile)
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)
```

**3. Edit file urls.py didalam direktori mywebsite**

**4. Edit file follow_form.html didalam direktori src/restaurants/templates/profiles/snippet (jika belum ada silahkan dibuat direktorinya dan filenya)**

**5. Untuk melihat access authenticated, buka pada mode penyamaran browser 127.0.0.1:8000/u/(nama user)**

"Kita bisa melihat bahwa tidak ada tombol follow"
**6. Coba buka tanpa mode penyamaran (sudah login Django Admin)**

# Following Home Page Feed
**1. Edit file views.py didalam direktori 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:
return render(request, "home.html", {})
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")[:]
return render(request, "menus/home-feed.html", {'object_list': qs})
class ItemListView(LoginRequiredMixin, ListView):
def get_queryset(self):
return Item.objects.filter(user=self.request.user)
class ItemDetailView(LoginRequiredMixin, DetailView):
def get_queryset(self):
return Item.objects.filter(user=self.request.user)
class ItemCreateView(LoginRequiredMixin, 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
```

**2. Edit file urls.py didalam direkotori mywebiste**
```
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'), # Fixed the URL pattern
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')), # Fixed the syntax error
url(r'^about/$', TemplateView.as_view(template_name='about.html'), name='about'), # Fixed the URL pattern
url(r'^contact/$', TemplateView.as_view(template_name='contact.html'), name='contact'), # Fixed the URL pattern
]
```

3. Edit file home-feed.html didalam direktori restaurants/templates/menus

```
{% 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 %}
```

**4. Buka pada browser ubuntu 172.0.0.1:8000**

# Register View
**1. Buat file forms.py dalam direkotri profiles**

```
from django import forms
from django.contrib.auth import get_user_model
User = get_user_model()
class RegisterForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'email',)
def clean_email(self):
email = self.cleaned_data.get("email")
qs = User.objects.filter(email__iexact=email)
if qs.exists():
raise forms.ValidationError("Cannot use this email. It's already registered")
return email
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
#user.password = "asdfasd"
user.is_active = False
if commit:
user.save()
user.profile.send_activation_email()
# create a new user hash for activating email.
return user
```

**2. Edit file views.py didalam direktori profiles**
```
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 CreateView, DetailView, View
# Create your views here.
from menus.models import Item
from restaurants.models import RestaurantLocation
from .forms import RegisterForm
from .models import Profile
User = get_user_model()
class RegisterView(CreateView):
form_class = RegisterForm
template_name = 'registration/register.html'
success_url = '/'
def dispatch(self, *args, **kwargs):
# if self.request.user.is_authenticated():
# return redirect("/logout")
return super(RegisterView, self).dispatch(*args, **kwargs)
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")
if username is None:
raise Http404
return get_object_or_404(User, username__iexact=username, is_active=True)
def get_context_data(self, *args, **kwargs):
context = super(ProfileDetailView, self).get_context_data(*args, **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_exists = Item.objects.filter(user=user).exists()
qs = RestaurantLocation.objects.filter(user=user).search(query)
if items_exists and qs.exists():
context['locations'] = qs
return context
```

**3. Edit file urls.py didalam direktori mywebsite**
```
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView, LogoutView
from menus.views import HomeView
from profiles.views import ProfileFollowToggle, RegisterView
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', HomeView.as_view(), name='home'),
url(r'^register/$', RegisterView.as_view(), name='register'),
url(r'^login/$', LoginView.as_view(), name='login'),
url(r'^logout/$', LogoutView.as_view(), name='logout'),
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'),
]
```

**4. Buat direktori registration didalam direktori src/restaurants/template & Buat file login.html dan register.html


**5. Edit file login.html **
```
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>Your account doesn't have access to this page. To proceed,
please login with an account that has access.</p>
{% else %}
<p>Please login to see this page.</p>
{% endif %}
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{# Assumes you setup the password_reset view in your URLconf #}
{% endblock %}
```

**6. Edit file register.html**
```
{% extends "base.html" %}
{% block content %}
<div class='col-sm-6 col-sm-offset-3 text-center'>
<h1>Register</h1>
<form method="post" action="{% url 'register' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Register" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
<p>Did you need to <a href='{% url "login" %}'>Login</a>?
</div>
{% endblock %}
```

**7. Edit file models.py didalam direktori profiles**
```
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) # 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)
objects = ProfileManager()
def __str__(self):
return self.user.username
def send_activation_email(self):
print("Activation")
pass
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)
```

8. Edit file base.py didalam direktori src/mywebsite/settings
Sesuaikan seperti gambar dibawah ini

**9. Stop runserver dan runserver kembali**
**10. Akses server dengan 127.0.0.1:8000/register/ dan coba register user baru**

**11. Cek pada Django admin dan cek apakah user baru sudah tertambahkan**

# Activation Keys
**1. Edit file views.py di direktori profiles**
```
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 CreateView, DetailView, View
# Create your views here.
from menus.models import Item
from restaurants.models import RestaurantLocation
from .forms import RegisterForm
from .models import Profile
User = get_user_model()
def activate_user_view(request, code=None, *args, **kwargs):
if code:
qs = Profile.objects.filter(activation_key=code)
if qs.exists() and qs.count() == 1:
profile = qs.first()
if not profile.activated:
user_ = profile.user
user_.is_active = True
user_.save()
profile.activated=True
profile.activation_key=None
profile.save()
return redirect("/login")
return redirect("/login")
class RegisterView(CreateView):
form_class = RegisterForm
template_name = 'registration/register.html'
success_url = '/'
def dispatch(self, *args, **kwargs):
# if self.request.user.is_authenticated():
# return redirect("/logout")
return super(RegisterView, self).dispatch(*args, **kwargs)
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")
if username is None:
raise Http404
return get_object_or_404(User, username__iexact=username, is_active=True)
def get_context_data(self, *args, **kwargs):
context = super(ProfileDetailView, self).get_context_data(*args, **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_exists = Item.objects.filter(user=user).exists()
qs = RestaurantLocation.objects.filter(owner=user).search(query)
if items_exists and qs.exists():
context['locations'] = qs
return context
```

**2. Edit file models.py di direktori profiles**
```
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from .utils import code_generator
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) # 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()
activation_key = models.CharField(max_length=120, blank=True, null=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 send_activation_email(self):
if not self.activated:
self.activation_key = code_generator()# 'somekey' #gen key
self.save()
#path_ = reverse()
path_ = reverse('activate', kwargs={"code": self.activation_key})
subject = 'Activate Account'
from_email = settings.DEFAULT_FROM_EMAIL
message = f'Activate your account here: {path_}'
recipient_list = [self.user.email]
html_message = f'<p>Activate your account here: {path_}</p>'
print(html_message)
# sent_mail = send_mail(
# subject,
# message,
# from_email,
# recipient_list,
# fail_silently=False,
# html_message=html_message)
sent_mail = False
return sent_mail
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)
```

**3. Edit file utils.py didalam direktori profiles**
sudo nano utils.py
```
import random
import string
from django.conf import settings
SHORTCODE_MIN = getattr(settings, "SHORTCODE_MIN", 35)
#from shortener.models import KirrURL
def code_generator(size=SHORTCODE_MIN, chars=string.ascii_lowercase + string.digits):
# new_code = ''
# for _ in range(size):
# new_code += random.choice(chars)
# return new_code
return ''.join(random.choice(chars) for _ in range(size))
```

**4. Edit file base.py didalam direktori src/mywebsite/settings**
```
ALLOWED_HOSTS = []
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'youremail@gmail.com'
EMAIL_HOST_PASSWORD = 'yourpassword'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'Your Name <you@email.com>'
ADMINS = (
('You', 'you@email.com'),
)
MANAGERS = ADMINS
```

5. Edit file urls.py didalam direktori src/mywebsite
```
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic import TemplateView
from django.contrib.auth.views import LoginView, LogoutView
from menus.views import HomeView
from profiles.views import ProfileFollowToggle, RegisterView, activate_user_view
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', HomeView.as_view(), name='home'),
url(r'^register/$', RegisterView.as_view(), name='register'),
url(r'^activate/(?P<code>[a-z0-9].*)/$', activate_user_view, name='activate'),
url(r'^login/$', LoginView.as_view(), name='login'),
url(r'^logout/$', LogoutView.as_view(), name='logout'),
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'),
]
```

**6. Migrasi Server**
python manage.py makemigrations
python manage.py migrate
python manage.py runserver

**7. Akses server dengan 127.0.0.1:8000/register/ dan coba register user baru**

ketika sudah register

acitvasi sudah langsung otomatis keluar pada terminal
