[복습/0529] Django project

nikevapormax·2022년 5월 29일
0

WIL

목록 보기
8/8
post-thumbnail

🤦‍♂️ django의 user model 사용

🤷‍♂️ 내가 만든 user model과 비교

  • 데이터베이스 창을 열어보면 굉장히 많은 db 파일들이 존재한다. 내가 이번에 만든 my_usertweet을 제외한 파일들은 django에서 기본적으로 제공하는 db 파일들이다.
  • django에서 기본적으로 제공하는 user model은 auth_user다.
  • 따라서 나는 my_userauth_user를 비교해보도록 하겠다.
  • 우리가 auth_user를 사용하게 된다면, django에서 기본적으로 제공하는 기능들을 사용할 수 있기 때문에 로그인 및 로그아웃이 굉장히 쉬워진다.

🤷‍♂️ 나의 user model 업그레이드

- 클래스의 상속

  • class B에서 class A의 기능들을 사용하고 싶다면 다음과 같이 괄호 안에 A를 넣어주면 된다.
class B(A):
	def 기능_B_1:
		return '기능_B_1'
  • 우리가 user/models.py에서 선언했던 UserModel 또한 위와 같은 구조로 이루어져 있다.
    • class UserModel(models.Model)과 같이 작성함으로 인해 UserModel에서 우리는 임포트 받아온 models의 Model 클래스의 기능을 사용할 수 있는 것이다.
#### user/models.py ####

from django.db import models

# 내가 생성한 user model
class UserModel(models.Model):
    # 데이터베이스에 반영되는 model의 이름 작성
    class Meta:
        db_table = "my_user"

    # 사용자 이름
    username = models.CharField(max_length=20, null=False)
    # 비밀번호
    password = models.CharField(max_length=256, null=False)
    # 상태 메세지
    bio = models.CharField(max_length=256, default='')
    # 생성일
    created_at = models.DateTimeField(auto_now_add=True)
    # 수정일
    updated_at = models.DateTimeField(auto_now=True)

- 업그레이드

  • 우리는 기본적으로 django에서 제공하는 auth_user를 사용할 것이며, 그 테이블에 없는 내용인 bio를 추가해 my_user를 사용할 것이다.
#### user/models.py ####

from django.db import models
# django에서 기본적으로 제공하는 auth_user와 연동되는 클래스인 AbstractUser 사용
from django.contrib.auth.models import AbstractUser

# 내가 생성한 user model
class UserModel(AbstractUser):
    # 데이터베이스에 반영되는 model의 이름 작성
    class Meta:
        db_table = "my_user"

    # auth_user에 있는 내용들을 다 지워줌
    # 상태 메세지
    bio = models.CharField(max_length=256, default='')
  • 이렇게 모델을 수정하고 나서 djangoProject/settings.py로 들어가 내용을 수정해주어야 한다. (해당 파일 맨 밑으로 내려가 작성)
#### djangoProject/settings.py ####

# django 너의 기본 모델도 좋지만, 나는 그걸 튜닝해서 UserModel로 합쳤어. 너가 알아서 일 좀 해줘
AUTH_USER_MODEL = 'user.UserModel'

🤷‍♂️ user model을 db에 적용시키기

  • 우리가 맨처음 usertweet 모델을 각각의 models.py에 입력하고 난 후 한 작업이 있다.
  • 터미널에 아래의 코드들을 입력하여 그 작업을 진행하고, django에게 우리의 db가 바뀐 것을 알려주도록 하겠다.
$ python manage.py makemigrations
# python manage.py migrate

  • 위의 사진을 통해 확인할 수 있듯이 UserModel의 값들 중 우리가 삭제한 값이 삭제되거나 변경되고, auth_user에만 있던 값들이 우리의 모델에 추가되는 것을 볼 수 있다.
  • 데이터베이스 창에서도 변경이 잘 되었는지 확인해보도록 하겠다.

🤷‍♂️ 회원가입 수정하기

  • django의 get_user_model을 사용해 회원가입을 수정해보도록 하겠다.
    • get_user_model : 사용자가 데이터베이스 안에 있는지 검사하는 함수
  • 회원가입을 할 때, django 서버로 POST 요청이 들어오게 된다. 따라서 우리는 POST 요청을 받는 부분을 수정할 것이다.
