GithubHelp home page GithubHelp logo

project_09's Introduction

๐Ÿ“Œ My Ninth Project ๐Ÿ“‹


- Outline : 2022๋…„ 5์›” 6์ผ, ์•„ํ™‰๋ฒˆ์งธ ๊ด€ํ†ต ํ”„๋กœ์ ํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์˜€๋‹ค. ์ด์ œ ์ •๋ง ์–ผ๋งˆ ๋‚จ์ง€ ์•Š์•˜๋‹ค๊ณ  ์ƒ๊ฐ์ด ๋“ ๋‹ค. ๋‹ค์Œ์ฃผ์— ๋งˆ์ง€๋ง‰ ๊ด€ํ†ตํ”„๋กœ์ ํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด, ์ตœ์ข…ํ”„๋กœ์ ํŠธ๋งŒ ์•ž๋‘๊ณ  ์žˆ๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด์„œ ์˜ค๋Š˜ ๋Š๊ผˆ๋˜ ์ ์— ๋Œ€ํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์—ฌ๊ธฐ์— ์ ์–ด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.
์˜ค๋Š˜ ํ”„๋กœ์ ํŠธ๋Š” ์กฐ๊ธˆ ํŠน์ดํ•˜๋‹ค๊ณ  ๋Š๊ปด์กŒ๋˜ ์ ์ด ์žˆ์—ˆ๋‹ค. ์–ด๋Š ์ •๋„ ์ž์œจ์„ฑ์ด ๋ณด์žฅ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ๋ง์€ ๊ณง ๊ธฐ๋Šฅ ๋ฉด์— ์žˆ์–ด์„œ๋Š” ์ž‘๋™์ด ๋  ์ˆ˜ ์žˆ๊ฒŒ๋” ์ž‘์„ฑํ•˜๋˜, ๊ทธ ์™ธ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ํŒ€ ๋ณ„๋กœ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑํ•˜๋ผ๋Š” ๋œป์ด์—ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์ž๋ฉด, ์ ์ ˆํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ํ™”๋ฅผ ์ถ”์ฒœํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค๋Š” ๊ฒƒ์ด ๊ทธ๋Ÿฌํ•˜์˜€๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ๋” ํŒ€์›๊ณผ ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฒƒ ๊ฐ™๊ณ , ์„œ๋กœ ์ƒ๊ฐ์„ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ๊ณง ๊ฐœ๋ฐœ์ด์ง€ ์•Š์„๊นŒ ์ƒ๊ฐ์„ ํ–ˆ๋˜ ๋‚ ์ด๊ธฐ๋„ ํ•˜์˜€๋‹ค.
์˜ค๋Š˜ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋ฉด์„œ ๋Š๋‚€ ์ ๋ถ€ํ„ฐ ํ•œ ์ค„๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งํ•˜์ž๋ฉด, '์ฃผ๋ง๋™์•ˆ ๋งŽ์€ ๋ณต์Šต์„ ํ†ตํ•ด ๋” ๋งŽ์€ ๋„์›€์ด ๋˜์–ด์ฃผ๊ณ  ์‹ถ๋‹ค.' ์˜€๋‹ค. ์˜ค๋Š˜์€ ๊ฐ™์€ ๋ฐ˜์˜ '๊ณ ์€๋ฏผ' ํ•™์šฐ์™€ ํŽ˜์–ด๋ฅผ ์ด๋ฃจ์–ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์˜€๊ณ , ๊ทธ ๊ณผ์ • ์†์—์„œ ๋Š๋‚€ ์ ์„ ์ค‘์‹ฌ์œผ๋กœ ์„œ์ˆ ํ•ด ๋ณด๊ณ ์ž ํ•œ๋‹ค. ๋˜ํ•œ ๊ฐœ์„ ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„๋„ ์ž‘์„ฑํ•ด๋ณด๋ ค ํ•œ๋‹ค.


< Title : "์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ ์šฉํ•œ ์„œ๋ฒ„ ๊ตฌ์„ฑ" >

