--- 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(",") ``` ![image](https://hackmd.io/_uploads/HywZQlGkC.png) **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'), ] ``` ![image](https://hackmd.io/_uploads/SkOrQlz10.png) **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'), ] ``` ![image](https://hackmd.io/_uploads/B1chXgG10.png) **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 %} ``` ![image](https://hackmd.io/_uploads/r1ON4lMk0.png) **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 %} ``` ![image](https://hackmd.io/_uploads/SkgpVeGyA.png) **9. Kemudian buka di browser ubuntu** ![Screenshot (1426)](https://hackmd.io/_uploads/HktQSefJC.png) coba change item dari Django Admin ![Screenshot (1427)](https://hackmd.io/_uploads/SJIOHxfy0.png) kemudian liat pada items page ![Screenshot (1428)](https://hackmd.io/_uploads/HyhtBgf10.png) kemudian coba update items dengan web page tanpa Django Admin ![Screenshot (1430)](https://hackmd.io/_uploads/rJ2I8xMy0.png) # 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 ``` ![image](https://hackmd.io/_uploads/HkoGsMmkA.png) **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) ``` ![image](https://hackmd.io/_uploads/HkQLoM71C.png) **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'), ] ``` ![image](https://hackmd.io/_uploads/S1jniGQJC.png) **4. Akses 127.0.0.1:8000/items/1/edit di browser ubuntu** ![Screenshot (1431)](https://hackmd.io/_uploads/Bk9ZnM7k0.png) # 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 ``` ![image](https://hackmd.io/_uploads/r1OTW47kR.png) **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'), ] ``` ![image](https://hackmd.io/_uploads/B1L7GVmJ0.png) **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> ``` ![image](https://hackmd.io/_uploads/S1-2zN7k0.png) **4. Buat direktori restautants didalam direktori mywebsite/templates/ dan buat file form.html didalam direktori restaurants** ![image](https://hackmd.io/_uploads/S1O64EX1R.png) **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 %} ``` ![image](https://hackmd.io/_uploads/B1VgHEm1R.png) 6. Akses ke 127.0.01:8000/restaurants pada browser ubuntu ![Screenshot (1433)](https://hackmd.io/_uploads/rkK_HV71C.png) klik Add disamping restaurants ![Screenshot (1432)](https://hackmd.io/_uploads/r1SfBNQJ0.png) # 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** ![image](https://hackmd.io/_uploads/rySFQSXyR.png) **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) ``` ![image](https://hackmd.io/_uploads/HJrDTB710.png) **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'), ] ``` ![image](https://hackmd.io/_uploads/HkJ9aBQ10.png) **4. Edit file didalam direktori mywebsite/settings** ![image](https://hackmd.io/_uploads/rJL0pHmJC.png) **5. Buat direktori baru didalam mywebsite/templates dan buat file baru didalam direktori yang baru dibuat dengan nama user.html** ![image](https://hackmd.io/_uploads/BySdRSQy0.png) 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 %} ``` ![image](https://hackmd.io/_uploads/HJu9RS7y0.png) **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) ``` ![image](https://hackmd.io/_uploads/SkpgJUXJA.png) **7. Cek di browser ubuntu dengan 127.0.0.1:8000/u/(user)** ![Screenshot (1434)](https://hackmd.io/_uploads/By6SJIXkC.png) # 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 %} ``` ![image](https://hackmd.io/_uploads/Sk9GMI7yA.png) **2. Cek di browser ubuntu dengan 127.0.0.1:8000/u/(user)** ![Screenshot (1435)](https://hackmd.io/_uploads/H1fWfIX1R.png) # 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 ``` ![image](https://hackmd.io/_uploads/BJnMsL7J0.png) **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 %} ``` ![image](https://hackmd.io/_uploads/By5LsI7y0.png) **3. Akses ke 127.0.0.1:8000/u/(user) pada browser ubuntu** ![image](https://hackmd.io/_uploads/BJ3C9UQ10.png) Lakukan pencarian (contoh pada gambar melakukan pencarian warung) ![image](https://hackmd.io/_uploads/H1fT5IQ10.png) # 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) ``` ![image](https://hackmd.io/_uploads/r1FofDQJR.png) **2. Edit file admin.py didalam direktori profiles** ``` from django.contrib import admin from .models import Profile admin.site.register(Profile) ``` ![image](https://hackmd.io/_uploads/r1AAMP7yC.png) **3. Lakukan migrasi & jalankan server** python manage.py makemigrations python manage,py migrate python manage.py runserver ![image](https://hackmd.io/_uploads/H1TgXvm1C.png) **4. Cek pada browser ubuntu dengan membuka Django admin & cek Profiles sudah ada** ![image](https://hackmd.io/_uploads/BkJPQw71C.png) **5. Cek pada bagian profiles dan pilih salah satu user & lihat bahwa Followers dan Following ada** ![image](https://hackmd.io/_uploads/B1DK4wQ10.png) # 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 ``` ![image](https://hackmd.io/_uploads/B19ZfZN1C.png) **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) ``` ![image](https://hackmd.io/_uploads/HJ-b7-NJC.png) **3. Edit file urls.py didalam direktori mywebsite** ![image](https://hackmd.io/_uploads/B1QQXZEk0.png) **4. Edit file follow_form.html didalam direktori src/restaurants/templates/profiles/snippet (jika belum ada silahkan dibuat direktorinya dan filenya)** ![image](https://hackmd.io/_uploads/B1BPX-Ny0.png) **5. Untuk melihat access authenticated, buka pada mode penyamaran browser 127.0.0.1:8000/u/(nama user)** ![image](https://hackmd.io/_uploads/rJ36XbV10.png) "Kita bisa melihat bahwa tidak ada tombol follow" **6. Coba buka tanpa mode penyamaran (sudah login Django Admin)** ![image](https://hackmd.io/_uploads/rJJZEZN1A.png) # 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 ``` ![image](https://hackmd.io/_uploads/r1hwn-VkC.png) **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 ] ``` ![image](https://hackmd.io/_uploads/rJZpnZEJR.png) 3. Edit file home-feed.html didalam direktori restaurants/templates/menus ![image](https://hackmd.io/_uploads/SyA16bVJ0.png) ``` {% 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 %} ``` ![image](https://hackmd.io/_uploads/ryQZTbNJC.png) **4. Buka pada browser ubuntu 172.0.0.1:8000** ![image](https://hackmd.io/_uploads/SyOXTbVyA.png) # Register View **1. Buat file forms.py dalam direkotri profiles** ![image](https://hackmd.io/_uploads/BkgeCZVkR.png) ``` 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 ``` ![image](https://hackmd.io/_uploads/rkATWWw1C.png) **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 ``` ![image](https://hackmd.io/_uploads/rkpeGZvJA.png) **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'), ] ``` ![image](https://hackmd.io/_uploads/S1yNMZvJ0.png) **4. Buat direktori registration didalam direktori src/restaurants/template & Buat file login.html dan register.html ![image](https://hackmd.io/_uploads/S1YKGZDkC.png) ![image](https://hackmd.io/_uploads/S1f5zbPJA.png) **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 %} ``` ![image](https://hackmd.io/_uploads/S1XbmbD1A.png) **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 %} ``` ![image](https://hackmd.io/_uploads/S1Dm7-v10.png) **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) ``` ![image](https://hackmd.io/_uploads/HJrcwZPJ0.png) 8. Edit file base.py didalam direktori src/mywebsite/settings Sesuaikan seperti gambar dibawah ini ![image](https://hackmd.io/_uploads/BJx0D-DkC.png) **9. Stop runserver dan runserver kembali** **10. Akses server dengan 127.0.0.1:8000/register/ dan coba register user baru** ![image](https://hackmd.io/_uploads/r17WubvJ0.png) **11. Cek pada Django admin dan cek apakah user baru sudah tertambahkan** ![image](https://hackmd.io/_uploads/HJydOWDk0.png) # 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 ``` ![image](https://hackmd.io/_uploads/BJcgcfv1C.png) **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) ``` ![image](https://hackmd.io/_uploads/SkYS9fv10.png) **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)) ``` ![image](https://hackmd.io/_uploads/SJnnqMPJ0.png) **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 ``` ![image](https://hackmd.io/_uploads/S1FmjfwyR.png) 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'), ] ``` ![image](https://hackmd.io/_uploads/H1YlR7vyA.png) **6. Migrasi Server** python manage.py makemigrations python manage.py migrate python manage.py runserver ![image](https://hackmd.io/_uploads/ByDknMvJ0.png) **7. Akses server dengan 127.0.0.1:8000/register/ dan coba register user baru** ![image](https://hackmd.io/_uploads/SyGLAQD1R.png) ketika sudah register ![image](https://hackmd.io/_uploads/SkDLRQw1C.png) acitvasi sudah langsung otomatis keluar pada terminal ![image](https://hackmd.io/_uploads/ryxvAXvyR.png)