과제
- 데이터베이스 설게 시, 중간자 모델을 사용하는게 좋다. 사용하지 않으면 이상하게 짜게 된다.
- 날짜 형식이 pasing 되지 않을 때는 UTC로 변환해준다.
- 일반적으로 서버는 무조건 UTC 표준시로 보내고, front-end에서 처리한다.
- template - date
9. 북마크 기능 추가
- 북마크한 동영상만 데이터베이스에 저장
- 북마크 한 동영상만 볼 수 있는 페이지 추가
- 검색 결과 북마크 추가/삭제 토글 버튼 추가
10. Migrate
이미 데이터베이스 테이블이 만들어진 상태이기 때문에 마이그레이션 시, 에러가 난다. 이럴때 이미 있는 데이터 베이스 모델을 보며 최대한 비슷하게 모델을 임시로 변경한다.
class MyUser(AbstractUser):
bookmark_videos = models.ManyToManyField(
'video.Video',
blank=True,
# through='BookmarkVideo',
)
class BookmarkVideo(models.Model):
myuser = models.ForeignKey(settings.AUTH_USER_MODEL)
video = models.ForeignKey('video.Video')
create_date = models.DateTimeField(auto_now_add=True)
# migration 에러를 해결하기 위해 db_table 이름을 설정한 후
# --fake 옵션을 주어 migration
# $./manage.py migrate member --fake
# db_table은 만들어져 있는 db_table 이름 필드이름과 동일하게 한다.
class Meta:
db_table = 'member_myuser_bookmark_videos'
11. Pagination
장고에서는 “Previous”/”Next” 링크를 제공하는 pagination 기능을 제공한다. 이 클래스는 django/core/paginator.py
에 있다.
# 2는 한 페이지에 나타낼 개수
>>> p = Paginator(objects, 2)
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
def listing(request):
contact_list = Contacts.objects.all()
# 한 페이지에 25개 노출
paginator = Paginator(contact_list, 25)
page = request.GET.get('page')
try:
# 현재 페이지 number와 앞,뒤 페이지 정보를 가지고 있다.
contacts = paginator.page(page)
except PageNotAnInteger:
# page가 integer가 아니거나 없을 경우에는 첫 번째 페이지로 이동
contacts = paginator.page(1)
except EmptyPage:
# 범위를 넘는 큰 수를 입력 할 경우(예: 9999), 마지막 페이지로 이동
contacts = paginator.page(paginator.num_pages)
return render(request, 'list.html', {'contacts': contacts})
페이스북 프로젝트
프로젝트 환경 설정
$ mkdir facebook
$ cd facebook
$ pyenv virtualenv 3.4.3 facebook
$ pyenv local facebook
$ pip install django
$ pip freeze > requirements.txt
$ django-admin startproject facebook
$ mv facebook django_app
$ vi .gitignore
# .conf 추가
# .idea 추가
$ tree
.
├── django_app
│ ├── facebook
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── manage.py
└── requirements.txt
프로젝트 구성
facebook <Repository directory>
├── django_app <Django project directory> # source directory 설정
│ ├── facebook <Settings directory>
│ ├── ft # python package
│ ├── static
│ └── templates
└── .conf
└── settings_local.json
Settings.py
import json
ROOT_DIR = os.path.dirname(BASE_DIR)
CONF_DIR = os.path.join(ROOT_DIR, '.conf')
# Load config file
config = json.loads(open(os.path.join(CONF_DIR, 'settings_local.json')).read())
# Template path
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')
# Static path
STATIC_URL = '/static/'
STATIC_DIR = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = {
STATIC_DIR,
}
# Media path
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
SECRET_KEY = config['django']['secret_key']
TEMPLATES = [
{
# ...
'DIRS': [
TEMPLATE_DIR
],
'APP_DIRS': True,
'OPTIONS': {
# ...
},
},
]
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
Custom User Model
- member app 생성
member/models.py
파일에 AbstractUser를 상속받는 MyUser를 구현- 마이그레이션
- 세팅 파일에 설정 추가
# member/models.py
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
pass
# facebook/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'member',
]
AUTH_USER_MODEL = 'member.MyUser'
페이스북에 앱 등록
Facebook Developer 사이트에서 새 앱 추가한다. 추가한 후, 발급한 앱 ID와 앱 시크릿 코드는 API에 사용할 값 이다.
서버측에서 페이스북 OAuth 사용하기
1. 로그인 기본 구조 구현
로그인 뷰 생성하기
- view 생성
- template 생성
- url 연결
2. 페이스북 로그인 창 띄우기
MySite에서 User가 페이스북 로그인을 클릭하면, MySite에서 페이스북으로 redirect
- request :
https://www.facebook.com/v2.8/dialog/oauth?client_id={app-id}&redirect_uri={redirect-uri}
페이스북은 User와 통신하여 로그인이 완료되면,
- response : redirect_uri에
code
파라미터를 포함해 전달
def login_fbv(request):
facebook_app_id = settings.config['facebook']['app_id']
context = {
'facebook_app_id': facebook_app_id
}
return render(request, 'member/login.html', context)
def login_facebook(request):
pass
<!-- member/login.html -->
<div>
<a href="https://www.facebook.com/v2.8/dialog/oauth?client_id=
&redirect_uri=http://localhost:8000/member/login/facebook">페이스북으로 로그인</a>
</div>
링크를 클릭하면 다음과 같이 응답이 온 것을 확인 할 수 있다.
[23/Feb/2017 22:46:33] "GET /member/login/facebook?code=AQBSR1b3pztG2P1cstilFj8HtT6x-HVOA9SzMC3VN-4MjPGpP1QD3 ... vDJEy HTTP/1.1" 404 2611
이 때, 요청이 정상적으로 되지 않을 경우 Facebook Developer 사이트에서 설정을 해줘야한다. 모든 앱보기에서 해당 앱을 클릭 한 후 ‘설정 - 기본설정’에 앱 도메인, 플랫폼 추가 - 웹 사이트 - 사이트 URL 등록 후 다시 시도한다.
3. 사용자 Access Token 요청
code
(사용자 정보)와 app_secret
(앱 정보)를 보내 사용자 Access Token을 요청
- request : `https://graph.facebook.com/v2.8/oauth/access_token?client_id={app-id}&redirect_uri={redirect-uri}&client_secret={app-secret}&code={code-parameter}
- response :
access_token
,token_type
,expires_in
def login_facebook(request):
APP_ID = settings.config['facebook']['app_id']
SECRET_CODE = settings.config['facebook']['secret_code']
REDIRECT_URI = 'http://localhost:8000/member/login/facebook'
if request.GET.get('code'):
code = request.GET.get('code')
# 전달받은 code 값을 이용해 access_token 요청
url_request_access_token = 'https://graph.facebook.com/v2.8/oauth/access_token'
params = {
'client_id': APP_ID,
'redirect_uri': REDIRECT_URI,
'client_secret': SECRET_CODE,
'code': code
}
r = requests.get(url_request_access_token, params=params)
pprint(r.text)
dict_access_token = r.json()
USER_ACCESS_TOKEN = dict_access_token['access_token']
print('USER_ACCESS_TOKEN : {}'.format(USER_ACCESS_TOKEN))
# pprint(r.text) 결과
'{"access_token":"EAAaZA35ZAAH...ZBSrXpluViQo4KIZD",
"token_type":"bearer","expires_in":5164067}'
# print('USER_ACCESS_TOKEN : {}'.format(USER_ACCESS_TOKEN)) 결과
USER_ACCESS_TOKEN : EAAaZA35ZAAHt4BAHKZBIZ...ZCPZAeJEZD
사용자 액세스 토큰은 단기 실행 토큰과 장기 실행 토큰의 두 양식으로 제공된다. 일반적으로 단기 실행 토큰의 사용 시간은 약 1~2시간인 반면, 장기 실행 토큰의 사용 시간은 약 60일이다.
4. 앱 Access Token 생성
앱 Access Token을 발급받지 않고 생성하는 방법으로 만든다.
access_token=app_id|app_secret
APP_ACCESS_TOKEN = '{app_id}|{secret_code}'.format(
app_id=APP_ID,
secret_code=SECRET_CODE
)
5. Access Token 검사, User 정보 접근
User Access Token과 App Access Token을 전달해 검증한다.
- request :
graph.facebook.com/debug_token?input_token={token-to-inspect}&access_token={app-token-or-admin-token}
- response :
is_valid
,user_id
# user_access_token, app_access_token 사용해 토큰 검증
url_debug_token = 'https://graph.facebook.com/debug_token'
params = {
'input_token': USER_ACCESS_TOKEN,
'access_token': APP_ACCESS_TOKEN,
}
r = requests.get(url_debug_token, params=params)
dict_debug_token = r.json()
pprint(dict_debug_token)
USER_ID = dict_debug_token['data']['user_id']
print('USER_ID : {}'.format(USER_ID))
# pprint(dict_debug_token) 결과
{'data': {'app_id': '1...8',
'application': 'wps-oauth',
'expires_at': 1493022658,
'is_valid': True,
'issued_at': 1487838658,
'scopes': ['public_profile'],
'user_id': '1...4'}}
# print('USER_ID : {}'.format(USER_ID)) 결과
USER_ID : 1...4
QnA
앱 Access Token 생성법이 2가지인데 이유는,
- 사용자 측에서 요청하는 경우
- 서버에서 직접 생성하는 경우 (파이프라인 사용하는 것)