๐ฅ Update Room Page
๐ฅ Delete Photo
๐ฅ Update Caption of Photo
๐ฅ Create Photo
from django.views.generic import ListView, DetailView, View, UpdateView # ๐ import from django.shortcuts import render from django.core.paginator import Paginator from . import models, forms ... ... class EditRoomView(UpdateView): # ๐ UpdateView ์์ model = models.Room # ๐ Model ์ง์ template_name = "rooms/room_edit.html" # ๐ renderํ ํ ํ๋ฆฟ ์ง์ fields = ( # ๐ ์ฌ์ฉํ field ์ง์ "name", "description", "country", "city", "price", "address", "guests", "beds", "bedrooms", "baths", "check_in", "check_out", "instant_book", "room_type", "amenities", "facilities", "house_rule", )
from django.urls import path from . import views app_name = "rooms" urlpatterns = [ path("<int:pk>/", views.RoomDetail.as_view(), name="detail"), path("<int:pk>/edit/", views.EditRoomView.as_view(), name="edit"), # ๐ Edit ๊ฒฝ๋ก path("search/", views.SearchView.as_view(), name="search"), ]
... ... <div class="w-1/3"> {% if room.host == user %} # ๐ ๊ฐ์ค์ host์ user๊ฐ ์ผ์นํ ๊ฒฝ์ฐ์๋ง ๋ฒํผ ์์ฑ <a href="{% url 'rooms:edit' room.pk %}" class="btn-link block">Edit Room</a> {% endif %} </div>
# rooms/room_edit.html {% extends "base.html" %} {% block page_title %} Update Room {% endblock page_title %} {% block search-bar %} {% endblock search-bar %} {% block content %} <div class="container lg:w-5/12 md:w-1/2 xl:w-1/4 mx-auto my-10 flex flex-col items-center border p-6 border-gray-400"> {% include 'mixins/room/room_form.html' with form=form cta="Update room" %} # ๐ form์ 'mixins/room/room_form.html'๋ก ์ ๋ฌ </div> {% endblock content %}
# mixins/room/room_form.html <form method="POST" class="w-full" enctype="multipart/form-data"> {% csrf_token %} {% if form.non_field_errors %} {% for error in form.non_field_errors %} <span class="text-red-700 font-medium text-sm">{{error}}</span> {% endfor %} {% endif %} {% for field in form %} # ๐ form์ ๊ฐ field๋ฅผ 'mixins/room/room_input.html'๋ก ์ ๋ฌ {% include 'mixins/room/room_input.html' with field=field %} {% endfor %} <button class="btn bg-red-500 text-white">{{cta}}</button> </form>
# room_input.html <div class="input w-full {% if field.errors %}has_error{% endif %}"> {{field.label}} # ๐ ๊ฐน field์ label {{field}} # ๐ ํ๋ input form {% if field.errors %} {% for error in field.errors %} <span class="text-red-700 font-medium text-sm">{{error}}</span> {% endfor %} {% endif %} </div>
# users/mixin.py from django.shortcuts import redirect from django.urls import reverse_lazy from django.contrib import messages from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin class EmailLoginOnlyView(UserPassesTestMixin): def test_func(self): return self.request.user.login_method == "email" def handle_no_permission(self): messages.error(self.request, "Can't go there") return redirect("core:home") class LoggedOutOnlyView(UserPassesTestMixin): # ๐ Loginํ ์ฌ์ฉ์๋ index ํ์ด์ง๋ก ์ด๋(Logoutํ ์ฌ์ฉ์๋ง ์ ๊ทผ ๊ฐ๋ฅ) def test_func(self): return not self.request.user.is_authenticated def handle_no_permission(self): messages.error(self.request, "Can't go there") return redirect("core:home") class LoggedInOnlyView(LoginRequiredMixin): # ๐ Loginํ ์ฌ์ฉ์๊ฐ ์๋๋ผ๋ฉด, login ํ์ด์ง๋ก ์ด๋ login_url = reverse_lazy("users:login")
from django.http import Http404 # ๐ import "Http404" from django.views.generic import ListView, DetailView, View, UpdateView from django.shortcuts import render from django.core.paginator import Paginator from users import mixin as user_mixins # ๐ import "mixin" from . import models, forms ... ... class EditRoomView(user_mixins.LoggedInOnlyView, UpdateView): # ๐ "LoggedInOnlyView" model = models.Room template_name = "rooms/room_edit.html" fields = ( "name", "description", "country", "city", "price", "address", "guests", "beds", "bedrooms", "baths", "check_in", "check_out", "instant_book", "room_type", "amenities", "facilities", "house_rule", ) def get_object(self, queryset=None): room = super().get_object(queryset=queryset) # print(room.host.pk, self.request.user.pk) if room.host.pk != self.request.user.pk: # ๐ ์ฌ์ฉ์์ host๊ฐ ์ผ์นํ์ง์์ผ๋ฉด,, raise Http404() return room # ๐ ์ผ์นํ๋ฉด ํด๋น room Object๋ฅผ ๋ฐํ
from django.http import Http404 from django.views.generic import ListView, DetailView, View, UpdateView from django.shortcuts import render from django.core.paginator import Paginator from users import mixin as user_mixins from . import models, forms ... ... class RoomPhotosView(user_mixins.LoggedInOnlyView, DetailView): model = models.Room template_name = "rooms/room_photos.html" def get_object(self, queryset=None): # ๐ ํ๋ฉด์ ํ์๋๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์! room = super().get_object(queryset=queryset) if room.host.pk != self.request.user.pk: raise Http404() return room # ๐ ๊ฐ์ค์ host์ ์ผ์นํ ๊ฒฝ์ฐ์๋ง Room ๊ฐ์ฒด๋ฅผ ํ ํ๋ฆฌ ๋ณ์๋ก ๋ฐํํด์ค๋๋ค.
from django.urls import path from . import views app_name = "rooms" urlpatterns = [ path("<int:pk>/", views.RoomDetail.as_view(), name="detail"), path("<int:pk>/edit/", views.EditRoomView.as_view(), name="edit"), path("<int:pk>/photos/", views.RoomPhotosView.as_view(), name="photos"), # ๐ ํด๋น ๊ฐ์ค์ ๋ํ ์ฌ์ง ์ ๋ณด๋ฅผ DetailView๋ก ๋ณด์ฌ์ฃผ๋ URL์ด์์. path("search/", views.SearchView.as_view(), name="search"), ]
{% extends "base.html" %} {% block page_title %} Update Room {% endblock page_title %} {% block search-bar %} {% endblock search-bar %} {% block content %} <div class="container lg:w-5/12 md:w-1/2 xl:w-1/4 mx-auto my-10 flex flex-col items-center border p-6 border-gray-400"> {% include 'mixins/room/room_form.html' with form=form cta="Update room" %} <div class="mt-5"> # ๐ ์ด Btn์ ํตํด photo๋ฅผ ์์ ํ ์ ์๋ ํ์ด์ง๋ก ์ ๊ทผํฉ๋๋ค:) <a href="{% url 'rooms:photos' room.pk %}" class="text-teal-500 font-medium">Edit Photos</a> </div> </div> {% endblock content %}
{% extends "base.html" %} {% block page_title %} {{room.name}}'s Photos {% endblock page_title %} {% block search-bar %} {% endblock search-bar %} {% block content %} <div class="container mx-auto my-10 flex flex-col p-6"> {% for photo in room.photos.all %} # ๐ related_name ์ผ๋ก ์ ๊ทผ <div class="mb-5 border p-6 border-gray-400 flex justify-between"> <div class="flex items-start"> <img src="{{photo.file.url}}" class="w-32 h-32"> # ๐ image <span class="ml-5 text-xl">{{photo.caption}}</span> # ๐ caption </div> <div class="flex flex-col w-1/5"> <a class="btn-link mb-5 bg-teal-500" href="#">Edit</a> # ๐ Edit ๋ฒํผ <a class="btn-link bg-red-600" href="#">Delete</a> # ๐ Delete ๋ฒํผ </div> </div> {% endfor %} </div> {% endblock content %}
from django.urls import path from . import views app_name = "rooms" urlpatterns = [ path("<int:pk>/", views.RoomDetail.as_view(), name="detail"), path("<int:pk>/edit/", views.EditRoomView.as_view(), name="edit"), path("<int:pk>/photos/", views.RoomPhotosView.as_view(), name="photos"), path( "<int:room_pk>/photos/<int:photo_pk>/delete/", views.delete_photo, name="delete-photo", ), # ๐ delete-btn ๊ฒฝ๋ก path("search/", views.SearchView.as_view(), name="search"), ]
# room_photo.html <div class="flex flex-col w-1/5"> <a class="btn-link mb-5 bg-teal-500" href="#">Edit</a> <a class="btn-link bg-red-600" href="{% url 'rooms:delete-photo' room.pk photo.pk %}">Delete</a> # ๐ "room.pk" & "photo.pk" ์ ๋ฌ </div>
"@login_required"๋ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ๋์ด์์ผ๋ฉด ์ ์์ ์ผ๋ก ์๋ ํจ์์ ๊ธฐ๋ฅ์ ํธ์ถํ๊ณ , ๋ก๊ทธ์ธ๋์ด์์ง ์์ผ๋ฉด settings.LOGIN_URL์ ์ง์ ๋ ๊ฒฝ๋ก๋ก redirect๋ฅผ ์์ผ์ค๋๋ค. ์ด์ settgins.py์ ์๋ ๋ด์ฉ์ ์ถ๊ฐํ ๊ป์:)
์ญ์ ๋ํ ํ์ฌ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํด๋น ๊ฐ์ค์ host์ ์ผ์นํด์ผ์ง๋ง ์ฒ๋ฆฌํด์ค ์ ์๋๋ก ํฉ๋๋ค.
from django.http import Http404 from django.views.generic import ListView, DetailView, View, UpdateView from django.shortcuts import render, redirect, reverse from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required # ๐ "login_required" import from django.contrib import messages # ๐ "messages" import from users import mixin as user_mixins from . import models, forms ... ... class RoomPhotosView(user_mixins.LoggedInOnlyView, DetailView): model = models.Room template_name = "rooms/room_photos.html" def get_object(self, queryset=None): room = super().get_object(queryset=queryset) if room.host.pk != self.request.user.pk: raise Http404() return room @login_required def delete_photo(request, room_pk, photo_pk): # print("Should delete {photo_pk} from {room_pk}") # ๐ 2๊ฐ์ pk๊ฐ์ด ์ ์ถ๋ ฅ๋ฉ๋๋ค:) user = request.user # ๐ ํ์ฌ loginํ ์ฌ์ฉ์๋ฅผ ๊ฐ์ ธ์ต๋๋ค. try: room = models.Room.objects.get(pk=room_pk) if room.host.pk != user.pk: # ๐ ์ฌ์ฉ์์ pk๊ฐ๊ณผ room.host์ pk๊ฐ ์ผ์น ํ์ธ messages.error(request, "Cant delete that photo") else: models.Photo.objects.filter(pk=photo_pk).delete() # ๐ ์ญ์ messages.success(request, "Photo Delete") return redirect(reverse("rooms:photos", kwargs={"pk": room_pk})) except models.Room.DoesNotExist: # ๐ ํด๋น Room์ด ์กด์ฌํ์ง ์๋ค๋ฉด, return redirect(reverse("core:home"))
from django.http import Http404 from django.views.generic import ListView, DetailView, View, UpdateView from django.shortcuts import render, redirect, reverse from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin from users import mixin as user_mixins from . import models, forms ... ... class EditPhotoView(user_mixins.LoggedInOnlyView, SuccessMessageMixin, UpdateView): model = models.Photo pk_url_kwarg = "photo_pk" # ๐ pk๊ฐ๋ง ์ฐพ๊ณ ์๊ธฐ ๋๋ฌธ์ phpto_pk๊ฐ์ ์ฐพ๋๋ก ์๋ ค์ค์ผํด์:) template_name = "rooms/photo_edit.html" success_message = "Photo Updated" fields = ("caption",) # ๐ ํํ ๋๋ ๋ฆฌ์คํธ๋ก fields๋ฅผ ์ง์ ํด์ฃผ์ด์ผํด์! def get_success_url(self): room_pk = self.kwargs.get("room_pk") # ๐ ํ์ฌ url์์ room_pk๊ฐ์ ๊ฐ์ ธ์ต๋๋ค:) return reverse("rooms:photos", kwargs={"pk": room_pk}) # ๐ room_pk๊ฐ์ url์ argument๋ก ์ ๋ฌํฉ๋๋ค.
from django.urls import path from . import views app_name = "rooms" urlpatterns = [ path("<int:pk>/", views.RoomDetail.as_view(), name="detail"), path("<int:pk>/edit/", views.EditRoomView.as_view(), name="edit"), path("<int:pk>/photos/", views.RoomPhotosView.as_view(), name="photos"), path( "<int:room_pk>/photos/<int:photo_pk>/delete/", views.delete_photo, name="delete-photo", ), path( "<int:room_pk>/photos/<int:photo_pk>/edit/", views.EditPhotoView.as_view(), name="edit-photo", ), # ๐ ์์ ๋ฒํผ์ url ๊ฒฝ๋ก์ "EditPhotoView"๋ฅผ ์ฐ๊ฒฐ์ํต๋๋ค. path("search/", views.SearchView.as_view(), name="search"), ]
{% extends "base.html" %} {% block page_title %} {{room.name}}'s Photos {% endblock page_title %} {% block search-bar %} {% endblock search-bar %} {% block content %} <div class="container mx-auto my-10 flex flex-col p-6"> {% for photo in room.photos.all %} <div class="mb-5 border p-6 border-gray-400 flex justify-between"> <div class="flex items-start"> <img src="{{photo.file.url}}" class="w-32 h-32"> <span class="ml-5 text-xl">{{photo.caption}}</span> </div> <div class="flex flex-col w-1/5"> edit ๋ฒํผ ๐ <a class="btn-link mb-5 bg-teal-500" href="{% url 'rooms:edit-photo' room.pk photo.pk %}">Edit</a> <a class="btn-link bg-red-600" href="{% url 'rooms:delete-photo' room.pk photo.pk %}">Delete</a> </div> </div> {% endfor %} <div class="flex justify-center mt-5"> # ๐ ๋ค๋ก๊ฐ๊ธฐ btn <a href="{% url 'rooms:edit' room.pk %}" class="text-teal-500 font-medium text-xl">Back to edit room</a> </div> </div> {% endblock content %}
# rooms/photo_edit.html {% extends "base.html" %} {% block page_title %} Update Photo {% endblock page_title %} {% block search-bar %} {% endblock search-bar %} {% block content %} <div class="container lg:w-5/12 md:w-1/2 xl:w-1/4 mx-auto my-10 flex flex-col items-center border p-6 border-gray-400"> {% include 'mixins/room/room_form.html' with form=form cta="Update photo" %} </div> {% endblock content %}
from django.db.models import fields from django.http import Http404 from django.views.generic import ListView, DetailView, View, UpdateView, FormView from django.shortcuts import render, redirect, reverse from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin from users import mixin as user_mixins from . import models, forms ... ... class AddPhotoView(user_mixins.LoggedInOnlyView, FormView): model = models.Photo template_name = "rooms/photo_create.html" # ๐ renderํ template fields = ("caption", "file") # ๐ ์ฌ์ฉํ fields form_class = forms.CreatePhotoForm # ๐ Form ์ง์
from django.urls import path from . import views app_name = "rooms" urlpatterns = [ ... ... path("<int:pk>/photos/add", views.AddPhotoView.as_view(), name="add-photos"), ... ...
# room_photos.html <div class="my-10 w-full"> # ๐ ์ํ๋ ์์น์ ์ถ๊ฐํด์ฃผ์ธ์:) <a href="{% url 'rooms:add-photo' room.pk %}"" class="btn-link w-1/6 block">Upload Photo</a> </div>
# photo_create.html {% extends "base.html" %} {% block page_title %} Upload photo {% endblock page_title %} {% block search-bar %} {% endblock search-bar %} {% block content %} <div class="container lg:w-5/12 md:w-1/2 xl:w-1/4 mx-auto my-10 flex flex-col items-center border p-6 border-gray-400"> {% include 'mixins/room/room_form.html' with form=form cta="Upload photo" %} </div> {% endblock content %}
# rooms/forms.py from django import forms from django_countries.fields import CountryField from . import models ... ... class CreatePhotoForm(forms.ModelForm): class Meta: model = models.Photo fields = ("caption", "file") def save(self, *args, **kwargs): photo = super().save(commit=False)
... ... class AddPhotoView(user_mixins.LoggedInOnlyView, FormView): model = models.Photo template_name = "rooms/photo_create.html" fields = ("caption", "file") form_class = forms.CreatePhotoForm def form_valid(self, form): # ๐ form_valie() ๋งค์๋ ์ฌ์ฉ pk = self.kwargs.get("pk") # ๐ pk๊ฐ์ queryset์์ ๊ฐ์ ธ์,, form.save(pk) # ๐ form์ save() ๋งค์๋์ ๋๊ฒจ์ค๋๋ค.
# rooms/forms.py from django import forms from django_countries.fields import CountryField from . import models ... ... class CreatePhotoForm(forms.ModelForm): # ๐ ModelForm์ผ๋ก ๋ง๋ค๊ป์:) class Meta: model = models.Photo fields = ("caption", "file") def save(self, pk, *args, **kwargs): photo = super().save(commit=False) # print(pk) # ๐ View์์ ์ ๋ฌ๋ pk๊ฐ์ ํ์ธํ ์ ์์ด์. room = models.Room.objects.get(pk=pk) # ํด๋น pk๊ฐ์ Room Object๋ฅผ ๊ฐ์ ธ์์. photo.room = room # ๐ ์์ฑ๋ Object์ room field๊ฐ์ ์ง์ ํด์ค๋๋ค. photo.save() # ๐ ๊ทธ๋ฆฌ๊ณ ์ ์ฅ.
... ... class AddPhotoView(user_mixins.LoggedInOnlyView, FormView): model = models.Photo template_name = "rooms/photo_create.html" fields = ("caption", "file") form_class = forms.CreatePhotoForm def form_valid(self, form): pk = self.kwargs.get("pk") form.save(pk) messages.success(self.request, "Photo Uploaded") return redirect(reverse("rooms:photos", kwargs={"pk": pk}))