view 란?
view 는 Django 어플리케이션이 일반적으로 특정 기능과 템플릿을 제공하는 웹페이지의 한 종류 (django documentation)
View는 필요한 데이타를 모델 (혹은 외부)에서 가져와서 적절히 가공하여 웹 페이지 결과를 만들도록 컨트롤하는 역할을 한다.
일반 MVC Framework에서 말하는 Controller와 비슷한 역할을 한다.
뷰(view) 는 애플리케이션의 "로직"을 넣는 곳(djangogirls)
view의 구조
# views.py
from django.shortcuts import render
def post_list(request):
posts = Post.objects.all()
return render(request, 'blog/blog_list.html', {'posts': posts})
def post_detail(request, pk):
post = Post.objects.get(pk=pk)
# urls.py
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('/post_list', views.post_list, name='post_list'),
path('/post_detail/<int:pk>', views.post_detail),
]
# localhost:8000/post_list 로 이동하면 views.py의 post_list 함수 실행
# localhost:8000/post_detail/1
일반적인 함수형 view의 형태, 첫 번째 파라미터로 HTTP request를 받는다.
함수의 반환은 HttpResponse 객체를 반환한다.
from django.http import HttpResponse
# def 로직은 생략
return HttpResponse('<p>hello world</p>')
from django.http import HttpResponse
from django.template import loader
t = loader.get_template('myapp/index.html')
return HttpResponse(t.render)
request
view 함수에서의 첫 번째 파라미터인 request는 요청 및 응답에 대한 전반적인 처리를 담당하는 HttpRequest 객체를 의미
def my_view(request):
print(request.scheme) # usually http or https
print(request.path) # "/post/list" not including scheme or domain
# request.method
if request.method == 'GET':
do_get_something()
elif request.method == 'POST':
do_post_something()
use with ajax
// ajaxTest.js
var username = 'jeonghwan';
var data = {
'name': username
};
$('#button').click(function(e){
$.post(url, data)
.done(function(result){
alert('ajax success');
})
.fail(function(){
alert('ajax fail');
})
})
# views.py
def check_ajax(request):
name = request.POST.get('name') # 'jeonghwan'
name = request.POST['name'] # 'jeonghwan'
"""
차이점?
.get('name')은 POST로 들어온 값 중에서 'name'이 없으면 return None
값이 없는 경우 두 번째 파라미터에 디폴트 값을 지정해줄 수 있음.
name = request.POST.get('name', 'park')
['name']은 없으면 raise KeyError
"""
*shortcut(from django.shortcuts import )
view에서 사용하는 객체나 함수를 좀 더 편하게 사용할 수 있도록 지원하는 패키지
-
render
from django.shortcuts import render return render(request, template_name, context=None, content_type=None, status=None, using=None) # required arguments => request, template_name # context = 템플릿에 넘겨줄 값들의 dictionary로 ex) { 'post': post } # content_type = default 값은 'text/html', 문서 참고 # status = 응답코드, default 값은 200 # using = 템플릿을 로드하는데 사용되는 템플릿 엔진의 이름, 기본값은 엔진 클래스를 정의하는 모듈의 이름... 뭔 말인가... 'BACKEND': 'django.template.backends.django.DjangoTemplates', ??
-
get_object_or_404
from django.shortcuts import get_object_or_404 from .models import Post post = get_object_or_404(Post, pk=pk) """ 직독직해로도 알 수 있듯이 해당 모델에서 원하는 값을 반환 - call get() 없으면 404 - raise Http404 ... not DoesNotExist """
-
redirect
from django.shortcuts import redirect # view 함수 내에서 특정 url로 이동하고자 할 때 사용 # url로 이동할 수 있는 HttpResponseRedirect 객체 반환 def my_view(request): ... return redirect('https://example.com/')
reverse() 나 get_absolute_url() 과 같이 쓰일 수 있다.
-
reverse()
# urls.py app_name = 'blog' urlpatterns = [ path('/post_detail/<int:pk>', views.post_detail, name='post_detail'), ]
# views.py from django.shortcuts import redirect from django.urls import reverse def post_detail(request, pk): post = Post.objects.get(pk=pk) # name 값으로 url에 접근 가능, 스트링으로 반환 # /post_detail/<int:pk> ... /post_detail/2 url = reverse('blog:post_detail', args=[2]) url = reverse('blog:post_detail', kwargs={'pk': post.pk}) return redirect(url)
-
get_absolute_url()
# models.py class Post(models.Model): ... def get_absolute_url(self): return reverse('blog:post_detail', args=[self.id])
# views.py from django.shortcuts import redirect from .models import Post def post_detail(request, pk): post = Post.objects.get(pk=pk) return redirect(post) # get_absolute_url() method will be called to figure out the redirect URL
<!-- post_list.html --> <a href='{{ post.get_absolute_url }}'>{{ post.name }}</a>
get_absolute_url() 메소드 이용시, view 로직이나 html에서 해당 url 작성이 간결해질 수 있음
-
Decorator
기존의 코드에 기능을 추가하기 위해 사용
# views.py
from django.contrib.auth.decorators import login_required
from django.views.decorators import require_POST
...
@login_required
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'post_detail.html', {'post': post})
@require_POST
def only_post(request, pk):
post = get_object_or_404(Post, pk=pk)
form = PostForm(request.POST)
form.save()
CBV(Class Based View)
- Organization of code related to specific HTTP methods (
GET
,POST
, etc.) can be addressed by separate methods instead of conditional branching.- Object oriented techniques such as mixins (multiple inheritance) can be used to factor code into reusable components.
- 참고1
- 참고2
- Class-based views API reference
-
FBV(Function Based View)
# urls.py app_name = 'blog' urlpatterns = [ path('/list', views.post_list, name='post_list'), ]
# views.py def post_list(request): posts = Post.objects.all() return render(request, 'blog/blog_list.html', {'posts': posts})
-
CBV(Class Based View)
# urls.py from .views import PostList app_name = 'blog' urlpatterns = [ path('/list', PostList.as_view(), name='post_list'), ]
# views.py from django.views import View class PostList(View): def get(self, request): posts = Post.objects.all() return render(request, 'blog/blog_list.html', {'posts': posts}) def post(self, request): do_something()
TemplateView
- tempate_name 변수에 파일명이나 경로 지정해주면 요청 처리
- get_context_data 메서드 : context 데이터 처리, 코드 까보면 Mixin 개념이 활용된 것을 볼 수 있다. 참고
from django.views.generic import TemplateView class PostList(TemplateView): template_name = 'blog/blog_list.html' def get_context_data(self, **kwargs): context = super(PostList, self).get_context_data(**kwargs) posts = Post.objects.all() context['posts'] = posts return context
# 위에서 오버라이딩한 get_context_data method class ContextMixin(object): def get_context_data(self, **kwargs): if 'view' not in kwargs: kwargs['view'] = self return kwargs
ListView
from django.views.generic.list import ListView class PostListView(ListView): """ template_name 디폴트 값은 '모델명 소문자_list.html', post_list.html 컨텍스트 변수 디폴트 값은 object_list """ model = Post template_name = 'blog/blog_list_view.html' # 템플릿 파일 지정 context_object_name = 'posts' # 컨텍스트 변수 이름 지정 paginate_by = 5 # 페이징 처리를 위해 선언 한 페이지당 5개 글 def get_queryset(self): return Post.objects.filter(published_date__lte=timezone.now())
<!-- blog_list_view.html --> <!-- get_queryset() 메소드에서 가져온 값의 존재 유무 확인 --> {% if posts %} <ul> <!-- 있으니까 쫙 나열 --> {% for post in posts %} <li><a href="{% url 'blog:post_detail' post.pk %}">{{ post.title }}</a></li> {% endfor %} </ul> {% else %} <p>No post</p> {% endif %} <!-- 페이징 처리 --> {% if is_paginated %} <ul class='pagination'> <!-- 현재 페이지를 기준으로 이전 페이지가 있으면 --> {% if page_obj.has_previous %} <li><a href="?page={{ page_obj.previous_page_number }}">«</a></li> {% else %}<!-- 없으면 --> <li class="disabled"><span>«</span></li> {% endif %} <!-- 페이지 번호 표시 --> {% for i in paginator.page_range %} <!-- 페이지 번호가 현재 있는 페이지와 동일한 경우 --> {% if page_obj.number == i %} <li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li> {% else %} <!-- 다른 경우 이동할 수 있도록 a 태그 사용 --> <li><a href="?page={{ i }}">{{ i }}</a></li> {% endif %} {% endfor %} <!-- 현재 페이지를 기준으로 다음 페이지가 있으면 --> {% if page_obj.has_next %} <li><a href="?page={{ page_obj.next_page_number }}">»</a></li> {% else %}<!-- 없으면 --> <li class="disabled"><span>»</span></li> {% endif %} </ul> {% endif %}
DetailView
from django.views.generic.detail import DetailView class PostDetailView(DetailView): """ 템플릿 파일 디폴트 : 모델명 소문자_detail.html ... post_detail.html 컨텍스트 변수 디폴트 : object url의 pk 파라미터 값을 활용하여 해당 값 조회 """ model = Post template_name = 'blog/blog_detail_view.html' context_object_name = 'post'
<!-- blog_detail_view.html --> <div class="post"> {% if post.published_date %} <div class="date"> {{ post.published_date }} </div> {% endif %} <h1>{{ post.title }}</h1> <p>{{ post.text|linebreaksbr }}</p> </div>