인스타그램 프로젝트
인스타그램의 주요 기능은 다음과 같다.
- 사진 등록
- 팔로우 하기
- 좋아요 하기
- 코멘트 남기기
프로젝트 설정
- 프로젝트 명 : instagram
- 어플리케이션 : member, post
1. 프로젝트로 사용할 폴더 생성
2. pyenv virtualenv 3.4.3 <환경명>
3. pyenv local <환경명>
4. pip install django
5. django-admin startproject <프로젝트명>
6. mv <프로젝트명> django_app
7. Pycharm interpreter세팅
8. django_app폴더를 Sources root로 설정
DB 모델 설계
인스타 그램 서비스를 구성하는 DB모델을 설계해본다
member app
- MyUser model
- Attributes
- username (유일한 값을 갖음)
- last_name
- first_name
- nickname
- email
- date_joined
- last_modified
- following
- Method
- follow : 인자로 다른 Myuser 객체를 받아 해당 Myuser 객체를 팔로우 하도록 함
- unfollow : 위와 반대로 동작
- followers : 자신을 follow하고 있는 User목록, Property 처리
- change_nickname
post app
- Post model
- Attributes
- author (ForeignKey, User)
- photo
- like_users (Intermediate모델로 PostLike를 사용)
- content
- created_date
- Method
- add_comment
- like_count (property)
- comment_count (property)
- Comment model
- Attributes
- author (ForeignKey, User)
- post (ForeignKey, Post)
- content
- created_date
- PostLike model
- Attributes
- user (ForeignKey, User)
- post (ForeignKey, Post)
- created_date
member app
변수 선언
class MyUser(models.Model):
username = models.CharField('유저네임', unique=True, max_length=30)
last_name = models.CharField('성', max_length=20)
first_name = models.CharField('이름', max_length=20)
nickname = models.CharField('닉네임', max_length=20)
email = models.EmailField('이메일', blank=True)
date_joined = models.DateField('가입한 날짜', auto_now_add=True)
last_modified = models.DateField('수정한 날짜', auto_now=True)
def __str__(self):
return self.username
유저를 자동으로 생성하는 메서드 추가
@staticmethod
def create_dummy_user(num):
"""
num 개수만큼 User1 ~ User<num>까지 임의의 유저를 생성한다.
:return: 생성된 유저 수
"""
last_name_list = ['박', '이', '김', '서']
first_name_list = ['민아', '혜리', '소진', '아영']
nickname_list = ['빵', '리헬', '쏘지', '율곰']
created_count = 0
for i in range(num):
try:
MyUser.objects.create(
username='User {}'.format(i + 1),
last_name=random.choice(last_name_list),
first_name=random.choice(first_name_list),
nickname=random.choice(nickname_list)
)
created_count += 1
except IntegrityError as e:
print(e)
return created_count
In [1]: from member.models import MyUser
In [2]: MyUser.create_dummy_user(10)
Out[2]: 10
In [3]: MyUser.objects.all()
Out[3]: <QuerySet [<MyUser: A user>, <MyUser: B user>, <MyUser: User 1>, <MyUser: User 2>, <MyUser: User 3>, <MyUser: User 4>, <MyUser: User 5>, <MyUser: User 6>, <MyUser: User 7>, <MyUser: User 8>, <MyUser: User 9>, <MyUser: User 10>]>
글로벌 변수 생성하여 유저를 할당하는 메서드 추가
@staticmethod
def assign_global_variables():
# sys 모듈은 파이썬 인터프리터 관련 내장모듈
# __main__ 모듈을 module 변수에 할당
module = sys.modules['__main__']
users = MyUser.objects.filter(username__startswith='User')
for index, user in enumerate(users):
# 글로벌 변수를 할당하기 위해서 메인 모듈에서 하는 것
# __main__ 모듈에 'u1, u2, u3, ...' 이름으로 각 MyUser객체를 할당
setattr(module, 'u{}'.format(index + 1), user)
In [1]: from member.models import MyUser
In [2]: MyUser.assign_global_variables()
In [3]: globals()
Out[3]:
{'In': ['',
'from member.models import MyUser',
'MyUser.assign_global_variables()',
'globals()'],
'MyUser': member.models.MyUser,
'Out': {},
'_': '',
'__': '',
# ...
'u1': <MyUser: User 1>,
'u10': <MyUser: User 10>,
'u2': <MyUser: User 2>,
'u3': <MyUser: User 3>,
'u4': <MyUser: User 4>,
'u5': <MyUser: User 5>,
'u6': <MyUser: User 6>,
'u7': <MyUser: User 7>,
'u8': <MyUser: User 8>,
'u9': <MyUser: User 9>}
follow, unfollow, followers, change_nickname 메서드 추가
def follow(self, user):
self.following.add(user)
def unfollow(self, myuser):
self.following.remove(user)
# follower는 수정할 수 없으므로,
# property를 이용해 읽기 전용으로 쓰는게 좋음
@property
def followers(self):
return self.follower_set.all()
# 위에 3개 메서드는 Many to Many를 다루기 때문에 save가 필요 없지만,
# 이 메서드는 필요함
def change_nickname(self, new_nickname):
self.nickname = new_nickname
self.save()
post app
from django.db import models
from member.models import MyUser
class Post(models.Model):
author = models.ForeignKey(MyUser)
photo = models.ImageField(upload_to='post', blank=True)
content = models.TextField(blank=True)
created_date = models.DateTimeField(auto_now_add=True)
like_users = models.ManyToManyField(
MyUser,
through='PostLike',
related_name='like_post_set',
)
def __str__(self):
return 'Post[{]]'.format(self.id)
def toggle_like(self, user):
# 중간자 모델을 사용하기 때문에 PostLike 중간자 모델 매니저를 사용
# 핵심은 중간자 모델을 거쳐서 해야한다는 것이다.
# PostLike 중간자 모델에서 인자로 전달된 Post, MyUser 객체를 가진 row를 조회
# pl_list = PostLike.objects.filter(post=self, user=user)
pl_list = self.postlike_set.filter(user=user)
# 현재 인자로 전달된 user가 해당 Post(self)를 좋아여 한 적이 있는지 검사
# 만약에 이미 좋아요를 했을 경우 해당 내역을 삭제
# 아직 내역이 없을 경우 생성
if pl_list.exists():
pl_list.delete()
else:
PostLike.objects.create(post=self, user=user)
# 파이썬 삼항연산자
# [True일 경우 실행할 구문] if 조건문 else [False일 경우 실행할 구문]
# return PostLike.objects.create(post=self, user=user) if not pl_list.exists() else pl_list.delete()
def add_comment(self, user, content):
# 자신에게 연결된 Comment 객체의 역참조 매니저(comment_set)로 부터
# create 메서드를 이용해 Comment 객체를 생성
return self.comment_set.create(
user=user,
content=content
)
@property
def like_count(self):
return self.like_users.count()
@property
def comment_count(self):
return self.comment_set.count()
class Comment(models.Model):
author = models.ForeignKey(MyUser)
post = models.ForeignKey(Post)
content = models.TextField()
created_date = models.DateField(auto_now=True)
def __str__(self):
return 'Post[{}]\'s Comment[{}], Author[{}]'.format(
self.post_id,
self.id,
self.author_id,
)
class PostLike(models.Model):
user = models.ForeignKey(MyUser)
post = models.ForeignKey(Post)
created_date = models.DateTimeField(auto_now=True)
class Meta:
unique_together = (
('user', 'post'),
)
def __str__(self):
return 'Post[{}]\'s Like[{}]'.format(
self.post_id,
self.id,
)
로그인 페이지 추가
1. def login 뷰를 생성
2. member app을 include를 이용해 'member' namespace를 지정
instagram/urls.py와 member/ulrs.py 모두 사용
3. login 뷰는 member/login URL과 연결되도록 member/urls.py 구현
4. login 뷰에서는 member/login.html 파일을 렌더함
5. settings.py에 TEMPLATE_DIR 변수를 할당하고 추가
# instagram/urls.py
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^member/', include('member.urls')),
]
# member/views.py
from django.http import HttpResponse
from django.shortcuts import render
from django.contrib.auth import authenticate, login
def login(request):
"""
request.method == 'POST'일 때와 아닐 때의 동작을 구분
POST일 때는 authenticate, login을 거치는 로직을 실행
GET일 때는 member/login.html을 render하여 return 한다.
"""
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
# authenticate 인자로 POST로 전달받은 username, password 사용
user = authenticate(username=username, password=password)
if user is not None:
# 장고의 인증관리 시스템을 이용하여 세션을 관리해주기 위해 login() 함수 사용
login(request, user)
return HttpResponse('Login Success')
else:
return HttpResponse('Login Failed')
# GET method 요청이 온 경우
else:
return render(request, 'member/login.html')
# member/ursl.py
from django.conf.urls import url
from . import views
# 네임스페이스 지정
app_name = 'member'
urlpatterns = [
url(r'^login/$', views.login, name='login'),
]
<!-- templates/member/login.html -->
<h1>Login!</h1>
Request and Response object
HttpRequest objects
Attributes
- HttpRequest.path : 어떤 URL을 통해 왔는지
- HttpRequest.method :
request.method == 'POST'
- HttpRequest.content_type : 리소스 타입, request에 넘어온 데이터가 어떤 형식인지 (Json인지, 자바스크립트인지, …)
- HttpRequest.GET
- HttpRequest.POST
Writing views
Customizing error views
Django의 기본 오류 views는 대부분의 웹 응용 프로그램에서 충분하지만, 필요하면 쉽게 재정의 할 수 있습니다. 아래에 표시된 것처럼 URLconf에서 핸들러를 지정하기만 하면됩니다.
# 이렇게 지정해주면 해당 뷰로 이동
handler404 = 'mysite.views.my_custom_page_not_found_view'
- 404 : 페이지를 찾을 수 없다.
- 500 : 서버에 오류가 있다.
- 400 : 잘못된 요청을 보냈다.
User authentication in Django
cookie-based user sessions (쿠키기반 사용자 세션)
- 유저가 새로고침을 할 때, 연결의 연속성이 없는데,
- 그것을 연결해주는 것이 세션
- 세션을 유지하게 해주는 것이 쿠키
- 유저는 고유한 쿠키 값을 가진다.
- 쿠키를 가지고 서버는 세션을 유지한다.
Overview
인증(authentication)과 권한(authorization)
auth system
- Users
- Permissions
- Groups
- A configurable password hashing system (패스워드 해싱시스템) : 해시를 했을때, 다시 평문으로 복원되면 안된다.
- Forms and view tools for logging in users, or restricting content
- pluggable backend system : 3rd 로그인방식, id/pwd 방식과 같이 여러가지 방식을 갈아끼워 사용할 수 있다.
third-party package(로 제공)
- Password strength checking
- Throttling of login attempts : 로그인 시도 제한
- Authentication against third-parties (OAuth, for example)
Installation
MIDDLEWARE : request와 response사이에서 일어나는 과정을 미들웨어라고 한다. 모든 request에 대한 로그를 남고기 싶을 때, 미들웨어를 사용하면 된다.
Using the Django authentication system
User objects
기본 사용자의 primary 속성
- username : 장고와 장고관련 3rd party 툴에서도 기본적으로 사용하기 때문에 변경하지 않는 것이 좋다.
- password
- first_name
- last_name
Creating users
In [2]: from django.contrib.auth.models import User
In [3]: user = User.objects.create_user('jhon', 'test@test.com', 'johnpassword')
In [4]: user.username
Out[4]: 'jhon'
In [5]: user.password
Out[5]: '*****hased_password*****'
# 아래와 같이 해도 비밀번호가 변경되지만,
# 해시한 값이랑 다르기 때문에 나중에 로그인 할 수 없다.
user.password = '1234'
user.save()
# 패스워드 변경을 위해 set_password 메서드를 사용한다.
user.set_password('123')
Authentication in Web requests
Django는 request 객체를 이용해 세션과 미들웨어를 사용하여 인증 시스템을 연결합니다.
과제
- 인스타그램 프로젝트 : 로그인 페이지에 폼을 사용해서 처리하게 변경
- The template layer : Language overview 정리