Do It 실습 views.py 상세 분석

이정연·2022년 10월 4일
0

Django

목록 보기
8/12
post-thumbnail

CBV 구현

📄 PostList

CODE

class PostList(ListView): # ListView를 상속받은 Class
    model = Post # Post 모델 사용
    ordering = '-pk' # 포스트 최신순 정렬
    paginate_by = 5 # 한 페이지에 5개 포스트 표시

    # 카테고리 레코드 추가를 위한 함수 오버라이딩
    def get_context_data(self,**kwargs):
        context = super(PostList,self).get_context_data()
        context['categories'] = Category.objects.all()
        context['no_category_post_count'] = Post.objects.filter(category=None).count()

        return context

분석

  • 목적: 포스트를 표시하기 위한 함수
  • ListView 상속 받은 CBV
  • Post 모델 사용
  • 포스트 최신순 정렬
  • 한 페이지에 5개 포스트 표시

get_context_data

  • ListView, DetailView에 내장된 메서드

  • model = 모델 이라고 선언하면 get_context_data에서 자동으로 모델_list = 모델.objects.all() 명령

  • 따라서 모델_list.html에서 {% for 레코드 in 모델_list %} 바로 사용 가능

  • 본 코드에서는 카테고리 기능을 사용하기 위해 get_context_data 오버라이딩


🅰 PostDetail

CODE

class PostDetail(DetailView):
    model = Post

    def get_context_data(self,**kwargs):
        context = super(PostDetail,self).get_context_data()
        context['categories'] = Category.objects.all()
        context['no_category_post_count'] = Post.objects.filter(category=None).count()
        context['comment_form'] = CommentForm

        return context

분석

  • 목적: 포스트 목록에서 클릭했을 때 포스트의 자세한 내용을 보여주기 위해
  • DetailView를 상속한 CBV
  • PostList와 마찬가지로 카테고리/댓글 기능을 사용하기 위해 get_context_data 오버라이딩

👋🏻 PostCreate

CODE

class PostCreate(LoginRequiredMixin,UserPassesTestMixin,CreateView):
    model = Post
    fields = ['title','hook_text','content','head_image','file_upload','category']

    def test_func(self):
        return self.request.user.is_superuser or self.request.user.is_staff

    # form_valid 재정의
    def form_valid(self,form):
        current_user = self.request.user
        if current_user.is_authenticated and (current_user.is_staff or current_user.is_superuser):
            form.instance.author = current_user
            response = super(PostCreate,self).form_valid(form)

            tags_str = self.request.POST.get('tags_str')
            if tags_str:
                tags_str = tags_str.strip()

                tags_str = tags_str.replace(',',';')
                tags_list = tags_str.split(';')

                for t in tags_list:
                    t = t.strip()
                    tag,is_tag_created = Tag.objects.get_or_create(name=t)
                    if is_tag_created:
                        tag.slug = slugify(t,allow_unicode = True)
                        tag.save()
                    self.object.tags.add(tag)
            return response
        else:
            return redirect('/blog/')

분석

  • 클래스 3개를 상속받는 CBV
  • 여러 클래스를 동시 상속 받을 때는 Mixin Class를 사용한다.
  • LoginRequiredMixin은 로그인을 했을 때만 보여지도록
  • UserPassesTestMixin은 접속 유저의 권한을 알려준다.
  • Post Model을 사용하며 각 필드를 지정해주고 있다.

form_valid

  • 방문자가 폼에 담아 보낸 유효한 정보를 사용해 포스트를 만들고, 이 포스트의 고유 경로로 보내주는(redirect) 역할을 한다.
  • 필드에 작성자가 없다. 왜냐하면 작성자는 방문자이기 때문이다.
  • 따라서 이를 자동으로 완성시켜주기 위해 form_valid를 오버라이딩
  • 방문자가 로그인하였다면 작성자를 방문자로 하여 form 전송
  • 비로그인이면 블로그로 리다이렉트
  • 추가적으로 관리자/스태프만 글을 쓸 수 있도록 조건 추가

GET vs POST

클라이언트가 서버에 요청해서 서버가 제공해야하는 자원이 있다.

Ex)
클라이언트가 로그인을 하려고 한다.
아이디와 패스워드를 입력하고 올바른 정보인지 서버로부터 확인을 해야한다.

위와 같은 상황에서 클라이언트는 서버로부터 request를 보내야 한다.

GET 방식과 POST 방식 모두 서버에 request를 하는 것이지만

결정적인 차이는 다음과 같다.

  • GET: 클라이언트의 데이터를 URL에 붙여서 보낸다.
  • POST: 클라이언트의 데이터를 Body에 붙여서 보낸다.

GET

Ex)
ID: happyeon PW: 1234

회원정보가 위와 같다. 이 상황에서 GET 메소드를 이용하면

www.xxx.com?id=happyeon&pass=1234

이렇게 보내게 된다. URL뒤에 "?"를 통해 데이터 표현의 시작점을 알린다 그리고 "&"을 통해 key-value쌍을 구분한다.

  • URL 형태로 표현되므로 다른 사람이 접속 가능
  • 길이가 한정적이므로 데이터 양의 한계 존재

request.GET vs request.GET.get()

  • request.GET은 GET 뒤로 오는 모든 파라미터를 다 포함하는 딕셔너리
  • request.GET.get()은 key값이 딕셔너리 안에 있으면 value 리턴
  • 키가 없으면 None 리턴

POST

  • POST 방식은 URL이 아닌 Body에 데이터를 넣어 보낸다.
  • 따라서 Content-Type이라는 헤더필드 필요

