Django Start - tutorial
장고란?
- 파이썬에서 같은 작업을 반복하지 않고 데이터베이스를 쉽게 사용하게 해주는 프레임워크
프레임 워크 (frame work)
- 프로젝트마다 반복되는 작업을 쉽게 할 수 있게 만든 프로그램
장고 시작하기
SSH key 생성하여 적용
https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/
- 터미널에서 홈폴더에 .ssh 폴더를 생성한다.
$ ssh-keygen -t rsa -b 4096 -C "깃헙 계정"
- 만들어진 파일 id_rsa.pub의 내용을 복사
$ cat id_rsa.pub
- github.com/profile/SSH and GPG keys 항목에 SSH key를 추가하여 붙여넣는다.
- github의 저장소를 생성하여 나오는 SSH 주소 “git@github.com:~~”로 git을 생성한다.
Blog 만들기 (djangogirls Tutorial)
1. 프로젝트 시작
# 장고 프로젝트를 새로 만든다.
$ ~/djangogirls$ django-admin startproject mysite
# 상위 폴더 이름을 변경
$ mv mysite django_app
# 명령어 확인
$ ./manage.py
# 로컬에서 서버를 시작 (컨트롤+C)로 종료
$ ./manage.py runserver
2. app 추가
# 프로젝트 폴더 내에 blog app을 추가한다.
$ django-admin startapp blog
# mysite/settings.py 파일의 INSTALLED_APPS 항목에 blog를 추가
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
]
3. 모델 만들기
# blog/models.py에 블로그 글의 모델을 만든다.
from django.db import models
from django.utils import timezone
# models의 Models를 상속받음
class Post(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
- 모델 생성 시, 필드에 이름을 정해줄 수 있다.
- ForeignKey 필드는 verbose_name 으로 정해준다. 위치 확인
from django.db import models
class Question(models.Model):
question_text = models.CharField('질문내용', max_length = 200)
pub_date = models.DateTimeField('발행일자')
class Choice(models.Model):
# ForeignKey 필드는 verbose_name 으로 정해준다. 위치 확인
question = models.ForeignKey(Question, verbose_name = '해당 질문', on_delete = models.CASCADE)
choice_text = models.CharField('선택내용', max_length = 200)
votes = models.IntegerField('총 투표수', default = 0)
4. 데이터베이스 설정
# 만들어진 모델을 데이터베이스에 적용한다.
$ ./manage.py migrate
# 데이터베이스를 만들기 위해 변경된 사항을 체크하여 보관한다.
$ ./manage.py makemigrations (app name:생략하면 전체)
# 체크했던 변경 사항들을 데이터베이스에 적용한다.
$ ./manage.py migrate (app name:생략하면 전체)
- 클래스 1개가 1테이블
-
데이터베이스 내용 확인은 SQLite Browser
- 필드 추가한 뒤(blank=True, null=True없이), makemigrations 하면 에러 발생한다. default 값을 정해주거나 코드 수정
데이터베이스 버전 확인 (migrations)
$ ./manage.py showmigrations
데이터베이스 롤백 rollback
# 0001_initial 상태로 되돌아가기
$ ./manage.py migrate polls 0001_initial
# 이후 버전을 삭제한다.
$ rm polls/migrations/삭제할 버전의 py 파일
5. 관리자
# blog/admin.py 에 아래 내용을 추가
from django.contrib import admin
from .models import Post
admin.site.register(Post)
# 터미널에서 서버 시작
$ ./manage.py runserver
# 관리자 계정을 만든다
$ ./manage.py createsuperuser
- 웹브라우저에서 127.0.0.1:8000/admin으로 접속 후 관리자 계정으로 로그인
- External Libraries/site-packages/django/contrib 에 있는 내용이 자동으로 생성됨
6. url
- root url 정규표현식 :
r'^$'
# mysite/urls.py
from django.conf.urls import url
from django.contrib import admin
from blog import views
urlpatterns = [
url(r'^admin/', admin.site.urls), # 기본주소/admin/ 으로 접속 시 admin.site.urls 실행
url(r'^$', views.post_list), # 기본주소로 접속 시, views.post_list 실행
]
- 만들어진 app내에 urls.py를 만들어 app 별로 url을 관리할 수 있다.
# mysite/urls.py
from django.conf.urls import url, include
from django.contrib import admin
# polls.urls를 모듈로 불러와서 사용 가능하다. (동적)
from polls import urls as polls_urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
# polls.urls를 모듈로 사용.
# url(r'^polls/', include('polls.urls')),
# polls.urls를 모듈로 불러와서 사용 가능하다. (동적)
url(r'^polls/', include(polls_urls)),
]
# polls/urls.py
from django.conf.urls import url
from . import views
# from polls import views
urlpatterns = [
url(r'^$', views.index, name = 'index')
]
7. templates
- 프로젝트 폴더 하위에 templates/blog 폴더 생성
- mysite/settings.py 파일의 TEMPLATES 항목에 아래 내용 추가 (지정한 경로의 폴더에서 template을 찾는다)
# mysite/settings.py
# 괄호는 상위폴더를 뜻한다. 그래서 djang _app
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# templates 폴더 경로 변수 생성
TEMPLATE_DIR = os.path.join(BASE_DIR, 'templates')
# 같은 파일 아래 부분에 TEMPLATES의 DIRS의 리스트에 위 경로를 입력해준다.
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
8. ORM / Query Sets
- ORM : 장고에서 데이터베이스를 객체로 불러오기 위한 쿼리
# django shell
$ ./manage.py shell
# blog.models에서 Post를 불러온다
>>> from blog.models import Post
# 출력하여 확인
>>> Post.objects.all()
# 실제 쿼리 확인
>>> pl = Post.objects.all()
>>> print(pl.query)
SELECT "blog_post"."id", "blog_post"."author_id", "blog_post"."title", "blog_post"."text", "blog_post"."created_date", "blog_post"."published_date" FROM "blog_post"
# User 테이블에서 user데이터를 불러온다.
>>> from django.contrib.auth.models import User
# user를 설정해준다.
>>> user = User.objects.get(id=1)
# 새 포스트 작성
>>> Post.objects.create(title='test title', text='test text', author=user)
# filter
>>> Post.objects.filter(title__contains='title')
<QuerySet [<Post: first title>, <Post: test title>]>
# 실제 쿼리 확인
>>> pl = Post.objects.filter(title__contains='title')
>>> print(pl.query)
SELECT "blog_post"."id", "blog_post"."author_id", "blog_post"."title", "blog_post"."text", "blog_post"."created_date", "blog_post"."published_date" FROM "blog_post" WHERE "blog_post"."title" LIKE %title% ESCAPE '\'
# timezone 불러오기
>>> from django.utils import timezone
# 범위 설정
>>> Post.objects.filter(published_date__lte=timezone.now())
[<Post: Sample title>]
# 특정 포스트를 불러옴
>>> post = Post.objects.get(title='test title')
# 퍼블리시
>>> post.publish()
get
model.objects.get(조건)
- 조건에 맞는 1개의 결과만 반환한다.
value_name.column_name
으로 사용- 조건에 맞는 값이 1개 이상일 경우 에러 발생
key = model1.objects.get(pk=1)
print(key.name)
all
model.objects.all()
- 모든 값을 반환
key = model1.objects.all()
print(key[0]['name'])
filter
model.objects.filter(조건)
- 조건에 맞는 모든 값을 반환
key = model2.objects.filter(name='lee')
print(key[0]['name'])
조건 키워드
키워드 | 설명 | 사용예 |
---|---|---|
__lt / __gt</br>__lte / __gte | ~보다 작다 / ~보다 크다</br>~보다 작거나 같다. ~보다 크거나 같다. | id가 1보다 큰 데이터 검색</br>model1.objects.filter(id__gt=1) |
__in | 주어진 리스트 안에 존재하는 데이터 검색 | model1.objects.filter(id__in[2, 3, 5] |
__year</br>__month</br>__day | 해당 년도, 월, 일 검색 | model1.objects.filter(published_date__year=2015) |
__isnull | 해당 열의 값이 null인 데이터 검색 | model1.objects.filter(name__isnull=True) |
__contains</br>__icontains | 해당 열의 값이 지정한 문자열을 포함하는 데이터 검색</br>__icontains는 대소문자를 구별하지 않음. | model1.objects.filter(name__contains=’com’) |
__startswith</br>__istartswith | 해당 열의 값이 지정한 문자열로 시작하는 데이터 검색</br>__istartswith는 대소문자를 구별하지 않음. | model1.objects.filter(name__startswith=’com’) |
__endswith</br>__iendswith | 해당 열의 값이 지정한 문자열로 끝나는 데이터 검색</br>__iendswith는 대소문자를 구별하지 않음. | model1.objects.filter(name__endswith=’com’) |
__range | 문자, 숫자, 날짜의 범위를 지정 | model1.objects.filter(id__range(2, 10) |
order by
model.objects.order_by('값')
- 기본은 오름차순 정렬
- 내림차순일 경우, 컬럼명 앞에
-
를 붙여준다.
value = model.objects.order_by('pk') #오름차순
value = model.objects.order_by('-pk') #내림차순
values
model.objects.values('값')
- 특정 컬럼의 값만 반환
value = model.objects.values('pk') # query set 형태로 pk값만 반환
9. 템플릿 동적 데이터
- blog/views.py 파일에 아래 내용을 추가하여 urls.py 파일에서 호출할때 실행할 함수를 만든다.
- 쿼리셋을 views.py 파일에 적용한다.
- post_list.html 에서 보이게 셋팅
from django.shortcuts import render
from django.utils import timezone
from .models import Post
def post_list(request):
# posts 변수에 ORM을 이용해서 전체 Post의 리스트(쿼리셋)을 대입
# posts = Post.objects.all()
# posts 변수에 ORM을 사용해서 전달할 쿼리셋이 Post의 published_date가 timezone.now()보다 작안값을 가질때만 해당하도록 필터를 사용한다.
posts = Post.objects.filter(published_date__lte=timezone.now())
context = {
'title': 'PostList from post_list view',
'posts': posts,
}
return render(request, 'blog/post_list.html', context = context)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Django Girls Tutorial by Joe</title>
</head>
<body>
<div>
<h1><a href="#">Django Girls Blog by Joe</a></h1>
<h3></h3>
</div>
# posts를 순회하기 위해 for문을 사용
</body>
</html>
10. bootstrap 적용
(1) bootstrap
을 다운받아 django_app/static
폴더에 넣어준다.
(2) mysite/settings.py
에 static
폴더 경로 추가
# django_app/static 폴더의 경로를 STATIC_DIR에 할당
STATIC_DIR = os.path.join(BASE_DIR, 'static')
# 이 경로로 시작하는 URL은 정적파일들의 위치에서 파일을 찾아 리턴
STATIC_URL = '/static/'
# 이 리스트(또는 튜플)의 경로는 STATIC_URL로 요청된 파일을 찾는 폴더로 사용됨
STATICFILES_DIRS = (
STATIC_DIR,
)
(3) post_list.html에 아래 내용을 추가해 준다. (bootstrap 경로)
<head>
<!--적용되면 경로는 static/bootstrap/css/bootstrap.css 이렇게 바뀜. 탬플릿태그-->
<link rel="stylesheet" href="{\% static 'bootstrap/css/bootstrap.css' %}">
</head>
(4) bootstrap을 적용해줄 부분에 적용해준다.
11. 템플릿 확장하기 (post_detail 만들기)
(1) views.py에 view 추가
def post_detail(request):
context = {
# id값(장고에서는 pk로 사용가능)이 아래 urls.py에서 보내준 pk값과 일치하는 post를 보내준다.
'post': Post.objects.get(id=pk)
}
return render(request, 'blog/post_detail.html', context)
(2) urls.py에 경로 추가
urlpatterns = [
url(r'^admin/', admin.site.urls),
# url에 이름을 정해주면 다른 곳에서 참조가 가능하다.
url(r'^$', views.post_list, name='post_list'),
# post/로 시작하고 숫자 1개 이상을 가진 뒤 /로 끝나는 url
# post 이후에 오는 숫자를 pk로 이름지어줌. -> views.py에서 인자로 사용 가능
# views.post_detail(request, pk) 형태로 전달됨
url(r'post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
]
(3) html에서 경로를 정해준다.
<div class="post-list">
</div>
12. 글쓰기 기능 만들기 post_create.html
(1) urls.py에 post 작성을 위한 url을 만들어 준다.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^$', views.post_list, name='post_list'),
# views.post_detail(request, pk) 형태로 전달됨
url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
url(r'^post/create/$', views.post_create, name='post_create'),
]
(2) views.py에 “POST” 매소드로 요청이 올때 실행할 post_create를 만들어준다.
def post_create(request):
# 요청의 method가 'GET'일 경우
if request.method == 'GET':
context = {
}
return render(request, 'blog/post_create.html', context)
# 요청의 method가 'POST'일 경우
elif request.method == 'POST':
# request.POST 값은 딕셔너리로 나온다.
data = request.POST
title = data['title']
text = data['text']
user = User.objects.first()
# 데이터베이스에 위 값으로 데이터를 쓴다.
post = Post.objects.create(
text=text,
title=title,
author=user,
)
# 요청 처리가 완료되면 post_detail url+pk를 리버스로 가지고 와서 redirect
# url dispatcher
return redirect('post_detail', pk=post.pk)
(3) post_create.html에 보여줄 화면을 만든다.
{\% extends 'blog/base.html' %}
{\% block content %}
<form action="" method="POST">
{# 응답이 적잘한 사용자에게서 왔는지 확인한다. 해킹방지 #}
{\% csrf_token %}
<input name="title" type="text">
<textarea name="text" id="" row="10" cols="100"></textarea>
{# submit 타입의 버튼은 form의 내용을 POST라는 매서드로 서버로 보낸다. #}
<button type="submit">Add post</button>
</form>
{\% endblock %}
(4) 장고 폼을 적용하기 위해 blog/forms.py 파일 생성
- blog app에 사용될 form들을 관리하는 파일
# blog/forms.py
# django의 forms를 import
from django import forms
# post_create에서 사용할 form을 정의하는 클래스 생성
class PostCreateForm(forms.Form):
title = forms.CharField(
label='제목',
max_length = 10,
required = True,
widget=forms.TextInput(
attrs = {
'class': 'form-control',
}
)
)
text = forms.CharField(
label='내용',
widget = forms.Textarea(
attrs = {
'class': 'form-control',
}
),
required = True
)
requirements.txt 만들기
- 프로젝트 가상환경에 설치된 pip 리스트를 문서로 만든다.
$ pip freeze > requirements.txt
reformatting!
vim에서 ctrl+z로 강제 종료 되었을 때, $ fg
+ Enter 하면 restore 됨.
url을 app 별로 관리
# mysite/urls.py
from django.conf.urls import url, include
from django.contrib import admin
# polls.urls를 모듈로 불러와서 사용 가능하다. (동적)
from polls import urls as polls_urls
urlpatterns = [
url(r'^admin/', admin.site.urls),
# polls.urls를 모듈로 사용.
# url(r'^polls/', include('polls.urls')),
# polls.urls를 모듈로 불러와서 사용 가능하다. (동적)
url(r'^polls/', include(polls_urls)),
]
git에 tag 달기
# tag 만들기
$ git tag -a part1 -m "DjangoTutorial Part1"
# tag push
$ git push origin part1
html label tag
- label 테그 사이에 있는 텍스트를 클릭했을때 for의 값을 id로 가진 input tag를 선택하거나 해제한다.
<input type="radio" name="choice" id="choice value=">
<label for="choice"></label><br>
git tag
# make tag
$ git tag -a [tag_name] -m "message"
# push tag
$ git push origin [tag_name]
message framwork
- request에 담겨있던 메세지를 저장해놓고 response 할때 그 메세지를 담아서 보내줄 수 있다
base.html 만들기
- 각 html 에서 반복되는 부분(hrader 등)을 base.html로 만들어 따로 빼놓는다.
# base.html
# 다른 html 파일의 내용이 들어가야 하는 부분 설정
{\% block content %}
{\% endblock %}
# 다른 html files
# 상단에 base.html 을 가지고 올 것을 설정
{\% extends 'polls/base.html' %}
# block 부분 설정
{\% block content %}
< 태그들....>
{\% endblock %}
- base.html 파일을 만든 뒤,
static/css
설정
# settings.py 에 static 폴더 패스 지정
STATIC_DIR = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = [
STATIC_DIR,
]
# base.html 의 최상단에 적어준다.
{\% load static %}
<link rel="stylesheet" href="{\% static 'bootstrap/css/bootstrap.css' %}">