auth_user이고, 내가 만든 사용자 모델은 my_user이다. 
auth_user의 경우, 내가 직접 만들었던 user model보다 더 많은 속성을 가지고 있다.my_user model을 만들어 사용자 관리에 들어가게 되면 session을 사용하는 등의 과정을 거쳐 보안을 적용해야 하지만, auth_user model을 사용하게 되면 그런 과정을 거치지 않아도 된다.
만약 class B에서 class C의 기능을 사용하고 싶다면, 추가적인 코드를 작성하지 않고 class B(C)라고 작성하여 class C의 기능을 상속받아 사용할 수 있다.
이미 이전 주차에서 작성했었던 user/models.py에서 class의 상속이 있었다.
class UserModel(models.Model)를 보면 UserModel에서 models.Model의 기능을 상속받아 사용하겠다는 뜻이다.
#user/models.py
from django.db import models
class UserModel(models.Model):
# DB 테이블의 이름을 지정하는 등 데이터베이스의 정보를 넣어주는 역할
class Meta:
db_table = "my_user"
auth_user와 연동되는 AbstractUser를 상속받아 사용하기 때문에 이전에 직접 작성하였던 것중 auth_user의 내용과 중복되는 것들을 삭제해 주었다. ##### user/models.py #####
from django.db import models
# 우리는 django에서 기본적으로 제공하는 user 모델을 사용하겠다.
# AbstractUser는 django에서 기본적으로 제공하는 user model
from django.contrib.auth.models import AbstractUser
class UserModel(AbstractUser):
# DB 테이블의 이름을 지정하는 등 데이터베이스의 정보를 넣어주는 역할
class Meta:
db_table = "my_user"
# 원래 이전에 작성했던 usename, password, created_at, updated_at은 이미 auth_user에 존재
# 그러므로 그것들은 다 지우고, 우리가 추가하고 싶은 bio만 작성해주면 됨!
# 상세 정보
bio = models.CharField(max_length=256, default='')
mySpartaSns/settings.py# mySpartaSns/settings.py
# django의 기본 모델에 다른 정보를 끼워넣어 그것을 우리의 User model로 사용하기로 했기 때문에
# 그것을 django에게 알려주어야 한다.
AUTH_USER_MODEL = 'user.UserModel'
$ python manage.py makemigrations
$ python manage.py migrate
my_user에서 auth_user의 내용을 상속받아 사용하면서 기존 내용을 삭제하는 변화가 일어났기 때문에 아래의 메세지들에서 알 수 있듯이 변화가 일어나고 그 변화를 적용해준 것을 볼 수 있다.

# 사용자가 데이터베이스 안에 있는지 검사하는 함수
from django.contrib.auth import get_user_model
이미 데이터베이스에 있는 user인지 판별하는 부분을 수정하도록 하겠다. # 위에서 import한 get_user_model을 사용해 이미 있는 user인지 판별해줌
not_new_user = get_user_model().objects.filter(username=username)
# 이미 정보를 입력한 user가 존재한다면?
# filter 기능을 통해서 같은 이름이 있는지 걸러준다.
# not_new_user = UserModel.objects.filter(username=username)
if not_new_user:
return render(request, 'user/signup.html')
else:
UserModel.objects.create_user(username=username, password=password, bio=bio)
# new_user = UserModel()
# new_user.username = username
# new_user.password = password
# new_user.password2 = password2
# new_user.bio = bio
# 위에서 받아온 정보들을 저장하는 과정을 거처야 함 꼭!
# new_user.save()
# 위와 같이 회원정보를 성공적으로 저장하게 되면 로그인 페이지로 리다이렉트해준다.
return redirect('/sign-in')

