2주차때 createsuperuser라는 명령어로 관리자를 만들고,
관리자 아이디와 패스워드를 이용해 admin에 로그인 했다.
이때 OMR모델을 만들지도 않았는데 로그인 기능을 사용 할 수 있었던 이유는
장고에서 기본으로 제공하는 User(사용자)모델 과 로그인/로그아웃 기능 덕분이다.
데이터베이스 테이블 확인하기
auth_user
장고에서 기본적으로 제겅하는 데이터베이스
my_user
내가 만든 데이터베이스
#user/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# AbstractUser Django가 제공하는 기본적인 auth_user테이블과 연동
class UserModel(AbstractUser):
#장고의 기본 모델을 상속받는다.
class Meta:
db_table = "my_user"
bio = models.CharField(max_length=256, default='')
- AbstractUser
- Django가 제공하는 기본적인 auth_user테이블과 연동
- class로 상속받아 my_user 테이블에는
- 기존의 name,password,time등의 필드를 작성할 필요가 없어졌다.
- 장고의 기본모델을 사용하고, bio라는 필드를 추가해줬다는것을 Django에 알릴 필요가 있다.
# mySpartaSns/settings.py
AUTH_USER_MODEL = 'user.UserModel'
장고의 사용자 모델을 수정하겠다.
user앱 안에 있는 UserModel 클래스에 적용
user앱에서 작성한 UserModel과 Django기본 auth_user를 결합.
장고에게 수정한것을 알리고, 적용시키기
python manage.py makemigrations
python manage.py migrate
터미널 명령어, 수정한것을 알리고, 적용시킨다.
user app의 view.py의 sign_up_view 함수를 수정한다.
from django.contrib.auth import get_user_model
사용자가 데이터베이스 안에 있는지 검사하는 함수 제공
exist_user = get_user_model().objects.filter(username=username)
데이터베이스에 username을 가진 사용자가 있는지확인
UserModel.objects.create_user(username=username,password=password,bio=bio)
입력받은 데이터베이스 저장
데이터베이스를 더블클릭하면, 저장된 데이터를 확인할 수 있다.
password필드에, 기존에 저장한 데이터는 1234지만,
이번에 새로 저장한 데이터는 Django의 암호화 기능이 추가되어 있다.
따라서 로그인시 암호화된 password와 비교하여 로그인을 할 수 있는 기능이 필요하다.
필요한 헤더파일
from django.contrib import auth
me = auth.authenticate(request,username = username,password=password)
if me is not None:
me.login(request,me)
return HttpResponse(me.username)
if문을 통해 me에 해당하는 정보가 비어있지 않다면, 로그인 성공
me.login(request,me)
Django의 로그인 기능을 이용하여, 사용자를 정보를 반환
from django.shortcuts import render,redirect
def home(request):
user = request.user.is_authenticated
#사용자가 로그인 했는지 검사해주는, 내장 기능
if user:
#사용자가 있다면
return redirect('/tweet')
else:
#사용자가 없다면 로그인 화면으로 이동
return redirect('/sign-in')
def tweet(request):
# 탬플릿의 tweet html파일을 보여주는 함수
if request.method =='GET':
return render(request,'tweet/home.html')
#render : html을 보여주는 함수
- 매개변수.user.is_authenticated
- 사용자가 로그인 했는지, 검사해주는 Django내장 기능
- render
- 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 함수 연결
]
- path('', views.home, name='home'),
- path가 없을때,127.0.0.1:8000일 때에는 views.py의 home 함수 연결
- path('tweet/', views.tweet, name='tweet')
- tweet/ url로 접근시, views.py의 tweet 함수 연결
def home(request):
user = request.user.is_authenticated
if user:
return redirect('/tweet')
- home 함수는, 사용자가 있다면, /tweet url로 보낸다.
path('tweet/', views.tweet, name='tweet')
- tweet url로 접근시, views.py의 twwet 함수를 호출한다.
def tweet(request):
if request.method =='GET':
return render(request,'tweet/home.html')
- render 메소드는 html을 보여주는 함수
- tweet함수는, 템플릿의 home.html 파일을 보여주는 기능을 한다.
from django.urls import path,include
from . import views
urlpatterns = [
.. 기본 기능들 ..
path('',include('user.urls')),
path('',include('tweet.urls')),
]
include메소드를 이용해, user,tweet앱의 url을
urlpatterns 리스트에 포함시킨다.
동작순서
1. 사용자로부터 url 요청을 받으면, 프로젝트의 urls.py 접속한다.
2. urlpattens리스트에 path에 해당하는 url이 있는지 검사한다.
3. 해당하는 urldl tweet앱의 url이라면, tweet앱의 urls.py로 이동한다.
4. tweet앱의 urlspattens리스트는, 조건에 맞춰 views.py의 함수를 호출한다.
5. views.py는 조건에 맞춰 사용자가 있는지 확인하고, html을 보여줄지 , redirect메소드를 사용할지 결정한다.
if me is not None:
auth.login(request,me)
return redirect('/')
로그인 성공시, 페이지를 새로고침한다.
문제점
1. 로그인을 했는데도 회원가입,로그인 박스가 나타난다.
2. 로그인을 한 유저의 정보가 출력되는 것이 없다.
<h5 class="card-title">{{ user.username }}</h5>
<p class="card-text">{{ user.bio }}</p>
탬플릿 문법 {{ }}
Django의 템플릿 문법을 사용해 card-title에 유저의 정보를 출력한다.
<form class="form-inline my-2 my-lg-0">
{% if not user.is_authenticated %}
-- 기본적으로 보여줄 네비게이션 코드 --
{% else %}
{{ user.username }} 님 반갑습니다!
{% endif %}
</form>
템플릿 문법 {% %}
{% if not user.is_authenticated %}
만약, 사용자가 로그인 한 상태가 아니라면 참이된다.
{% else %}
로그인 한 상황이 아니라면 출력할 코드 작성
{% endif %}
조건 분기문을 종료하겠다.
문제점
1. tweet/ url로 접속시, 로그인 해야만 접속할 수 있는
로그인을 하지 않아도 페이지에 접속할 수 있다.
2. 로그인,로그아웃 기능이 없다
def tweet(request):
if request.method =='GET':
user = request.user.is_authenticated
if user:
return render(request,'tweet/home.html')
else:
return redirect('/sign-in')
user = request.user.is_authenticated
장고의 내장 기능 user.is_authenticated를 이용하여, 로그인한 상태인지 확인한다.
if문을 통해 로그인을 했으면 render메소드, html을 보여주는 메소드를 통해
home.html을 보여준다
else문을 통해 , 로그인을 하지 않았다면, redirect메소드를 이용해 로그인 페이지로 이동한다.
추가 문제점 발견
1. 로그인한 상황에서 sign-in,sign-up 페이지로 이동시
로그인한 상황임에도 불구하고, 로그인 페이지,회원가입 페이지로 이동할 수 있다.
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')
똑같이 Django 내장기능 request.user.is_authenticated
를 통해 로그인 한 상태인지 확인하여
로그인한 상황이라면, 회원가입 페이지에 접속할 수 없게끔 제한한다.
- return redirect('/')
- '/'함수는 사용자가 요청했던 원래의 위치로 이동한다.
- 우리의 코드의 경우 tweet의 views.py의 home 함수를 확인해야한다.
def home(request):
user = request.user.is_authenticated
if user:
return redirect('/tweet')
else:
return redirect('/sign-in')
home함수는 사용자가 있다면 tweet페이지로 이동
없다면 sign-in 페이지로 이동하는 기능을 하고 있다.
로그인 페이지도, 로그인한 상태라면, 접근할수 없게 제한하기
user app의 views.py이동
def sign_in_view(request):
if *--(POST방식)--*:
*--로그인 할 수 있는지, 없는지 작동하는 기능--*
#GET 방식
elif request.method == 'GET':
user = request.user.is_authenticated
if user:
return redirect('/')
else :
return render(request, 'user/signin.html')
마찬가지로 장고의 내장 기능 매개변수.user.is_authenticated 를 이용하여
인증을 받은 상황이라면, redirect한다.
로그아웃 기능 만들기
user app의 views.py이동
def logout(request):
auth.logout(request)
return redirect('/')
auth.logout(매개변수)
Django의 내장기능, 로그아웃 기능을 작동하고
redirect를 이용해, 이전 요청으로 돌아간다.
from django.contrib.auth.decorators import login_required
@login_required
def logout(request):
auth.logout(request)
return redirect('/')
Django의 내장기능을 사용하여, 로그인이 되어있어야만 사용할 수 있는 함수임을 알린다.
urlpatterns = [
# -- 로그인, 회원가입 url 생략 --#
path('logout/',views.logout,name='logout'),
]
urlpatterns에 logout url 추가
logout에 접속할 수 있는 버튼 만들기
템플릿의 base.html이동
<form class="form-inline my-2 my-lg-0">
{% if not user.is_authenticated %}
사용자가 로그인 하지 않은 상황일때 동작할 구간
{% else %}
<ul class="navbar-nav mr-auto">
<li class="nav-item disabled">
<span class="nav-link">
{{ user.username }}님 반갑습니다!
</span>
</li>
<li class="nav-item active">
<a class="nav-link" href="/logout"> 로그아웃 </a>
</li>
</ul>
{% endif %}
</form>
같은 url이라도 접근하는 방식에 따라서 기능이 달라질 수 있다.
템플릿의 home.html 이동
<form action = '/tweet/' method = 'post'>
{% csrf_token %}
<div>
<textarea name='my-content'</textarea>
# 사용자가 글을 작성하는 영역 - my-content
</div>
<button type="submit">작성하기</button>
</form>
form 태그 안, submit으로 form태그를 작동,
tweet앱의 views로 이동하고 데이터는 post형태로 보낸다.
이때 csrf_token은 보안 Django의 보안 기능이 내포되어 있다.
from .models import TweetModel
# TweetModel 가져오기
TweetModel import
def tweet(request):
# 탬플릿의 tweet html파일을 보여주는 함수
if request.method =='GET':
//로그인 했을때 , 안했을때의 동작 생략
elif request.method == 'POST': # 요청 방식이 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')
def tweet(request):
# 탬플릿의 tweet html파일을 보여주는 함수
if request.method =='GET':
user = request.user.is_authenticated
if user:
all_tweet = TweetModel.objects.all().order_by('-created_at')
render(request,'tweet/home.html',{'tweet':all_tweet})
else:
return redirect('/sign-in')
정렬 단축기 ctrl + alt + l
html 작성 요약,( 필요한 기능만 )
{% for tw in tweet %}
{{ tw.content }}
{{ tw.author.username }}-{{ tw.created_at|timesince }}
{% endfor %}
- {% for tw in tweet %}
- tweet을 key값으로 for 반복문을 돈다.
- {{ tw.content }}
- 데이터베이스의 content를 불러온다.
author = models.ForeignKey(UserModel, on_delete=models.CASCADE)
author는 UserModel을 담고 있다.
- {{ tw.author.username }}-{{ tw.created_at|timesince }}
- tw.author는 UserModel이 갖고있는 모든 데이터를 가져온다.
- 그중 username을 호출하여 가져온 코드
- tw.created_at
- 작성한 날 , 몇월 몇시 몇분이 출력
- timesince를 출력하여, 작성시간 - 현재시간의 차이값을 출력한다.
- {% endfor %}
- 반복문 종료
삭제 할 기능을 추가 할 url은 http://127.0.0.1:8000/tweet/delete/게시글의id 입니다.
만약, 2번 게시글을 삭제하고 싶다면 tweet/delete/게시글id/2
from django.contrib.auth.decorators import login_required
#어떤 함수가 동작할때 로그인 되어 있어야만 동작할 수 있는 데코레이터 import
@login_required()
def delete_view(request,id):
my_tweet = TweetModel.objects.get(id=id)
my_tweet.delete()
return redirect('/tweet')
삭제 기능함수
데코레이터 함수로, 로그인이 되어 있어야만 동작할 수 있는 기능이 포함되어 있다.
urlpatterns = [
path('tweet/delete/<int:id>', views.delete_tweet, name='delete-tweet'),
]
urlpatterns 리스트의 url 생성
<int:id> : 삭제할 tweet의 id를 반환
views.delete_tweet : views.py의 delete_tweet 함수 호출
{% 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>
{% if tw.author == user %} : 이게시글의 작성자가, 현재 로그인한 작성자인지 확인
나만의 장고 사용자를 모델을 만들었다.
기존의 데이터베이스를 확장하여 장고 데이터베이스 확장하여 연결(클래스 상속)
회원가입,로그인,로그아웃 기능을 만들었다.
게시글 작성, 삭제,읽어오는 기능을 만들었다.
tweet의 게시글에 댓글 작성,불러오기,삭제 기능 만들기
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)
- TweetComment 클래스 추가
- 장고의 model을 상속받았다.
- tweet : TweetModel의 모든 데이터를 저장하는 변수
- author : UserModel의 모든 데이터를 저장하는 변수
- comment : 데이터의 필드
python manage.py makemigrations
python manage.py migrate
from .models import TweetComment
소스코드 작성
@login_required
def detail_tweet(request, id):
my_tweet = TweetModel.objects.get(id=id)
tweet_comment = TweetComment.objects.filter(tweet_id=id).order_by('-created_at')
return render(request,'tweet/tweet_detail.html',{'tweet':my_tweet,'comment':tweet_comment})
- @login_required
- 로그인을 했을때만, 함수가 실행될 수 있도록 제한하는 데코레이터
- def detail_tweet(request, id):
- 불러오기 함수, 인자값으로 id를 받는다.
- my_tweet = TweetModel.objects.get(id=id)
- id에 해당하는 필드의 데이터를 저장한다.
- tweet_comment = TweetComment.objects.filter(tweet_id=id).order_by('-created_at')
- TweetComment클래스에 저장된 데이터를 id값을 조회하여 해당하는 데이터만을 저장하는데, 최신순으로 내림차순하여 정렬한다.
- return render(request,'tweet/tweet_detail.html',{'tweet':my_tweet,'comment':tweet_comment})
- render메소드는, html을 사용자에게 보여주는 기능을 한다.
- tweet':my_tweet,'comment':tweet_comment}
- 데이터를 딕셔너리 형태로 보낸다.
@login_required
def write_comment(request, id):
# 댓글 작성 함수, 데코레이터로 로그인한 사용자만 인증받아 작성할 수 있는 내장 기능이 첨가되어 있다.
if request.method == 'POST':
#POST방식일때 동작
comment = request.POST.get("comment","")
# 전달받은 데이터를 comment 변수에 저장
current_tweet = TweetModel.objects.get(id=id)
# TweetModel의 데이터베이스 필드중 id값과 일치하는 모든 데이터를 변수에 저장한다.
TC = TweetComment()
#TweetComment클래스의 객채 생성
TC.comment = comment
TC.author = request.user
TC.tweet = current_tweet
# POST방식으로 전달된 데이터를 객체에 저장하고
TC.save()
#데이터를 저장한다.
return redirect('/tweet/'+str(id))
# id순서에 맞는 페이지를 반환한다.
@login_required
def delete_comment(request, id):
#댓글 삭제기능, 데코레이터로 로그인한 사용자만 접근할 수 있다.
comment = TweetComment.objects.get(id=id)
#TweetComment클래스의 id필드에, 매개변수 id에 해당하는 댓글 데이터를 불러온다.
current_tweet = comment.tweet.id
#tweet id, 현재 게시글의 위치값을 반환한다.
comment.delete()
# 해당하는 댓글 데이터 삭제
return redirect('/tweet/'+str(current_tweet))
#게시글에 해당하는 페이지를 반환한다.