(This project was carried out in Python 3.9.9 and Django 3.2.12 environment .)

  • ์š”๊ตฌ์‚ฌํ•ญ : ์ปค๋ฎค๋‹ˆํ‹ฐ ์„œ๋น„์Šค์˜ ์ƒ์„ธ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์„ ์œ„ํ•œ ๋‹จ๊ณ„๋กœ, ๋น„๋™๊ธฐ ํ†ต์‹ (AJAX)์„ ํ™œ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ UI/UX๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(ํ”„๋กœ์ ํŠธ ํŒŒ์ผ์˜ settings.py, urls.py์™€ base.html ๋“ฑ์˜ ๊ธฐ๋ณธ ์„ค์ •์€ ์ œ์™ธํ•˜๊ณ  , ์•ฑ ํŒŒ์ผ์— ๊ด€ํ•ด์„œ๋งŒ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.)


  • ์œ ์ € ํŒ”๋กœ์šฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„

    • accounts/views.py

      # follow ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ๋งŒ ์ž‘์„ฑ.
      
      @require_POST
      def follow(request, user_pk):
          followed = False
          if request.user.is_authenticated:
              person = get_object_or_404(get_user_model(), pk=user_pk)
              user = request.user
              if person != user:
                  if person.followers.filter(pk=user.pk).exists():
                      person.followers.remove(user)
                  else:
                      person.followers.add(user)
                      followed = True
              context = {
                  'followed': followed,
                  'followerCount': user.followers.count(),
                  'followingCount': user.followings.count(),
              }
              return JsonResponse(context)
          return redirect('accounts:login')
    • accounts/profile.html

      {% extends 'base.html' %}
      
      {% block content %}
        <h1>{{ person.username }}์˜ ํ”„๋กœํ•„ ํŽ˜์ด์ง€</h1>
        {% with followings=person.followings.all followers=person.followers.all %}
          <div>
            <div id='follow-count'>
              ํŒ”๋กœ์ž‰ : {{ followings|length }} / ํŒ”๋กœ์›Œ : {{ followers|length }}
            </div>
            {% if request.user != person %}
              <div>
                <form action="{% url 'accounts:follow' person.pk %}" id="follow-form" method="POST" data-user-id="{{ person.pk }}">
                  {% if request.user.is_authenticated %}
                    {% csrf_token %}
                  {% endif %}
                  {% if request.user in followers %}
                    <button id="followBtn">์–ธํŒ”๋กœ์šฐ</button>
                  {% else %}
                    <button id="followBtn">ํŒ”๋กœ์šฐ</button>
                  {% endif %}
                </form>
              </div>
            {% endif %}
          </div>
        {% endwith %}
      {% endblock %}
      
      {% block script %}
      <script>
        const followForm = document.querySelector('#follow-form')
        const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]')
      
        if (followForm) {
          followForm.addEventListener('submit', event => {
            if (csrf_token) {
              event.preventDefault()
              const userId = event.target.dataset.userId
              axios({
                method: 'post',
                url: `http://127.0.0.1:8000/accounts/${userId}/follow/`,
                headers : {'X-CSRFToken': csrftoken.value},
              })
              .then(response => {
                const input = document.querySelector('#followBtn')
                if (response.data.followed) {
                  input.innerText = '์–ธํŒ”๋กœ์šฐ'
                } else {
                  input.innerText = 'ํŒ”๋กœ์šฐ'
                }
                
                const followCount = document.querySelector('#follow-count')
                followCount.innerText = `ํŒ”๋กœ์ž‰ : ${response.data.followingCount} / ํŒ”๋กœ์›Œ : ${response.data.followerCount}`
              })
            }
          })
        }
      </script>
      {% endblock script %}

  • ๋ฆฌ๋ทฐ ์ข‹์•„์š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

    • community/views.py

      # like ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ๋งŒ ์ž‘์„ฑ.
      
      @require_POST
      def like(request, review_pk):
          if request.user.is_authenticated:
              review = get_object_or_404(Review, pk=review_pk)
              user = request.user
              if review.like_users.filter(pk=user.pk).exists():
                  review.like_users.remove(user)
                  liked = False
              else:
                  review.like_users.add(user)
                  liked = True
              context = {
                  'liked': liked,
                  'count': review.like_users.count(),
              }
              return JsonResponse(context)
          return redirect('accounts:login')
    • community/index.html

      {% extends 'base.html' %}
      
      {% block content %}
        <h1>Community</h1>
        <hr>
        {% for review in reviews %}
          <p>์ž‘์„ฑ์ž : <a href="{% url 'accounts:profile' review.user.username %}">{{ review.user }}</a></p>
          <p>๊ธ€ ๋ฒˆํ˜ธ: {{ review.pk }}</p>
          <p>๊ธ€ ์ œ๋ชฉ: {{ review.title }}</p>
          <p>๊ธ€ ๋‚ด์šฉ: {{ review.content }}</p>
          <form action="{% url 'community:like' review.pk %}" method="POST" class="d-inline like-form" data-review-id="{{ review.pk }}">
            {% csrf_token %}
            {% if user in review.like_users.all %}
              <button id="like-{{ review.pk }}">์ข‹์•„์š” ์ทจ์†Œ</button>
            {% else %}
              <button id="like-{{ review.pk }}">์ข‹์•„์š”</button>
            {% endif %}
          </form>
          <p>
            <span id="like-count-{{ review.pk }}">
              {{ review.like_users.all|length }}
            </span>
            ๋ช…์ด ์ด ๊ธ€์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค.
          </p>
          <a href="{% url 'community:detail' review.pk %}">[detail]</a>
          <hr>
        {% endfor %}
      {% endblock %}
      
      
      {% block script %}
      <script>
        const likeForms = document.querySelector('.like-form')
        const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]')
        likeForms.forEach(form => {
          forms.addEventListener('submit', event => {
            event.preventDefault()
            const reviewId = event.target.dataset.reviewId
            axios({
              method: 'post',
              url: `http://127.0.0.1:8000/community/${reviewId}/like/`,
              headers : {'X-CSRFToken': csrftoken.value},
            })
            .then(response => {
              const likeCount = document.querySelector(`#like-count-${reviewId}`)
              likeCount.innerText = response.data.count
              const button = document.querySelector(`#like-${reviewId}`)
              if (response.data.liked) {
                button.innerText = '์ข‹์•„์š” ์ทจ์†Œ'
              } else {
                button.innerText = '์ข‹์•„์š”'
              }
            })
          })
        })
      </script>
      {% endblock script %}

  • ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด, ์ „์ฒด ์˜ํ™” ๋ชฉ๋ก ์กฐํšŒ ํŽ˜์ด์ง€์—์„œ ์˜ํ™” ๋ชฉ๋ก์„ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

    • movies/views.py

      # ๋ชฉ์ ์— ๋งž๋Š” ์ฝ”๋“œ๋งŒ ์ž‘์„ฑ.
      
      @require_safe
      def index(request):
          movies = Movie.objects.order_by('-popularity')
          paginator = Paginator(movies, 10)
          page_number = request.GET.get('page')
          # queryset
          page_obj = paginator.get_page(page_number)
          
          context = {
              'movies': page_obj,
          }
          return render(request, 'movies/index.html', context)
    • movies/index.html

      {% extends 'base.html' %}
      {% load bootstrap5 %}
      {% block content %}
        <h1>Movies</h1>
      
        <div id="movieList">
          {% for movie in movies %}
          <div class="movie">
            <h2>{{ movie.title }}</h2>
            <p>{{ movie.overview }}</p>
            <a href="{% url 'movies:detail' movie.pk %}">[DETAIL]</a>
            <hr>
          </div>
          {% endfor %}
      
          <!-- ์—ฌ๊ธฐ์— JSON ๋‚ด์šฉ์„ ๋‹ด์€ Element append -->
        </div>
      
      {% comment %} 
        <div class="d-flex justify-content-center">
          {% bootstrap_pagination movies %}
        </div> {% endcomment %}
      {% endblock %}
      
      {% block script %}
      <script>
        let page = 2
        const movieList = document.querySelector('#movieList')
      
        document.addEventListener('scroll', function (event) {
          const { scrollTop, clientHeight, scrollHeight } = document.documentElement
          // ์™„์ „ ๋ฐ”๋‹ฅ์— ๋„๋‹ฌ
          // if (scrollHeight - scrollTop === clientHeight) {
      
          // 1. ์Šคํฌ๋กค์ด ๋ฐ”๋‹ฅ(์–ธ์ €๋ฆฌ)์— ๋„๋‹ฌ ํ–ˆ์„๋•Œ, => document / event 
          if (scrollTop + clientHeight >= scrollHeight - 5) {
            // 2. ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ 10๊ฐœ๋ฅผ ๋ถˆ๋Ÿฌ์˜ด(AJAX) => axios / DRF
            axios({
              method: 'get',
              url: `/movies/ajax/?page=${page}`
            })
              .then(res => {
                  const movies = res.data
                  // 3. ์‘๋‹ต JSON ๋ฐ์ดํ„ฐ 10๊ฐœ๋ฅผ ํ™”๋ฉด์— ๋ถ™์ž„
                  movies.forEach(movie => {
                    const movieDiv = document.createElement('div')
      
                    const h2 = document.createElement('h2')
                    h2.innerText = movie.title
      
                    const p = document.createElement('p')
                    p.innerText = movie.overview
      
                    const a = document.createElement('a')
                    a.innerText = '[DETAIL]'
                    a.href = `/movies/${movie.id}/`
      
                    const hr = document.createElement('hr')
      
                    movieDiv.append(h2, p, a, hr)
                    movieList.appendChild(movieDiv)
                  })
                  page++
                })
                .catch(err => console.error(err))
          }
        })
      </script>
      {% endblock script %}

  • ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด, ๋‹จ์ผ ์˜ํ™” ์ƒ์„ธ ์กฐํšŒ ํŽ˜์ด์ง€์—์„œ ์˜ํ™” ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

    • movies/views.py

      # ๋ชฉ์ ์— ๋งž๋Š” ์ฝ”๋“œ๋งŒ ์ž‘์„ฑ.
      
      @require_safe
      def detail(request, movie_pk):
          movie = get_object_or_404(Movie, pk=movie_pk)
          context = {
              'movie': movie,
          }
          return render(request, 'movies/detail.html', context)
    • movies/detail.html

      {% extends 'base.html' %}
      
      {% block content %}
        <h2 class="text-center">DETAIL</h2>
        <h3>{{ movie.pk }} ๋ฒˆ์งธ ๊ธ€</h3>
        <hr>
        <p>title: {{ movie.title }}</p>
        <p>release_date: {{ movie.release_date }}</p>
        <p>popularity: {{ movie.popularity }}</p>
        <p>vote_count: {{ movie.vote_count }}</p>
        <p>vote_average: {{ movie.vote_average }}</p>
        <p>overview: {{ movie.overview }}</p>
        <p>
          <img src="{{ movie.poster_path }}" alt="">
        </p>
        <hr>
        <a href="{% url 'movies:index' %}">[back]</a>
      {% endblock  %}

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋˜์–ด ์žˆ๋‹ค๋ฉด, 10๊ฐœ์˜ ์˜ํ™”๋ฅผ ์ถ”์ฒœํ•˜์—ฌ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

    • movies/views.py

      @require_safe
      def recommended(request):
          if request.user.is_authenticated:
              movies = Movie.objects.order_by('-vote_average')[:10]
              context = {
                  'movies': movies,
              }
              return render(request, 'movies/recommended.html', context)
          return redirect('accounts:login')
    • movies/recommended.html

      {% extends 'base.html' %}
      
      {% block content %}
        <h2 class="text-center">์˜ํ™” ์ถ”์ฒœ</h2>
        {% for movie in movies  %}
          <a href="{% url 'movies:detail' movie.pk %}">{{ movie.title }}</a>
          <hr>
        {% endfor %}
      {% endblock %}


project_09's People

Contributors

dozinq avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.