해싱이 되어 있지 않다. 그러므로 현재 코드를 그대로 사용하게 된다면 해싱된 비밀번호와 해싱되지 않은 비밀번호를 계속 대조하는 것이므로 절대로 로그인에 성공할 수 없을 것이다. authenticate 모듈을 활용해 사용자 인증을 해준다.# authenticate : 입력한 비밀번호와 해싱된 비밀번호가 일치하는지, 그것과 사용자가 일치하는지 한 번에 확인해줌
me= auth.authenticate(request, username=username, password=password)
# 앞에 있는 username은 UserModel 안에 있는 username이다. 뒤에 있는 것은 우리가 입력한 username이다.
# me = UserModel.objects.get(username=username)
if me.password == password:
request.session['user'] = me.username
⬇️
if me is not None:
auth.login(request, me)
from django.shortcuts import render, redirect
def home(request):
# user가 로그인되어 있는지 확인할 수 있음
user = request.user.is_authenticated
if user: # user가 로그인되어 있으면
return redirect('/tweet') # /tweet 으로 리다이렉트 해주고
else: # 로그인되어 있지 않다면
return redirect('/sign-in') # 로그인 페이지로 리다이렉트 해준다.
def tweet(request):
if request.method == 'GET':
return render(request, 'tweet/home.html')
# tweet/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'), # 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
path('tweet/', views.tweet, name='tweet') # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
]
http://127.0.0.1:8000/로 들어가게 되면 다음과 같은 이쁜 화면이 나오게 된다.
mySpartaSns에 포함되어 있는 내 django 프로젝트의 모든 세팅과 url의 정보가 담겨 있는 파일들을 수정하지 않음을 알게 되었다. 그러므로 수정을 시작해보겠다.mySpartaSns/urls.pypage not found가 되버린다.from django.contrib import admin
from django.urls import path, include
# 현재 내가 있는 파일에서 views.py를 불러옴
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.base_response, name='first_test'),
path('first/', views.first_view, name='first_view'),
# spartaSns와 user의 url이 연결되었다!!
path('', include('user.urls')),
path('', include('tweet.urls')),
]

if me is not None:
auth.login(request, me)
return redirect('/')
user의 정보가 하나도 보이지 않는다. Sign in과 Sign Up 버튼이 계속 보여지고 있다.
template 문법을 통해 개인 정보를 표시할 것이며, 해당 수정은 templates/tweet/home.html에서 이루어졌다. auth_model과 우리가 만든 user_model을 합친 모델에서 우리에게 필요한 정보인 username과 bio를 가져와 반영시켜주었다. <!-- base.html을 기본으로 가져가고, 남는 공간에 content를 붙여 넣겠다. -->
{% extends 'base.html' %}
{% block content %}
<div class="container timeline-container">
<div class="row">
<!-- 왼쪽 컬럼 -->
<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>
Sign in과 Sign Up 버튼 부분을 수정해주도록 하겠다. template 문법을 사용하며, 이번에는 조건문을 사용하기 위해{% .... %}를 사용하도록 하겠다. <form class="form-inline my-2 my-lg-0">
<!-- 유저가 인증이 되었니?(로그인이 완료된 사용자니?)에 대해 묻는 조건문 -->
<!-- sign in과 sign up 버튼이 보이려면 로그인이 되어 있지 않아야 한다. -->
<!-- 만약 사용자 인증이 되어 있지 않다면 -->
{% 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 }}님 반갑습니다~!
<!-- if문 종료 -->
{% endif %}
</form>

시크릿 모드를 통해 우리의 http://127.0.0.1:8000/tweet으로 들어가게 되면 아래의 화면이 나오게 된다. 로그인을 하지 않은 상태에서도 해당 페이지에 갈 수 있도록 길이 나있는 것 같다.
tweet/urls.py로 먼저 들어가 보았다. /tweet 이라는 url이 존재하고, 해당 url에 접속하게 되면 tweet/views.py에 있는 tweet 함수가 실행된다는 것을 알 수 있었다. urlpatterns = [
path('', views.home, name='home'), # 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
path('tweet/', views.tweet, name='tweet') # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
]tweet/views.py로 들어가 tweet 함수를 찾았고, 해당 내용을 수정하기로 하였다. def tweet(request):
if request.method == 'GET':
# 사용자 인증에 대한 정보를 user 변수에 대입
user = request.user.is_authenticated
# 만약 사용자 인증이 되어 있다면
if user:
# tweet/home.html 로 사용자가 갈 수 있도록 함
return render(request, 'tweet/home.html')
else:
# 사용자 인증이 되지 않았으므로 로그인 화면으로 보내줌
return redirect('/sign-in')
사용자 인증 기능을 추가했고, 아래의 사진처럼 로그인이 되지 않은 사람을 걸러내 로그인 시도를 먼저 할 수 있도록 해줄 수 있었다.

