본 포스트는 Inflearn 'Vue.js - Django 연동 웹 프로그래밍' 강의 일부를 요약한 내용입니다
장점
: todo item을 저장하거나 삭제할 때 페이지 이동이 발생하지 않음! (Vue.js에서 제공하는 SPA - Single Page Application 기능)단점
: todo item을 저장하거나 삭제할 때 페이지 이동 발생(3개의 html)django-mixin
믹스인 기능
을 활용하는 방식URL패턴 | 뷰 이름 | 템플릿 파일명 |
---|---|---|
todo/mixin/ | TodoMOMCV(MultipleObjectMixin, CreateView) | todo_form_list.html |
todo/99/delete2 | TodoMixinDelV(DeleteView) | 없음(팝업창) |
주의 : view에서 template으로 넘겨줘야 할 context 변수가 어떤 것인지 체크해야 함!
- 예제) form에서는 context변수 사용X, list에서는 object_list변수 사용
다중 상속을 받을 때는 좀 더 복잡한 CreateView를 메인으로 두고 ListView기능은 mixin으로 처리하는 것이 에러 가능성을 줄이는 방법임!!
- 코딩 예시 : (mixin, main)
- mixin 먼저 쓰고 main은 나중에 씀!
- 'mixin'은 메인 재료에 추가되는 첨가물을 의미함
getContextData()
기능multipleObjectMixin
클래스에 들어있음urlpatterns = [
# 중략..
# Multiple Object Mixin Create View
path('mixin/', views.TodoMOMCV.as_view(), name="mixin"),
]
<!-- 중략... body 부분만 -->
{% block content %}
<div id = 'app'>
<h1>My Todo App !</h1>
<strong>서로 할 일이나 의견을 공유해 봅시다.</strong>
<br>
<!-- form을 보여주는 부분->
<!-- form태그에는 action과 method속성이 필요함 -->
<form class="inputBox" action="" method="POST"> {% csrf_token %} <!--csrf공격 방지 위한 토큰-->
<input class="name" type="text" placeholder="name ..." name="name">
<input class="item" type="text" placeholder="type anyting welcomed ..."
name="todo"> <!-- 엔터 눌러도 add기능 되도록 구현 -->
<!-- v-model대신 name속성으로 변경 = 변수명은 todoTable의 변수명과 동일 해야함(주의!) -->
<button type="submit" class="btn btn-info btn-sm">ADD</button>
</form>
<!-- list를 보여주는 부분-->
<ul class="todoList">
<!-- v-for는 장고 템플릿 문법으로 대체 -->
{% for todo in object_list %} <!-- listView에서는 object_list라는 context 변수를 넘겨줌 -->
<li>
<!-- vue.js {mustache문법} -> { { 장고 템플릿 문법 } } 으로 변경 -->
<span>{{ todo.name }} :: {{ todo.todo }}</span> <!-- column명은 todo임!-->
<!-- 삭제버튼 클릭했을 때의 동작 <a>태그로 넣어줌 : delete url요청 & 파라미터로 todoID 넣음 -->
<span class="removeBtn"><a href="{% url 'todo:delete' todo.id %}">×</a></span>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}]
class TodoMOMCV(MultipleObjectMixin, CreateView):
# CreateView form처리 위한 것(model속성, fields속성, templatename속성 필요)
model = Todo
fields = '__all__'
template_name = 'todo/todo_form_list.html'
# redirect 위한 코드 (새로 만든 'mixin' url로 redirect)
success_url = reverse_lazy('todo:mixin')
# object_list context변수 만들기 위해 self.object_list 변수 미리 준비 -> get, post 메소드에서
# CreateView의 get메서드 overriding
def get(self, request, *args, **kwargs):
# DB에서 레코드들을 꺼내오는 메서드 : get_queryset()
# get_queryset()메서드는 MultipleObjectMixin과 CreateView 둘 다 가지고 있기 때문에 먼저 오는 클래스에서 상속받음!
self.object_list = self.get_queryset()
# 상위 클래스의 메서드 오버라이딩 시 인자는 동일하게 써줌 (특별한 경우에만 변경)
return super().get(request, *args, **kwargs)
# 상위 클래스의 get메서드 그대로 호출
# => CreateView의 getContextData()메서드 호출
# => getContextData()메서드에 의해서 obejct_list라는 context변수가 template에 넘어가게 됨!
# CreateView의 post메서드 overriding
# get메서드와 동일한 방식으로 overriding
def post(self, request, *args, **kwargs):
self.obejct_list = self.get_queryset()
return super().post(request, *args, **kwargs)
<body>
<!-- 중략 -->
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav mr-auto">
<li class="nav-item mx-1 btn btn-primary">
<a class="nav-link text-white" href="{% url 'home' %}">Home</a></li>
<li class="nav-item mx-1 btn btn-primary">
<a class="nav-link text-white" href="{% url 'todo:vonly' %}">VueOnly</a></li>
<li class="nav-item mx-1 btn btn-primary">
<a class="nav-link text-white" href="{% url 'todo:create' %}">DjangoOnly</a></li>
<!-- DjangoMixin 메뉴 추가 -->
<li class="nav-item mx-1 btn btn-primary">
<a class="nav-link text-white" href="{% url 'todo:mixin' %}">DjangoMixin</a></li>
<!-- 중략 -->
<!-- todo_form_list.html body 일부분 -->
<ul class="todoList">
<!-- v-for는 장고 템플릿 문법으로 대체 -->
{% for todo in object_list %} <!-- listView에서는 object_list라는 context 변수를 넘겨줌 -->
<li>
<!-- vue.js {mustache문법} -> { { 장고 템플릿 문법 } } 으로 변경 -->
<span>{{ todo.name }} :: {{ todo.todo }}</span> <!-- column명은 todo임!-->
<!-- 삭제버튼 클릭했을 때의 동작 <a>태그로 넣어줌 : delete url요청 & 파라미터로 todoID 넣음 -->
<span class="removeBtn"><a href="{% url 'todo:delete2' todo.id %}">×</a></span>
</li>
{% endfor %}
</ul>
urlpatterns = [
# 중략
# delete2 url 추가
path('<int:pk>/delete2/', views.TodoDelV2.as_view(), name='delete2'),
]
class TodoDelV2(DeleteView):
model = Todo
# template은 기존 template 사용
template_name = 'todo/todo_confirm_delete.html'
# 삭제 성공 후 redirect : mixin으로 변경
success_url = reverse_lazy('todo:mixin')
정리
1. mixin페이지서에는 form과 list모두 보여줌
2. list부분에서 x표시를 눌러서 삭제 페이지(delete2)로 이동
3. 삭제 페이지(delete2)에서 삭제를 완료하면 list페이지(이전 Django-Only에서 사용하던 페이지)가 아니라 mixin페이지로 돌아오도록 구현
기존 DeleteView는 get요청
과 post요청
두 개 다 처리를 해야 삭제 완료가 되었음
- x표시를 눌렀을 때 보여주는 화면(/todo/id/delete2/) : get요청
으로 메시지와 confirm버튼 보여줌
- confirm버튼을 눌렀을 때 post요청
을 보내고 그 때 DB에서 레코드 하나를 삭제함
수정할 부분 : get요청
에 대한 처리를 생략하고 post요청
처리만 하도록 설계
-> deleteView에도 get과 post 메서드가 있음
-> get은 단순히 form을 보여주는 로직임
(출처 : https://ccbv.co.uk/)
-> post는 delete메서드를 호출하고 delete에서 실제 삭제가 이루어짐
팝업창 없이 그냥 바로 삭제
# views.py
class TodoDelV2(DeleteView):
model = Todo
# form을 보여주는 과정이 생략되었으므로 template_name은 불필요
# template_name = 'todo/todo_confirm_delete.html'
success_url = reverse_lazy('todo:mixin') # 삭제 성공 후 redirect : mixin으로 변경
# 기존 로직에서 get요청이 왔을 때 페이지 이동 없이 바로 delete처리를 하도록 overriding
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
{% block extra-style %}
<style>
/* 중략 */
/* x표 위에 마우스 위치했을 때 색 변경 */
.removeBtn:hover {
color: red;
}
.modal-footer a {
color: white;
}
</style>
{% endblock %}
<!-- 중략 -->
<!-- 삭제버튼 클릭했을 때의 팝업창이 뜨도록 토글형식으로 설정하고 타겟을 id로 지정해줌-->
<!-- 정보를 넘겨주기 위해 data-id/name/todo 지정-->
<span class="removeBtn" data-toggle="modal" data-target="#myModal"
data-id="{{ todo.id }}" data-name="{{ todo.name }}" data-todo="{{ todo.todo }}">×</span>
</li>
{% endfor %}
</ul>
<!-- Modal -->
<div class="modal" id="myModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Are you sure to delete ?</h5>
</div>
<div class="modal-body">
body-text
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-sm">
<a href="">Delete</a></button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
<!-- 데이터를 전달받기 위한 JavaScript -->
{% block extra-script %}
<script>
$('#myModal').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget)
var id = button.data('id')
var name = button.data('name')
var todo = button.data('todo')
var modal = $(this)
// name과 todo는 modal-body에 사용 -> body-text에 채워지는 내용
modal.find('.modal-body').text(name + '::: '+ todo)
// id는 url 만들때 사용 -> href의 값으로 채워지는 부분
modal.find('.modal-footer a').attr('href', '/todo' + id + '/delete2/')
})
</script>
{% endblock %}
VueOnly
로 추가, 삭제시 처음에만 페이지 로딩이 발생하고 이후 추가적으로 발생하지 않음 (실제로 페이지 이동 발생x) ➡ Client rendering
DjangoMixin
으로 추가, 삭제시 매번 페이지 로딩이 발생 (실제로 페이지 이동 발생O) ➡ Server rendering