#### user/views.py ####

# render를 통해서 html을 화면에 띄워줌
from django.shortcuts import render, redirect
# .models: 내가 위치하고 있는 app에서
# UserModel : UserModel을 가져와 사용하겠다.
from .models import UserModel
# 화면에 글자를 띄울 때 사용!
from django.http import HttpResponse
# get_user_model : 사용자가 데이터베이스 안에 있는지 검사하는 함수
from django.contrib.auth import get_user_model

# 회원가입
def sign_up_view(request):
    # 우리의 장고 서버에게 들어오는 요청에 따른 행동을 한다.
    # GET 메서드면 화면을 보여주고
    if request.method == 'GET':
        return render(request, 'user/signup.html')
    # POST 메서드면 아래의 작업들을 진행하겠다.
    elif request.method == 'POST':
        # request.POST로 건너오는 정보 중 name='username'인 것들을 사용하겠다.
        # username이 없다면 None(빈칸)으로 처리하겠다.
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)
        password2 = request.POST.get('password2', None)
        bio = request.POST.get('bio', None)

        # 만약 이 둘이 같지 않다면
        if password != password2:
            # 다시 회원가입 화면을 보여준다.
            return render(request, 'user/signup.html')
        else:
            # 회원가입을 시도한 사용자의 아이디가 중복되지 않는지 확인해야 됨!!!
            not_new_user = get_user_model().objects.filter(username=username)

            if not_new_user:
                return render(request, 'user/signup.html')
            else:
                # create_user 함수를 사용해 이전의 5줄짜리 코드를 한 줄로 축소시킴
                UserModel.objects.create_user(username=username, password=password, bio=bio)

        # 회원가입이 마무리되었으니 redirect를 써서 로그인 페이지로 보내주자!!
        return redirect('/sign-in')
        
        
 # 하단 생략       
  • 위의 코드를 입력한 후, 회원가입 테스트를 진행하고 데이터베이스를 한 번 확인해 보았다.
    • 방금 테스트를 진행했던 wjdeorms2의 비밀번호가 자동으로 암호화된 것을 볼 수 있다.

🤷‍♂️ 로그인 수정하기

  • 현재 모델은 로그인에 성공하게 되더라도 페이지 전환이 되지 않는다. 그 이유는 우리가 auth_user를 우리의 UserModel에 사용하게 되면서 비밀번호의 암호화가 진행되었기 때문이다.
  • 바로 위의 사진에서 보이듯 3번 row의 password가 암호화된 것을 볼 수 있다. 즉, db에서 사용자가 입력한 비밀번호에 대조하는 것이 저 암호화된 password이고, 사용자가 입력한 값은 암호화하기 전 비밀번호이기 때문에 항상 오류가 날 수 밖에 없다.
#### user/views.py ####

# 인증 기능
from django.contrib import auth

# 상단 생략

# 로그인
def sign_in_view(request):
    if request.method == 'POST':
        # 사용자가 화면에서 입력한 값들을 가져와서 각 변수에 저장
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)

        # authenticate : 사용자가 입력한 비밀번호와 암호화된 비밀번호가 일치하는지, 사용자와 관련있는 비밀번혼지도 검사
        me = auth.authenticate(request, username=username, password=password)

        # 이제는 me 값에 모든 정보가 담겨있기 때문에 이 값이 있는지 없는지만 판단하면 됨
        if me is not None:
            # 사용자를 로그인 시켜주면 된다.
            auth.login(request, me)
            return HttpResponse(f'반갑습니다, {me.username} 님!')
        else:
            # 로그인에 실패하면 다시 로그인 시도를 할 수 있게 로그인 페이지로 보내줌
            return redirect('/sign-in')
    elif request.method == 'GET':
        return render(request, 'user/signin.html')

🤦‍♂️ 로그인 이후 기능 다듬기

  • 우리는 로그인이 되고 나서 하는 모든 행위들을 tweet app과 관련된 페이지에서 진행할 것이다. 따라서 우리는 url을 연결하거나 어떤 행위들을 정의하는 것들 전부를 tweet app 안에서 해결할 것이다.

