/MOVIDIC

나만의 인생 영화 사전

Primary LanguageHTML

Django Project: MOVIDIC

기억에 남는 인생 영화에 대한 정보와 평가를 저장하고 조회할 수 있는 서비스입니다.



프로젝트 목표

  • 데이터를 생성,조회,수정,삭제할 수 있는 Webapplication제작
  • Djangoweb framework를 사용한 데이터 처리
  • DjangoModel과 ORM에 대한 이해
  • Django ModelForm을 활용한 사용자 요청 데이터 유효성 검증

사용 기술


프로젝트 구조

📦05_pjt
┣ 📂movies
┃ ┣ 📂migrations
┃ ┃ ┣ 📜0001_initial.py
┃ ┃ ┗ 📜__init__.py
┃ ┣ 📂templates
┃ ┃ ┗ 📂movies
┃ ┃ ┃ ┣ 📜create.html
┃ ┃ ┃ ┣ 📜detail.html
┃ ┃ ┃ ┣ 📜index.html
┃ ┃ ┃ ┣ 📜search.html
┃ ┃ ┃ ┗ 📜update.html
┃ ┣ 📜admin.py
┃ ┣ 📜apps.py
┃ ┣ 📜forms.py
┃ ┣ 📜models.py
┃ ┣ 📜tests.py
┃ ┣ 📜urls.py
┃ ┣ 📜views.py
┃ ┗ 📜__init__.py
┣ 📂mypjt
┃ ┣ 📜asgi.py
┃ ┣ 📜settings.py
┃ ┣ 📜urls.py
┃ ┣ 📜wsgi.py
┃ ┗ 📜__init__.py
┣ 📂static
┃ ┗ 📂css
┃ ┃ ┣ 📜material-kit.min.css
┃ ┃ ┗ 📜nucleo-icons.css
┃ ┗ 📂js
┃ ┃ ┣ 📜bootstrap.min.js
┃ ┃ ┣ 📜material-kit.min.js
┃ ┃ ┗ 📜popper.min.js
┣ 📂templates
┃ ┗ 📜base.html
┣ 📜.gitignore
┣ 📜db.sqlite3
┣ 📜manage.py
┣ 📜README.md
┗ 📜requirements.txt



00_Model


필드명 데이터 타입 역할
title varchar(20) 영화 제목
audience integer 관객 수
release_date date 개봉일
genre varchar(30) 장르
score float 평점
poster_url text 포스터 경로
cover_url text 커버 이미지 경로
trailer_url text 예고편 경로
description text 줄거리


01_Index



MOVIDICindex페이지입니다.
서비스를 소개하는 header영역과, 등록한 영화 정보를 확인할 수 있는 section영역으로 이루어져 있습니다.
영화 카드를 클릭하면, 해당 영화의 상세 페이지로 이동됩니다.


  • views.index
    페이지가 요청되면, 등록된 영화 전체 데이터들을 QuerySet으로 반환하여 페이지를 렌더링합니다.
      @require_safe
      def index(request):
          movies = Movie.objects.all()
          context = {
              'movies': movies,
          }
          return render(request, 'movies/index.html', context)


02_Create



영화 정보를 등록할 수 있는 create 페이지 입니다.
index 페이지의 영화 등록 버튼을 클릭하여 접근 가능합니다.
Submit 버튼으로 제출하면 등록한 영화의 상세 페이지로 이동됩니다.


  • views.create
    Submit으로 제출된 데이터들을 DB에 저장합니다.
    POST 방식으로 요청이 들어왔을 때만, form의 유효성 검증을 합니다.
    유효성 검증을 통과하면 새로운 영화 데이터를 저장하고, 해당 영화 상세 페이지로 리다이렉트시킵니다.
    유효성 검증을 통과 못했거나, GET 방식으로 요청이 들어온 경우엔 create 페이지로 이동합니다.
      @require_http_methods(['GET', 'POST'])
      def create(request):
          if request.method == 'POST':
              form = MovieForm(request.POST)
              if form.is_valid():
                  movie = form.save()
                  return redirect('movies:detail', movie.pk)
          else:
              form = MovieForm()
          context = {
              'form': form,
          }
          return render(request, 'movies/create.html', context)


03_Detail