이미 로그인한 사용자는 굳이 회원가입과 로그인 페이지에 접속할 필요가 없다. 현재의 상태로는 url을 만지지 않고 흘러가는데로 html 안에서만 과정을 진행한다면 문제될 것이 없었다. 그런데 url을 만지는 순간 로그인을 했는데도 페이지가 이동되는 것을 알 수 있었다.
해당 문제를 해결하기 위해 회원가입과 로그인이 이루어지는 user/views.py를 수정해보도록 하겠다.
앞에서 수정했던 부분은 그대로 가져가면서, method == 'GET'인 부분의 코드를 수정해 보았다.
redirect('/')를 한 이유는 /를 담당하는 함수는 tweet/views.py의 home 함수이고, 해당 함수에서 사용자 인증에 성공하면 /tweet으로 redirect를 해주기 때문에 그냥 /를 사용했다.
아래의 tweet/urls.py를 확인해 보면, url이 없는 경우에 즉 http://127.0.0.1:8000/인 경우에 home 함수가 실행이 되는 것을 알 수 있다.

그 다음 사진을 확인해 보면, home 함수에서 사용자 인증이 되어 있다면 /tweet으로 url을 설정해주는 것을 알 수 있다.

# 뒷부분 생략
def sign_up_view(request):
if request.method == 'GET':
# user 변수에 사용자 인증 정보를 담는다.
user = request.user.is_authenticated
# 만약 사용자 인증이 되어 있다면
if user:
# home으로 바로 이동시켜주고
return redirect('/')
# 만약 사용자 인증이 되어 있지 않다면
else:
# 회원가입 페이지로 보내준다.
return render(request, 'user/signup.html')
# 앞부분 생략
def sign_in_view(request):
elif request.method == 'GET':
user = request.user.is_authenticated
if user:
return redirect('/')
else:
return render(request, 'user/signin.html')
user와 관련된 기능이기 때문에 user/views.py를 수정해주도록 하겠다. def logout(request):
auth.logout(request)
return redirect('/')
# 다른 부분 생략
from django.contrib.auth.decorators import login_required
# 사용자가 로그인이 되어있어야만 접근 가능하다고 알려주는 함수
@login_required
def logout(request):
auth.logout(request)
return redirect('/')
user/urls.py에서 url을 설정하도록 하겠다. from django.urls import path
from . import views
urlpatterns = [
# sign-up/ 으로 들어가면 views.py에 있는 sign_up_view 함수가 실행된다. (해당 함수는 signup.html로 render 해준다.)
path('sign-up/', views.sign_up_view, name='sign-up'),
# sign-in/ 으로 들어가면 views.py에 있는 sign_in_view 함수가 실행된다. (해당 함수는 signin.html로 render 해준다.)
path('sign-in/', views.sign_in_view, name='sign-in'),
path('logout/', views.logout, name='logout'),
]
base.html에 작업을 해주었다. <!-- 윗부분 및 아랫부분 생략 -->
<form>
<!-- 사용자 인증이 완료된 사용자라면 이름을 보여줘 -->
{% 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 class="nav-link" href="/logout">logout</a>
</li>
</ul>
<!-- if문 종료 -->
{% endif %}
</form>


textarea가 존재한다. 해당 부분에 글을 입력하고 버튼을 누르게 되면 게시글이 POST 요청이 되어 db에 저장되는 부분까지 진행해 보겠다. home.html을 열어 구조를 확인하고 수정을 하도록 하겠다.<form>태그 안의 내용이 버튼을 누르면 전송될 수 있도록 method와 action을 설정해 주었다. <!-- 글을 작성 하는 곳 -->
<div class="row mb-2">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<div class="media">
<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>
</div>
</div>
</div>
</div>
</div>

tweet/views.pyall_tweet이라는 변수를 만들어, 해당 변수 안에 사용자가 입력한 TweetModel 안에 있는 모든 정보를 저장하게 된다. # 뒷 부분 생략
def tweet(request):
if request.method == 'GET':
# 사용자 인증에 대한 정보를 user 변수에 대입
user = request.user.is_authenticated
# 만약 사용자 인증이 되어 있다면
if user:
# tweet 모델에 저장한 모든 데이터를 불러올 거야. 그런데 순서는 시간 순서가 아닌 최신순으로 불러와줘.
all_tweet = TweetModel.objects.all().order_by('-created_at')
# tweet/home.html 로 사용자가 갈 수 있도록 함
return render(request, 'tweet/home.html', {'tweet': all_tweet})
else:
# 사용자 인증이 되지 않았으므로 로그인 화면으로 보내줌
return redirect('/sign-in')
template/tweet/home.htmltw.author.username는 tweet의 models.py에서 볼 수 있듯이 author는 외래키로 UserModel의 값을 받아와 쓰고 있다. 그러므로 username을 명시하지 않으면 UserModel의 모든 값이 나오게 되므로 명시를 해줘야 한다.<!-- 작성 된 글이 나오는 곳 -->
<div class="row">
<!-- views.py에서 게시글의 모든 정보를 tweet으로 넘겨줬음 -->
{% for tw in tweet %}
<div class="col-md-12">
<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;">
<!-- tw.author.username인 이유는 tweet의 models.py에서 볼 수 있듯이 author는 외래키로 UserModel
의 값을 받아와 쓰고 있다. 그러므로 username을 명시하지 않으면 UserModel의 모든 값이 나오게 되므로 명시를 해줘야 한다. -->
<!-- tw.created_at은 그냥 가져오게 되면 몇월 몇일 몇시 몇분까지 다 들고오게 되므로, timesince(django 기본제공 함수)를 적용해준다.-->
<span style="font-size: small">{{ tw.author.username }} - {{ tw.created_at|timesince }} 전</span>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>

어떤 게시글인지 지정해주는 작업이 필요하다.http://127.0.0.1:8000/tweet/delete/게시글의idtweet/views.pyid값을 가지고 TweetModel에서 같은 id인 게시글을 my_tweet 변수에 저장하고, 이것을 삭제한 후 /tweet으로 리다이렉트해준다. # 앞 부분 생략
# 로그인을 한 사용자만이 함수를 실행할 수 있도록 도와주는 기능
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 = [
path('', views.home, name='home'), # 127.0.0.1:8000 과 views.py 폴더의 home 함수 연결
path('tweet/', views.tweet, name='tweet'), # 127.0.0.1:8000/tweet 과 views.py 폴더의 tweet 함수 연결
path('tweet/delete/<int:id>', views.delete_tweet, name='delete-tweet'),
]
home.html에서 삭제 버튼을 만들도록 하겠다. <!-- 작성 된 글이 나오는 곳 -->
<div class="row">
<!-- views.py에서 게시글의 모든 정보를 tweet으로 넘겨줬음 -->
{% for tw in tweet %}
<div class="col-md-12">
<div class="card">
<div class="card-body">
<!-- 만약 이 글을 쓴 작성자가 현재 로그인한 사용자라면-->
{% if tw.author == user %}
<div style="text-align: right">
<!-- a 태그를 사용해 url로 이동할 수 있게 해줌 -->
<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>
<!-- 아래 부분 생략-->