프로젝트 생성, 관리

각각의 프로젝트들은 projects폴더 하위에 새 폴더를 만들어 사용한다. 새로 시작할 블로그 프로젝트는 projects/blog경로를 사용한다.

$ mkdir django-tutorial
$ cd django-tutorial/
$ pwd
/django-tutorial

# 가상환경 생성
$ pyenv virtualenv 3.4.3 django_tutorial_env

# 가상환경 적용
$ pyenv local django_tutorial_env

# Django 설치
$ pip list
$ pip install django

# Django Project 생성
$ django-admin startproject mysite

# 진행상태 확인
$ l
total 8
drwxr-xr-x   4 limhm  staff   136B  2  6 11:06 .
drwxr-xr-x  10 limhm  staff   340B  2  6 11:04 ..
-rw-r--r--   1 limhm  staff    20B  2  6 11:05 .python-version
drwxr-xr-x   4 limhm  staff   136B  2  6 11:06 mysite

$ cd ..
$ tree django-tutorial
django-tutorial
└── mysite
    ├── manage.py
    └── mysite
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

구조 설계

(Project name) <Repository directory>
└── django_app <Django project directory>
    ├── manage.py
    └── (Project name) <Settings Directory>
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

프로젝트명과 프로젝트를 담고 있는 폴더명(프로젝트 컨테이너 폴더)은 별개로 취급된다. Django에서 프로젝트 생성 시 기본적으로 프로젝트명과 프로젝트를 담고 있는 폴더명을 일치하게 생성해주는데, 처음에는 헷갈릴 수 있으므로 프로젝트를 담고 있는 폴더명은 다르게 바꾸어준다.

$ cd django-tutorial
$ mv mysite django_app

$ tree django-tutorial
django-tutorial
└── django_app
    ├── manage.py
    └── mysite
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

Git 설정

$ git init
$ vi .gitignore
$ pip freeze > requirements.txt

$ pwd
/django-tutorial

$ l
total 24
drwxr-xr-x   7 limhm  staff   238B  2  6 11:13 .
drwxr-xr-x  10 limhm  staff   340B  2  6 11:04 ..
drwxr-xr-x   9 limhm  staff   306B  2  6 11:16 .git
-rw-r--r--   1 limhm  staff   2.6K  2  6 11:13 .gitignore
-rw-r--r--   1 limhm  staff    20B  2  6 11:05 .python-version
drwxr-xr-x   4 limhm  staff   136B  2  6 11:06 django_app
-rw-r--r--   1 limhm  staff    15B  2  6 11:13 requirements.txt

$ git remote add origin https://github.com/pinstinct/django_tutorial_class.git

$ py .
$ ./manage.py runserver

$ git commit -a -m "Project init"

Pycharm 단축키

go to line에 할당된 cmd + l 제거
reformat code에 cmd + l 추가
reformat code : pip 규칙에 자동으로 맞춰준다. 자주 사용하는 것이 좋다.

# .gitignore에 추가
.ieda/

Django Tutorial

$ python -m django --version
$ mkdir polls
$ l
total 32
drwxr-xr-x  6 limhm  staff   204B  2  6 11:39 .
drwxr-xr-x  8 limhm  staff   272B  2  6 11:38 ..
-rw-r--r--  1 limhm  staff    12K  2  6 11:29 db.sqlite3
-rwxr-xr-x  1 limhm  staff   804B  2  6 11:06 manage.py
drwxr-xr-x  7 limhm  staff   238B  2  6 11:29 mysite
drwxr-xr-x  2 limhm  staff    68B  2  6 11:39 polls

$ python manage.py startapp polls
$ tree -I '__pycache__'
.
├── db.sqlite3
├── manage.py
├── mysite
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── polls
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

# alias tree-python 추가
# source ~/.zshrc

# pycharm open!
# Project Interpreter 설정
# usr/locar/var/pyenv/versions/django-toturial-env/bin/python

Hello world 출력

# mysite/urls.py
from django.conf.urls import url, include

urlpatterns = [
    # polls/일 경우, polls.urls를 호출
    url(r'^polls/', include('polls.urls')),
    url(r'^admin/', admin.site.urls),
]
# polls/urls.py
from django.conf.urls import url
from . import views


urlpatterns = [
    # 아무것도 입력이 되지 않았을 경우, views.index 호출
    url(r'^$', views.index),
]
# polls/views.py
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls site.")

데이터 베이스-모델

# polls/models.py
# 데이터 베이스가 어떻게 만들어질지 정의
class Qeustion(models.Model):
    q_tesxt = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Qeustion)
    c_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
# mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # polls가 장고에서 관리된다고 알려준다.
    'polls.apps.PollsConfig',
]
$ python manage.py makemigrations polls
Migrations for 'polls':
  polls/migrations/0001_initial.py:
    - Create model Choice
    - Create model Qeustion
    - Add field question to choice
# 결과를 polls/migrations/0001_initial.py에서 확인

# 실행할 sql문 확인
$ ./manage.py sqlmigrate polls 0001
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- choice에 field 필드 추가
--
# 관례상 외래키에 _id를 붙힌다.
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;

COMMIT;

# 데이터 베이스 생성
$ ./manage.py migrate
Applying polls.0001_initial... OK

SQL대신 장고 ORM으로 다루기

# ipython으로 실행하기 위해 설치
$ pip install ipython
$ pip install django_extensions

# mysite/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'django_extensions',

    'polls.apps.PollsConfig',
]

$ python manage.py shell_plus

In [1]: from polls.models import Question, Choice