🤷‍♂️ 로그인 성공 시 페이지 생성

- templates/tweet/home.html 페이지 생성

  • 우리가 로그인에 성공하더라도 지금까지는 사용자의 이름과 간단한 메세지만 빈 화면에서 보여주었기 때문에 그 부분을 수정하였다.
  • 위에서 만든 회원가입과 로그인에 관한 페이지처럼 template 문법을 사용해 작성해 우리가 base.html의 내용을 다른 html 파일에 옮겨놓지 않더라도 base.html의 내용 아래에 다른 html 파일들이 붙일 수 있다.

- tweet/views.py 작성

  • 우선, home 함수를 만들어 사용자 인증이 완료된다면 url 연결을 통해 화면을 이동시켜 준다.
  • tweet 함수를 통해 html로 연결시켜준다.
from django.shortcuts import render, redirect


def home(request):
    # user가 로그인되어 있는지 한 번에 확인 가능!
    user = request.user.is_authenticated

    if user:
        return redirect('/tweet')
    else:
        return redirect('/sign-in')


def tweet(request):
    # 화면을 보여주는 것이니까 GET 메서드
    if request.method == 'GET':
        return render(request, 'tweet/home.html')

- tweet/urls.py

# tweet/urls.py
from django.urls import path
from . import views

urlpatterns = [
	# 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
    path('', views.home, name='home'), 
    # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
    path('tweet/', views.tweet, name='tweet'), 
]

- djangoProject/urls.py

  • 위와 같이 세팅을 다 마무리해도 막상 로그인을 해보면 페이지가 적용되지 않는 것을 알 수 있다.
  • 맨 처음 user app을 적용할 때를 생각해보자. 우리는 그때 user app 안에 생성했던 user/urls.pydjangoProject/urls.pyinclude 함수를 사용해 추가해 주었다. 이번에도 같은 작업을 진행하도록 하겠다.
from django.contrib import admin
from django.urls import path, include

from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # user/urls.py와 djangoProject/urls.py를 연결해 줌. 이제 django가 url을 알아들을 수 있음.
    path('', include('user.urls')),
    path('', include('tweet.urls')),
]

- user/views.py

  • 추가적으로 나의 경우 실수로 user/views.py를 수정하지 않아서 계속 사용자의 이름만 로그인을 하면 나왔다. 아래 부분을 수정해야 한다.

# 상단 생략

 		# 이제는 me 값에 모든 정보가 담겨있기 때문에 이 값이 있는지 없는지만 판단하면 됨
        if me is not None:
            # 사용자를 로그인 시켜주면 된다.
            auth.login(request, me)
            # 여기로 들어오면 기본 url을 가진 곳으로 가게 된다.
            # 그곳이 바로 tweet/urls.py에 있는 views.home을 실행시키는 부분이다.
            return redirect('/')
        else:
            # 로그인에 실패하면 다시 로그인 시도를 할 수 있게 로그인 페이지로 보내줌
            return redirect('/sign-in')
            
# 하단 생략

🤷‍♂️ 사용자 정보 보여주기

  • 로그인에 성공하게 되면 아래와 같은 화면이 나오게 된다. 하지만 빨간 네모 박스에서와 같이 로그인에 성공하게 되어도 사용자의 정보가 나오지 않고 있다.
  • 따라서 해당 부분을 수정해보도록 하겠다.

- templates/tweet/home.html

  • 사용자의 정보를 띄워줄 공간을 확보하기 위해 원래 있던 값들을 조금 수정하도록 하겠다.
 <!-- 왼쪽 컬럼 -->
 <div class="col-md-3">
     <div class="card">
         <div class="card-body">
             <h5 class="card-title">{{ user.username }}</h5>
             <p class="card-text">{{ user.bio }}</p>
          </div>
     </div>
 </div>