🔑 PostUpdate

CODE

class PostUpdate(LoginRequiredMixin,UpdateView):
    model = Post
    fields = ['title','hook_text','content','head_image','file_upload','category','tags']

    template_name = 'blog/post_update_form.html'

    def get_context_data(self, **kwargs):
        context = super(PostUpdate, self).get_context_data()
        if self.object.tags.exists():
            tags_str_list = list()
            for t in self.object.tags.all():
                tags_str_list.append(t.name)
            context['tags_str_default'] = '; '.join(tags_str_list)

        return context

    def form_valid(self, form):
        response = super(PostUpdate, self).form_valid(form)
        self.object.tags.clear()

        tags_str = self.request.POST.get('tags_str')
        if tags_str:
            tags_str = tags_str.strip()
            tags_str = tags_str.replace(',', ';')
            tags_list = tags_str.split(';')

            for t in tags_list:
                t = t.strip()
                tag, is_tag_created = Tag.objects.get_or_create(name=t)
                if is_tag_created:
                    tag.slug = slugify(t,allow_unicode = True)
                    tag.save()
                self.object.tags.add(tag)

        return response
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
            return super(PostUpdate,self).dispatch(request,*args,**kwargs)
        else:
            raise PermissionDenied
  • template name을 따로 명시해줘서 포스트 수정 페이지를 따로 만듦
  • 기존에 걸려있던 태그들을 태그 추가 폼에 반영하기 위하여 get_context_data 오버라이딩
  • dispatch 메서드를 오버라이딩 하여 포스트 작성자와 수정 요청자 일치 확인

💬 CommentUpdate

CODE

class CommentUpdate(LoginRequiredMixin,UpdateView):
    model = Comment
    form_class = CommentForm

    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated and request.user == self.get_object().author:
            return super(CommentUpdate, self).dispatch(request,*args,**kwargs)
        else:
            raise PermissionDenied
  • form_class를 통해 댓글 폼으로 설정
  • PostUpdate view와 마찬가지로 dispatch를 통해 작성자 일치 확인

🔎 PostSearch

CODE

class PostSearch(PostList):
    paginate_by = None

    def get_queryset(self):
        q = self.kwargs['q']
        post_list = Post.objects.filter(
            Q(title__contains = q) | Q(tags__name__contains=q)
        ).distinct()
        return post_list

    def get_context_data(self,**kwargs):
        context = super(PostSearch, self).get_context_data()
        q = self.kwargs['q']
        context['search_info'] = f'Search: {q} ({self.get_queryset().count()})'

        return context

get_queryset

  • PostList Class를 상속받으며 한 페이지에 다 보여주기 위해 paginated_by 옵션을 None으로 설정
  • 127.0.0.1:8000/blog/q 👉🏻 검색어 q를 저장
  • 제목과 태그에 검색어 q가 포함된 모든 리스트를 뽑아온다

get_context_data

  • 페이지 상단에 "Search: 검색어 (포스트 개수)"를 출력한다

FBV 구현

🖇 category_page

CODE

def category_page(request,slug):
    if slug == 'no_category':
        category = '미분류'
        post_list = Post.objects.filter(category=None)
    else:
        category = Category.objects.get(slug=slug)
        post_list = Post.objects.filter(category=category)

    return render(
        request,
        'blog/post_list.html',
        {
            'post_list':post_list,
            'categories':Category.objects.all(),
            'no_category_post_count':Post.objects.filter(category=None).count(),
            'category': category,
        }
    )
  • 카테고리 선택시 포스트들 렌더링 기능
  • 파라미터: slug
  • 템플릿: post_list.html
  • slug를 기준으로 카테고리 정렬후 해당 포스트만 렌더링

🏷 tag_page

CODE

def tag_page(request,slug):
    tag = Tag.objects.get(slug=slug)
    post_list = tag.post_set.all()

    return render(
        request,
        'blog/post_list.html',
        {
            'post_list': post_list,
            'tag': tag,
            'categories': Category.objects.all(),
            'no_category_post_count': Post.objects.filter(category=None).count(),
        }
    )
  • 파라미터: slug
  • slug를 기준으로(선택한 태그를 기준으로) 포스트 리스트 렌더링
  • 템플릿: post_list.html

💬 new_comment

CODE

def new_comment(request,pk):
    if request.user.is_authenticated:
        post = get_object_or_404(Post,pk=pk)

        if request.method == 'POST':
            comment_form = CommentForm(request.POST)
            if comment_form.is_valid():
                comment = comment_form.save(commit=False)
                comment.post = post
                comment.author = request.user
                comment.save()
                return redirect(comment.get_absolute_url())
        else:
            return redirect(post.get_absolute_url())
    else:
        raise PermissionDenied
  • 댓글 작성 기능
  • 파라미터: pk (포스트 번호)
  • pk로 포스트를 갖고 오는데 없으면 404 발생
  • URL로 접근 요청시 GET 방식이므로 해당 포스트로 리다리엑트

    Ex) 127.0.0.1:8000/10/new_comment

  • CommentForm은 content 필드만 담고 있음

❌ delete_comment

CODE

def delete_comment(request,pk):
    comment = get_object_or_404(Comment,pk=pk)
    post = comment.post
    if request.user.is_authenticated and request.user == comment.author:
        comment.delete()
        return redirect(post.get_absolute_url())
    else:
        raise PermissionDenied
  • 댓글 삭제 기능
  • 로그인 & 작성자 일치 확인
profile
0x68656C6C6F21

0개의 댓글