Dstargram project - 2

[ Dstargram project ]

  • 접근 권한 주기

    • 해당 글을 올린 글쓴이가 아니라면 수정이나 삭제를 할 수 없도록 처리
    • views.py에서 dispatch() 적용

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      from django.http import HttpResponseRedirect
      from django.contrib import messages

      class PhotoUpdate(UpdateView):
      model = Photo

      fields = ['text', 'image']
      template_name = 'photo/photo_update.html'
      # success_url = '/'

      def dispatch(self, request, *args, **kwargs):
      object = self.get_object()
      if object.author !=request.user:
      # 삭제 페이지에서 권한이 없다!
      # 원래 디테일 페이지로 돌아가서 삭제에 실패했다!
      # 사용자가 선택할 수 없는 기능들은 눈에 안보이게 하는 것이 좋다.
      messages.warning(request, "수할 권한이 없습니다.")
      return HttpResponseRedirect(object.get_absolute_url())
      else:
      super(PhotoUpdate, self).dispatch(request, *args, **kwargs)

      class PhotoDelete(DeleteView):
      model = Photo
      template_name = 'photo/photo_delete.html'
      success_url = '/'

      # Life Cycle - iOS, Android, Vue, React, Django, Rails
      # Framework는 라이프 사이클이 존재 : 어떤 순서로 구동이 되는가?
      # URLConf -> View -> Model 순으로
      # 어떤 뷰를 구동할 때 그 안에서 동작하는 순서

      # 사용자가 접속했을 때, get? post? 등을 결정하고 분기하는 부분
      # 로직을 수행하고, 템플릿을 랜더링한다.
      def dispatch(self, request, *args, **kwargs):
      object = self.get_object()
      if object.author !=request.user:
      # 삭제 페이지에서 권한이 없다!
      # 원래 디테일 페이지로 돌아가서 삭제에 실패했다!
      # 사용자가 선택할 수 없는 기능들은 눈에 안보이게 하는 것이 좋다.
      messages.warning(request, "삭제할 권한이 없습니다.")
      return HttpResponseRedirect(object.get_absolute_url())
      else:
      super(PhotoDelete, self).dispatch(request, *args, **kwargs)

      # get과 post는 세트
      # get과 post는 로직을 수행하고, 템플릿을 렌더링한다.
      # def get(self, request, *args, **kwargs):
      #
      # def post(self, request, *args, **kwargs):
      #
      # def get_object(self, queryset=None):
      # 해당 쿼리셋을 이용해서 현재 페이지에 필요한 object를 인스턴스화 한다.
      # pass

      # def get_queryset(self):
      # 어떻게 데이를 가져올 것인가?
      # pass
    • base.html 수정

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <!--// 접근 권한이 없다는 메세지 위치 설정 및 출력 //-->
      <div class="container">
      {% if messages %}
      <div class="row">
      <div class="col"></div>
      <div class="col-6">
      {% for message in messages %}
      <div class="alert alert-danger">{{message}}</div>
      {% endfor %}
      </div>
      <div class="col"></div>
      </div>
      {% endif %}
      {% block content %}

      {% endblock %}
      </div>
    • photo_detail.html 수정

      1
      2
      3
      4
      5
      <!--// 해당 글을 올린 사용자가 아니라면 버튼이 보지 않는다. //-->
      {% if user == object.author %}
      <a href="{% url 'photo:update' object.id %}" class="card-link">수정</a>
      <a href="{% url 'photo:delete' object.id %}" class="card-link">삭제</a>
      {% endif %}
    • disqus.com을 이용하여 ‘댓글 달기’ 앱 이용

      • disqus.com 접속 및 가입 후, shortname 지정
      • 장고 프로젝트로 돌아와서 disqus 설치

        1
        $ pip install disqus
      • settings.py 수정

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        # disqus 추가
        # disqus.contrib.sites 추가
        INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'accounts',
        'photo',
        'disqus',
        'django.contrib.sites',
        ]

        # django-disqus : 데이터베이스가 필요없음 -> disqus.com에서 관리함
        # django.contrib.sites : 우리 프로젝트 사이트 정보 관리 -> python manage.py migrate를 해준다.

        DISQUS_WEBSITE_SHORTNAME = 'koostargram' # disqus에서 정한 shortname
        SITE_ID = 1
      • detail.html 수정

        1
        2
        3
        4
        5
        {# load xxx 해당 태그 기능을 지금부터 사용하겠다. #}
        {% load disqus_tags %}
        <div class="card-body">
        {% disqus_show_comments %}
        </div>
  • Instargram을 이용한 ‘좋아요’와 ‘저장’ 기능 구현

    • Instargram에서 이미지 파일 가져오기
      • background-image를 통해 이미지 저장
      • 요소 검사를 통해 ‘좋아요’와 ‘저장’ 이미지 위치 및 크기 픽셀 파악
    • dstargram_project/static/images에 button_image.png로 저장
    • base.html에서 이미지를 불러오고 스타일 적용

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      {% load static %}
      <!--// <img src="{% static 'images/button_image.png' %}"> //-->
      <style>
      .btn-like {
      display:inline-block; /* html 요소의 영역 표시 속성 */
      width:24px; /* 너비 */
      height:24px; /* 높이 */
      background:url({% static 'images/button_image.png' %}); /* 배경 이미지 */
      background-position:-231px -130px; /* 배경 이미지 포지션 */
      text-indent:-9999px; /* 영역 안에 있는 텍스트의 들여쓰기 */
      overflow:hidden; /* 영역 바같으로 밀려난 컨텐츠의 표시 여부 */
      border:none; /* 테두리 여부 */
      }
      /*
      어떤 요소를 클릭했을 때, class를 더하거나 빼서 효과를 변경한다.
      */
      .btn-like.active {
      background-position:-231px -104px;
      }

      .btn-save {
      display:inline-block;
      /*
      inline : text 형태 컨텐츠가 있는 만큼 영역을 차지하는 형태, 한줄에 여러 요소가 있을 수 있다.
      block : 한 줄을 단독으로 차지하고, 크기는 지정할 수 있다.
      inline-block : text 형태로 한 줄에 여러 요소가 함께 있을 수 있지만, 크기도 지정할 수 있다.
      */
      width:24px;
      height:24px;
      background:url({% static 'images/button_image.png' %});
      background-position:-208px -255px;
      text-indent:-9999px;
      overflow:hidden;
      border:none;
      }

      .btn-save.active {
      background-position:-130px -255px;
      }
      </style>

      <!-- //
      <div class="btn-like"></div>
      <div class="btn-save"></div>
      //-->

      <!--// 자바 스크립트를 이용하여 버튼 변경 테스트
      <script type="text/javascript">
      $(function() {
      $('.btn-like').click(function(e) {
      //e.preventDefault();
      //$(this).toggleClass('active');
      //$(this).blur();
      });

      $('.btn-save').click(function(e) {
      //e.preventDefault();
      //$(this).toggleClass('active');
      //$(this).blur();
      });
      });
      </script>
      //-->
    • models.py 수정

      1
      2
      3
      4
      # Photo class에 필드 추가
      like = models.ManyToManyField(User, related_name='like_post', blank=True)
      saved = models.ManyToManyField(User, related_name='save_post', blank=True)
      # makemigrations 및 migration 적용
    • views.py 수정

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      from django.views.generic.base import View
      from django.http import HttpResponseForbidden
      class PhotoLike(View):
      def get(self, request, *args, **kwargs):
      # like를 할 정보가 있다면 진행, 없다면 증단
      if not request.user.is_authenticated:
      return HttpResponseForbidden()
      else:
      # 1. 어떤 포스팅?
      # url : www.naver.com/blog/like/?photo_id=1
      # request.GET.get('photo_id')
      # url : www.naver.com/blog/like/1/
      # path('blog/like/<int:photo_id>')
      # kwargs['photo_id']
      # 2. 누가?
      if 'photo_id' in kwargs:
      photo_id = kwargs['photo_id']
      photo = Photo.objects.get(pk=photo_id)
      user = request.user

      if user not in photo.like.all():
      photo.like.add(user)
      else:
      photo.like.remove(user)
      return HttpResponseRedirect('/')

      class PhotoSave(View):
      def get(self, request, *args, **kwargs):
      # like를 할 정보가 있다면 진행, 없다면 증단
      if not request.user.is_authenticated:
      return HttpResponseForbidden()
      else:
      if 'photo_id' in kwargs:
      photo_id = kwargs['photo_id']
      photo = Photo.objects.get(pk=photo_id)
      user = request.user

      if user not in photo.saved.all():
      photo.saved.add(user)
      else:
      photo.saved.remove(user)
      return HttpResponseRedirect('/')
    • urls.py 수정

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # 모듈에 PhotoLike, PhotoSave 추가
      from .views import PhotoList, PhotoCreate, PhotoUpdate, PhotoDelete, PhotoDetail, PhotoLike, PhotoSave

      # urlpatterns에 like, save 경로 추가
      urlpatterns = [
      path('like/<int:photo_id>/', PhotoLike.as_view(), name='like'),
      path('save/<int:photo_id>/', PhotoSave.as_view(), name='save'),
      path('saved/', PhotoSaved.as_view(), name='saved'),
      path('', PhotoList.as_view(), name = 'index'),
      path('create/', PhotoCreate.as_view(), name = 'create'),
      path('update/<int:pk>', PhotoUpdate.as_view(), name = 'update'),
      path('delete/<int:pk>', PhotoDelete.as_view(), name = 'delete'),
      path('detail/<int:pk>', PhotoDetail.as_view(), name = 'detail'),
      ]
    • list.html 수정

      1
      2
      3
      4
      5
      6
      7
      <ul class="list-group list-group-flush">
      <li class="list-group-item">
      <!--// 조건을 이용하여 이미지 변경 적용 //-->
      <a href="{% url 'photo:like' object.id %}" class="float-left btn-like {% if user in object.like.all %} active {% endif %}">Like</a>
      <a href="{% url 'photo:save' object.id %}" class="float-right btn-save {% if user in object.saved.all %} active {% endif %}">Save</a>
      </li>
      </ul>
    • detail.html 수정

      1
      2
      3
      4
      5
      6
      <ul class="list-group list-group-flush">
      <li class="list-group-item">
      <a href="{% url 'photo:like' object.id %}" class="float-left btn-like {% if user in object.like.all %} active {% endif %}">Like</a>
      <a href="{% url 'photo:save' object.id %}" class="float-right btn-save {% if user in object.saved.all %} active {% endif %}">Save</a>
      </li>
      </ul>
Share