- templates/base.html

  • 위의 사진에서도 볼 수 있듯이 우리가 이미 로그인을 한 상태임에도 불구하고 현 페이지에서는 우상단에서 Sign InSign Up 버튼을 볼 수 있다.
  • base.html을 수정해 해당 현상을 고쳐보도록 하겠다.
  • template 문법을 사용해 조건문을 형성해 주었고, 사용자 인증이 되지 않은 경우에만 우상단에 Sign InSign Up 버튼이 보이도록 조치하였다.
<form class="form-inline my-2 my-lg-0">
    {% if not user.is_authenticated %}
    <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
            <a class="nav-link" href="/sign-in"> Sign In <span class="sr-only"></span></a>
        </li>
        <li class="nav-item active">
            <a class="nav-link" href="/sign-up"> Sign Up <span class="sr-only"></span></a>
        </li>
    </ul>
    {% else %}
        {{ user.username }}  반갑습니다!
    {% endif %}
</form>

🤷‍♂️ 로그인 오류

  • 이번 오류를 수정하는 부분들은 전부 url을 통짜로 입력해 이동하는 것이므로 둘 다 GET 요청을 사용하는 부분들이다.
  • 로그인을 하지 않아도 시크릿 모드를 통해 url을 작성해 http://127.0.0.1:8000/tweet/에 들어가게 되면 로그인을 해야만 들어갈 수 있던 페이지에 들어갈 수 있게 된다.
  • 해당 문제를 해결하기 위해 다음 페이지를 수정하도록 하겠다.

- tweet/views.py

  • 무작정 이 페이지로 들어온 것이 아니라, 우리는 url을 추적해서 들어오게 되었다. tweet/ 이 뚫렸기 때문에 tweet/urls.py로 들어가 보았다.
  • 해당 url은 tweet/views.pytweet 함수를 실행하게 해준다. 따라서 tweet/views.py로 들어왔고, 해당 함수를 수정하도록 하겠다.
def tweet(request):
    # 화면을 보여주는 것이니까 GET 메서드
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            return render(request, 'tweet/home.html')
        else:
            return redirect('/sign-in')
  • 두 번째로 우리는 로그인을 이미 한 사용자이더라도 url에 /sign-up이랑 /sign-in을 입력하면 회원가입 및 로그인 페이지로 이동하는 것을 알 수 있다. 이 부분에 대한 수정도 진행하도록 하겠다.
  • 아래 사진을 보면 이미 로그인을 했기 때문에 우상단에 아이디가 나오는데 로그인을 다시 할 수 있는 것을 볼 수 있다.

- user/views.py

  • 위에서와 마찬가지로 사용자 인증이 완료된 경우에는 기본 url로 리다이렉트 시켜주어 사용자가 다른 페이지로 셀 수 없도록 하였다.
# 회원가입
def sign_up_view(request):
    # 회원가입 페이지가 로그인했을 때는 보이지 않도록 조치
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            return redirect('/')
        else:
            return render(request, 'user/signup.html')
            
 # 로그인