# Question 객체를 가진 모든 것
In [2]: q = Question.objects.all()

In [3]: q
Out[3]: <QuerySet []>

# .query : 쿼리문 확인
In [4]: print(q.query)
SELECT "polls_question"."id", "polls_question"."q_text", "polls_question"."pub_date" FROM "polls_question"

In [5]: from django.utils import timezone

In [6]: q = Question(q_text = "what new?", pub_date=timezone.now())

In [8]: q.save()

In [9]: q.id
Out[9]: 1

In [10]: q.q_text
Out[10]: "what new?"

In [11]: q.pub_date
Out[11]: datetime.datetime(2017, 2, 6, 12, 22, 38, 99032, tzinfo=<UTC>)

# 아직 메모리 상에만 있음
In [12]: q.q_text = "what up?"

# 저장을 해주면, 데이터베이스에 적용 됨
In [13]: q.save()

# filter는 where절 사용, 결과는 리스트로 반환
# 값이 없으면, 빈 리시트 반환
In [4]: print(Question.objects.filter(id=1).query)
SELECT "polls_question"."id", "polls_question"."q_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."id" = 1

# get은 한 개만 리턴
In [10]: q = Question.objects.get(pk=1)

In [14]: q
Out[14]: <Question: what up?>

In [15]: from django.utils import timezone

In [16]: current_year = timezone.now().year

# __ : 속성의 속성을 참조할 때
In [17]: Question.objects.get(pub_date__year=current_year)
Out[17]: <Question: Whats up?>

# Choice 객체 확인
# 역참조 : 역참조할테이블(소문자)_set 으로 역참조
In [18]: q.choice_set.all()
Out[18]: <QuerySet []>

# 객체 생성
In [19]: q.choice_set.create(c_text='Not much', votes=0)
Out[19]: <Choice: Not much>

In [20]: q.choice_set.create(c_text='The sky', votes=0)
Out[20]: <Choice: The sky>

In [21]: q.choice_set.create(c_text='Just hacking again', votes=0)
Out[21]: <Choice: Just hacking again>

In [23]: c = q.choice_set.create(c_text='tired', votes=0)

# Question 객체 확인
# 참조 : . 으로 참조
In [24]: c.question
Out[24]: <Question: what up?>

# 속성에 접근할 때는 .이 아니라 __로 접근
# Question의 pub_date의 year
In [25]: Choice.objects.filter(question__pub_date__year=current_year)
Out[25]: <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>, <Choice: tired>]>

# 데이터베이스에서 삭제
In [26]: c.delete()
Out[26]: (1, {'polls.Choice': 1})

Admin

# polls/urls.py
from django.conf.urls import url
from . import views

urlpatterns = [
    # 정규표현식 : 아무것도 입력이 되지 않았을 경우
    # views.index 호출
    url(r'^$', views.index),

    # 숫자 1개 이상은 question_id
    # question_id를 view.detail에 넘겨줌
    url(r'^(?P<question_id>[0-9]+)/$',
        views.detail, name='detail'),

    # ex: /polls/5/results/
    url(r'^(?P<question_id>[0-9]+)/results/$',
        views.results, name='results'),

    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$',
        views.vote, name='vote'),
]
from django.http import HttpResponse
from .models import Question


def index(request):
    return HttpResponse("Hello, world. You're at the polls site.")


def detail(request, question_id):
    # pub_date 컬럼(필드)를 기준으로 내림차순 정렬(-pub_date, 마이너스)한 결과
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    # 돌려줄 문자열을 작성
    output = ', '.join([q.question_text for q in latest_question_list])

    # output2 = ''
    # for q in latest_question_list:
    #     output2 += q.question_text + ', '
    # output2 = output2[:-2]

    return HttpResponse(output)


def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)


def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

디버그 하는법

Debug Configurations > + 버튼, python 선택

  • Name : Django runserver
  • Script: manage.py
  • Script parameters: runserver
  • Python interpreter : 설정환 가상환경으로 되어있나 확인
  • Working directory : ~/projects/django-tutorial/django_all

브레이크 포인트를 output2 변수라인에 설정한다. 디버그 실행 후, 웹페이지에서 다시 접속하면 포인트에서 멈춘다. 그 때, request객체를 확인할 수 있다.

뷰와 분리하기

django_app/templates/polls/index.html 생성한다.

from django.template import loader
from .models import Question

# request : 사용자가 요청할때 담겨오는 것
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    # 템플릿 loader를 이용해서 'polls/index.html'에서 템플릿을 가져옴
    template = loader.get_template('polls/index.html')
    # 템플릿 파일에 전달할 context 객체를 정의
    # 'latest_question_list' 키에 값을 할당
    # 해당 키를 템플릿에서 사용
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(context, request)

위의 내용을 줄여 쓰면 아래와 같다.

# render() 함수 사용
def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

템플릿 경로를 설정해준다.

# mysite/settings.py
import os

# BASE_DIR 장고 앱을 가르킴
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 서버 실행시 출력
# ~/projects/django_tutorial/django_app
print(BASE_DIR)

# 템플릿 디렉토리 추가
# 기본적으로 polls/templates/polls/를 찾기 때문에 알려줘야 함
TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')

# ~/projects/django_tutorial/django_app/templates
print(TEMPLATES_DIR)

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            # 템플릿 검색 경로 추가
            TEMPLATES_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',
            ],
        },
    },
]

과제

Django girls tutorial : Django모델 ~ CSS - 예쁘게 만들기(배포하기는 제외)

Django girls tutorial 문서 정리

Django girls tutorial 프로젝트