등록된 영화의 상세 내용을 조회하는 detail 페이지입니다.
해당 영화에 해당하는 커버 이미지와 포스터 이미지가 출력되며, 트레일러를 감상할 수 있는 링크 버튼도 있습니다.
해당 영화 데이터를 수정이나 삭제 할 수 있는 버튼도 마지막에 출력됩니다.


  • views.detail
    요청과 함께 넘어 온 pk로 영화를 특정하고, 해당 영화 데이터로 페이지를 렌더링합니다.

      @require_safe
      def detail(request, pk):
          movie = Movie.objects.get(pk=pk)
          context = {
              'movie': movie,
          }
          return render(request, 'movies/detail.html', context)


04_Update



등록된 영화의 상세 내용을 수정할 수 있는 update 페이지입니다.


  • views.update
    create와 마찬가지로, POST 방식으로 요청이 들어왔을 때는 form의 유효성 검증 후 해당 영화의 상세 페이지로 이동합니다.
    GET 방식의 요청이 들어오거나, 유효성 검증을 통과하지 못한 경우는 다시 update 페이지로 리다이렉트시킵니다.

      @require_http_methods(['GET', 'POST'])
      def update(request, pk):
          movie = Movie.objects.get(pk=pk)
          if request.method == 'POST':
              form = MovieForm(request.POST, instance=movie)
              if form.is_valid():
                  form.save()
                  return redirect('movies:detail', movie.pk)
          else:
              form = MovieForm(instance=movie)
          context = {
              'form': form,
              'movie': movie,
          }
          return render(request, 'movies/update.html', context)


05_Delete


특정 영화 정보를 삭제하기 위한 url입니다.


  • views.delete
    detail 페이지에서 삭제 요청을 통해 함께 받아온 데이터의 pk로 해당 영화 정보를 삭제합니다.

      @require_POST
      def delete(request, pk):
          movie = Movie.objects.get(pk=pk)
          movie.delete()
          return redirect('movies:index')


06_Search



Navigation bar를 통해 영화 제목을 키워드로 검색하면,
해당 키워드와 매칭이 되는 영화들이 조회되는 search 페이지입니다.


  • nav.form
    Navgiation bar에서 form으로 영화 제목을 입력 받아 서버로 전송합니다.

      <form action="{% url 'movies:search' %}" method="GET">
        {% csrf_token %}
        <div class="input-group input-group-dynamic">
          <span class="input-group-text text-dark">
            <i class="fas fa-search" aria-hidden="true"></i>
          </span>
          <input class="text-dark form-control" name="title" placeholder="Search" type="text">
        </div>
      </form>
  • views.search
    search/ url로 함께 들어온 title 파라미터로 특정한 영화들의 QuerySet을 반환하여 페이지에 렌더링합니다.
    또한, 받아온 keywordsearch 페이지에서도 활용하기 위해 context에도 담아줍니다.

      @require_safe
      def search(request):
          keyword = request.GET.get('title')
          movies = Movie.objects.filter(title__contains=keyword)
          context = {
              'movies': movies,
              'keyword': keyword
          }
          return render(request, 'movies/search.html', context)
  • search.htmlnav.form
    검색에 활용한 keyword를 검색 창과, 검색 결과 출력 화면에 계속 활용합니다.

      <form action="{% url 'movies:search' %}" method="GET">
        {% csrf_token %}
        <div class="input-group input-group-dynamic">
          <span class="input-group-text text-dark">
            <i class="fas fa-search" aria-hidden="true"></i>
          </span>
          <input class="text-dark form-control" name="title" placeholder="Searh" type="text" value="{{ keyword }}">
        </div>
      
      ...
    
      <h2 class="text-center mb-7">"{{ keyword }}" 검색 결과</h2>



프로젝트 회고


이번 프로젝트는 직전 프로젝트와 달리 model_form을 통한 유효성 검증 실습을 할 수 있었습니다.
또한 newcreate, editupdate를 하나의 url로 통합하고,
GET또는 POST 요청 방식에 따라 서버에서의 처리가 달라지도록 함수 구현을 하였습니다.
마지막으로 부트스트랩 템플릿을 활용하여 UI의 개선 또한 신경을 써보았습니다.
배운 것들을 종합해서 실습할 수 있는 시간이 매우 유익한 것 같습니다.
앞으로 추가적으로 배울 부분들까지 전부 습득을 하여 실습을 진행했을 떄의 결과가 기대됩니다.