def sign_in_view(request):
    if request.method == 'POST':
        # 사용자가 화면에서 입력한 값들을 가져와서 각 변수에 저장
        username = request.POST.get('username', None)
        password = request.POST.get('password', None)

        # authenticate : 사용자가 입력한 비밀번호와 암호화된 비밀번호가 일치하는지, 사용자와 관련있는 비밀번혼지도 검사
        me = auth.authenticate(request, username=username, password=password)

        # 이제는 me 값에 모든 정보가 담겨있기 때문에 이 값이 있는지 없는지만 판단하면 됨
        if me is not None:
            # 사용자를 로그인 시켜주면 된다.
            auth.login(request, me)
            # 여기로 들어오면 기본 url을 가진 곳으로 가게 된다.
            # 그곳이 바로 tweet/urls.py에 있는 views.home을 실행시키는 부분이다.
            return redirect('/')
        else:
            # 로그인에 실패하면 다시 로그인 시도를 할 수 있게 로그인 페이지로 보내줌
            return redirect('/sign-in')
    elif request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            return redirect('/')
        else:
            return render(request, 'user/signin.html')
  • 여기서 /을 사용한 이유는 tweet/views.py에 있는 home함수가 /를 가지고 있기 때문이다. (`tweet/urls.py에서 기본 url 확인해봐)
  • 아래의 home 함수를 보면 알 수 있듯이 사용자 인증이 완료되어 있는 경우에는 /tweet으로 연결시켜 주기 때문에 기본 url을 적용해주었다.
#### tweet/views.py ####

def home(request):
    # user가 로그인되어 있는지 한 번에 확인 가능!
    user = request.user.is_authenticated

    if user:
        return redirect('/tweet')
    else:
        return redirect('/sign-in')
        
 # 하단 생략

🤷‍♂️ 로그아웃 기능

- user/views.py

  • django를 사용하기 때문에 세션이고 뭐고 다 때려치우고 이거 한줄이면 가능하다.
  • 로그인을 한 사용자만이 접근할 수 있도록 조치를 하였다.
#### user/views.py ####

from django.contrib.auth.decorators import login_required

# 로그아웃
# 사용자가 로그인이 꼭 되어 있어야만 접근 가능한 함수라는 것을 명시해줌
@login_required
def logout(request):
    auth.logout(request)
    return redirect('/')

- user/urls.py

  • 위에서 생성한 함수에 접근할 수 있도록 url을 생성해보도록 하겠다.
  • 그리고 이제 해당 url에 접근할 수 있는 트리거를 만들면 될 것 같다.
from django.urls import path
from . import views

urlpatterns = [
    path('sign-up/', views.sign_up_view, name='sign-up'),
    path('sign-in/', views.sign_in_view, name='sign-in'),
    path('logout/', views.logout, name='logout'),
]

- templates/base.html

  • 로그아웃 버튼을 상단 네비게이션 바에 만들고 싶기 때문에 우리는 base.html로 이동해 작업을 진행하도록 하겠다.
  • 아래와 같이 구조를 변경하여 이름 옆에 로그아웃 링크가 위치하게 하였고, 색을 빨간색으로 변경해 사용자가 잘 인식할 수 있도록 하였다.
<!-- 상단 생략 -->

 {% else %}
 <ul class="navbar-nav mr-auto">
     <!-- disabled를 통해 선택되지 않도록 조치 -->
     <li class="nav-item disabled">
         <span class="nav-link">
              {{ user.username }} 님 반갑습니다!!
         </span>
     </li>
     <li class="nav-item active">
         <a style="color: red;" class="nav-link" href="/logout"> LOGOUT </a>
     </li>
 </ul>
 {% endif %}

<!-- 하단 생략 -->


🤦‍♂️ 게시글 관리

🤷‍♂️ 게시글 쓰기

  • 우선 어떤 html 파일에서 우리가 게시글을 쓸 수 있는지 파악해보자.
  • 위의 사진을 보니 우리는 templates/tweet/home.html에서 게시글을 쓸 수 있다. 그러므로 해당 파일로 가서 작업을 진행하도록 하겠다.

- templates/tweet/home.html

  • <form> 태그를 사용하므로 action과 method를 설정해주었다.
<div class="media-body">
    <h5 class="mt-0">나의 이야기를 적어주세요</h5>
    <p>
       <form action="/tweet/" method="post">
           <!-- 암호화 정보 전송 -->
           {% csrf_token %}
           <div class="form-group mb-2">
               <textarea class="form-control" style="resize: none" name='my-content' id="my-content"></textarea>
           </div>
           <button type="submit" class="btn btn-primary" style="float:right;">작성하기</button>
       </form>
    </p>
</div>

- tweet/views.py

  • 우리가 위의 action에서 정의했던 것과 같이 현재 사용하려는 url은 tweet/이다. 그리고 이것을 tweet/urls.py 파일을 통해 확인해보면 이미 해당 url을 사용하고 있는 것을 알 수 있다.
  • 그러므로 프론트에서 POST 방식으로 보내는 정보를 받아주고, 글을 쓰기 위해 필요한 동작을 정의해주는 함수를 views.py에 있는 내용을 통해 수정해보도록 하겠다.
  • 그 전에 어떤 정보가 넘어오는지 확인하도록 하겠다.
<textarea class="form-control" style="resize: none" name='my-content' id="my-content"></textarea>
  • 우리는 textarea에 입력된 것을 가져오고, 해당 정보의 이름은 my-content이다.
  • 화면에서 입력한 정보를 POST 형식을 통해 가져오고, 이것을 db에 저장하는 코드를 작성하였다.
#### tweet/views.py ####

# 상단 생략

def tweet(request):
    # 화면을 보여주는 것이니까 GET 메서드
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            return render(request, 'tweet/home.html')
        else:
            return redirect('/sign-in')
    elif request.method == 'POST':
        # 지금 로그인하고 있는 사용자의 정보 전체를 들고 온다.
        user = request.user
        my_tweet = TweetModel()
        my_tweet.author = user
        my_tweet.content = request.POST.get('my-content', '')
        my_tweet.save()
        return redirect('/tweet')
  • 이렇게 하고 화면에 게시글을 입력해보면 화면에는 표시가 되지 않는 것을 알 수 있다. 반면 tweet db에는 저장이 잘 된것을 알 수 있다. 따라서 우리는 화면에 GET 요청으로 데이터를 보내주는 것에 대한 수정을 진행해야 한다.
  • GET 요청을 통해 모든 트윗의 내용을 화면으로 넘겨주었다.
#### tweet/views.py ####

# 상단 생략

def tweet(request):
    # 화면을 보여주는 것이니까 GET 메서드
    if request.method == 'GET':
        user = request.user.is_authenticated
        if user:
            # 모든 트윗의 내용을 불러오는데 최신순으로 불러오기 위해서 시간의 역순을 해준다.
            all_tweet = TweetModel.objects.all().order_by('-created_at')
            # {'tweet': all_tweet}을 사용해서 화면에 우리의 트윗 데이터를 다 넘겨줬다.
            return render(request, 'tweet/home.html', {'tweet': all_tweet})
        else:
            return redirect('/sign-in')
    elif request.method == 'POST':
        # 지금 로그인하고 있는 사용자의 정보 전체를 들고 온다.
        user = request.user
        my_tweet = TweetModel()
        my_tweet.author = user
        my_tweet.content = request.POST.get('my-content', '')
        my_tweet.save()
        return redirect('/tweet')
  • 이제 화면단으로 넘어가 작성된 글이 나오는 곳을 수정해보도록 하겠다.
<!-- templates/tweet/home.html -->

<!-- 상단 생략 -->
<div class="row">
    <!-- 여기서 tweet은 우리가 백앤드에서 넘겨준 그 키값 tweet -->
    {% for tw in tweet %}
    <div class="col-md-12 mb-2">
         <div class="card">
              <div class="card-body">
                   <div class="media">
                        <div class="media-body">
                             <h5 class="mt-0">{{ tw.content }}</h5>
                        </div>
                        <div style="text-align: right">
                             <!-- author는 UserModel에서 정보를 가져온 외래키이고, author를 선언할 때 모든 정보를 다 가져왔음 -->
                             <!-- 그래서 author 중에서 username만 출력해야 하기 때문에 author.username으로 씀 -->
                             <!-- created_at만 불러오면 '몇월 몇일 몇시 몇분'이라고 값이 나온다. 그래서 timesince를 사용 -->
                             <span style="font-size: small">{{ tw.author.username }}-{{ tw.created_at|timesince }} 전</span>
                        </div>
                   </div>
              </div>
         </div>
    </div>
    {% endfor %}
</div>

<!-- 하단 생략 -->

🤷‍♂️ 게시글 삭제

  • 게시글이 가지고 있는 각자의 고유 id 값을 삭제하는 방법을 사용할 것이다.
  • 우리는 글을 관리하는 tweet/views.py에 들어가서 delete 함수를 생성할 것이다. 고고
#### tweet/views.py ####

from django.contrib.auth.decorators import login_required

# 상단 생략

# 로그인을 한 사용자만이 글을 삭제할 수 있도록 조치
@login_required
def delete_tweet(request, id):
    my_tweet = TweetModel.objects.get(id=id)
    my_tweet.delete()
    return redirect('/tweet')
  • 함수를 생성하였으니, 이제 이 함수를 사용할 수 있도록 tweet/urls.py에 가서 url을 설정하도록 하겠다.
#### tweet/urls.py ####

from django.urls import path
from . import views

urlpatterns = [
    # 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
    path('', views.home, name='home'),
    # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
    path('tweet/', views.tweet, name='tweet'),
    path('tweet/delete/<int:id>', views.delete_tweet, name='delete-tweet'),
]
  • 이제 화면으로 가서 해당 url을 사용할 수 있는 트리거를 만들어 보도록 하겠다.
<!-- templates/tweet/home.py -->

<!-- 상단 생략 -->
<!-- 작성 된 글이 나오는 곳 -->
<div class="row">
    <!-- 여기서 tweet은 우리가 백앤드에서 넘겨준 그 키값 tweet -->
    {% for tw in tweet %}
        <div class="col-md-12 mb-2">
             <div class="card">
                  <div class="card-body">
                       <!-- user는 로그인이 되어 있는 사용자를 의미. -->
                       <!-- 즉, 로그인이 된 사용자의 정보와 외래키 author가 들고있는 사용자의 정보가 같아야지만 삭제를 할 수 있음 -->
                       {% if tw.author == user %}
                             <div style="text-align: right">
                                  <a href="/tweet/delete/{{ tw.id }}">
                                      <span class="badge rounded-pill bg-danger">삭제</span>
                                  </a>
                             </div>
                       {% endif %}
                       <div style="text-align: right">
                            <a href="#">
                               <span class="badge rounded-pill bg-success">보기</span>
                            </a>
                       </div>
                       <div class="media">
                            <div class="media-body">
                                 <h5 class="mt-0">{{ tw.content }}</h5>
                            </div>
                            <div style="text-align: right">
                                 <!-- author는 UserModel에서 정보를 가져온 외래키이고, author를 선언할 때 모든 정보를 다 가져왔음 -->
                                 <!-- 그래서 author 중에서 username만 출력해야 하기 때문에 author.username으로 씀 -->
                                 <!-- created_at만 불러오면 '몇월 몇일 몇시 몇분'이라고 값이 나온다. 그래서 timesince를 사용 -->
                                 <span style="font-size: small">{{ tw.author.username }}-{{ tw.created_at|timesince }} 전</span>
                            </div>
                        </div>
                   </div>
              </div>
         </div>
    {% endfor %}
</div>
  • 위와 같이 코드를 작성하게 되면 아래와 같은 화면이 생성된다. 아래의 화면은 로그인한 사용자가 본인이 작성한 게시글을 볼 때의 화면이다.

🤦‍♂️ 3주차 숙제

🤷‍♂️ tweet_detail.html 적용

  • templates/tweet/tweet_detail.html 파일 생성 후, 제공받은 코드를 입력해, 사용자가 보기 버튼을 누르게 되면 나오는 페이지를 생성했다.

🤷‍♂️ comment model 생성

  • 현재 우리는 게시글의 보기 버튼을 눌러 해당 게시글에 대한 댓글을 작성하고 볼 수 있게 하는 것이 목표다. 따라서 나는 모델을 먼저 생성해보도록 하겠다.
#### tweet/models.py ####

# 상단 생략

# 게시글의 댓글 모델
class TweetComment(models.Model):
    class Meta:
        db_table = "comment"

    # 어떤 게시글?
    tweet = models.ForeignKey(TweetModel, on_delete=models.CASCADE)
    # 댓글 작성자
    author = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    # 댓글 내용
    comment = models.CharField(max_length=256)
    # 몇 분전에 단거야?
    created_at = models.DateTimeField(auto_now_add=True)
    # 몇 분전에 수정한거야?
    updated_at = models.DateTimeField(auto_now=True)
  • 이제 모델을 사용하기 위해 모델을 등록해보도록 하겠다.
$ python manage.py makemigrations
$ python manage.py migrate

🤷‍♂️ url 연결

  • views.py를 수정하기 전에, 어떤 url이 필요할지 생각해보자.
    • 보기 버튼을 누르면 상세 페이지로 이동할 수 있는 url
    • 상세 페이지에서 댓글을 작성할 수 있는 url
    • 작성한 댓글을 지울 수 있는 url
#### tweet/urls.py ####

from django.urls import path
from . import views

urlpatterns = [
    # 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
    path('', views.home, name='home'),
    # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
    path('tweet/', views.tweet, name='tweet'),
    # 게시글 삭제
    path('tweet/delete/<int:id>', views.delete_tweet, name='delete-tweet'),
    # 상세 페이지로 이동
    path('tweet/<int:id>', views.tweet_detail, name='tweet-detail'),
    # 댓글 달기
    path('tweet/comment/<int:id>', views.write_comment, name='write-comment'),
    # 댓글 삭제
    path('tweet/comment/delete/<int:id>', views.delete_comment, name='delete-comment'),
]

🤷‍♂️ views.py 수정

  • 위에서 url을 만들고, 각 url에서 쓰일 함수 이름을 정의하였다. 이제 그 이름에 맞는 함수를 만들고 기능을 정의해보록 하겠다.
#### tweet/views.py ####

# 상단 생략

@login_required
def tweet_detail(request, id):
    my_tweet = TweetModel.objects.get(id=id)
    # 상세 페이지로 넘어가서 댓글이 뭐가 있는지 최신순으로 보여줘야 되겠지?
    comment = TweetComment.objects.filter(tweet_id=id).order_by('-created_at')
    return render(request, 'tweet/tweet_detail.html', {'tweet': my_tweet, 'comment': comment})


@login_required
def write_comment(request, id):
    if request.method == 'POST':
        comment = request.post.get("comment", '')
        current_tweet = TweetModel.objects.get(id=id)

        tweet_comment = TweetComment()
        tweet_comment.comment = comment
        tweet_comment.author = request.user
        tweet_comment.tweet = current_tweet # id 값으로 들어감
        tweet_comment.save()

        return redirect('/tweet/'+str(id))


@login_required
def delete_comment(request, id):
    comment = TweetComment.objects.get(id=id)
    current_tweet = comment.tweet.id
    comment.delete()

    return redirect('/tweet/'+str(current_tweet))

🤷‍♂️ home.html의 버튼 링크 수정

  • 마지막으로 상세 페이지에 들어가기 위해 링크를 설정해주어야 한다. 아직은 #으로 설정되어 있다.
<!-- templates/tweet/home.py -->

<!-- 상단 생략 -->

<!-- 작성 된 글이 나오는 곳 -->
<div class="row">
    <!-- 여기서 tweet은 우리가 백앤드에서 넘겨준 그 키값 tweet -->
    {% for tw in tweet %}
        <div class="col-md-12 mb-2">
             <div class="card">
                  <div class="card-body">
                       <!-- user는 로그인이 되어 있는 사용자를 의미. -->
                       <!-- 즉, 로그인이 된 사용자의 정보와 외래키 author가 들고있는 사용자의 정보가 같아야지만 삭제를 할 수 있음 -->
                       {% if tw.author == user %}
                             <div style="text-align: right">
                                  <a href="/tweet/delete/{{ tw.id }}">
                                      <span class="badge rounded-pill bg-danger">삭제</span>
                                  </a>
                             </div>
                       {% endif %}
                       <div style="text-align: right">
                            <a href="/tweet/{{ tw.id }}">
                               <span class="badge rounded-pill bg-success">보기</span>
                            </a>
                       </div>
                       <div class="media">
                            <div class="media-body">
                                 <h5 class="mt-0">{{ tw.content }}</h5>
                            </div>
                            <div style="text-align: right">
                                 <!-- author는 UserModel에서 정보를 가져온 외래키이고, author를 선언할 때 모든 정보를 다 가져왔음 -->
                                 <!-- 그래서 author 중에서 username만 출력해야 하기 때문에 author.username으로 씀 -->
                                 <!-- created_at만 불러오면 '몇월 몇일 몇시 몇분'이라고 값이 나온다. 그래서 timesince를 사용 -->
                                 <span style="font-size: small">{{ tw.author.username }}-{{ tw.created_at|timesince }} 전</span>
                            </div>
                        </div>
                   </div>
              </div>
         </div>
    {% endfor %}
</div>

profile
https://github.com/nikevapormax

0개의 댓글