django ch1 - ch4
개념
HTTP란 컴퓨터들끼리 HTML파일을 주고받을 수 있도록 하는 소통방식 또는 약속(protocol)이다.
클라이언트는 서버에게 request를 보내고, 서버는 response를 클라이언트에게 보낸다.
프로젝트 생성 및 환경 설정
레포지토리를 fork한 후, cmd를 켜 get clone [레포지토리 주소]로 폴더를 생성해준다.
pycharm에서 해당 폴더를 열고 터미널에 git checkout -b [브랜치 이름]를 쳐 branch를 만든다. (오른쪽 하단에서 현재 브랜치 확인 가능)
해당 프로젝트를 위한 가상환경을 세팅해준다.
Pycharm 상단 메뉴바 File - Settings - Project - Project Interpreter
add interpreter을 눌러서 하면 된다. 이때 파이썬 버전 10은 mySQL과 연동이 안되니, 버전을 9로 낮춰서 해야한다.
JW디렉토리에서 터미널에
./venv/Scripts/activate
를 쳐 가상환경을 돌려준다.
터미널에서 (venv)라는 명령어가 뜬다면 가상환경 설정에 성공한 것이다.
그러나 윈도우 설정상 권한 에러가 발생한다.
venv/Scripts/activate : 이 시스템에서 스크립트를 실행할 수 없으므로 ....\PythonWorkspace\test-venv\Scripts\Activate.ps1 파일을 로드할 수 없습니다. 자세한 내용은 about_Execution_Policies(https://go.microsoft.com/fwlink/?LinkID=135170)를 참조하십시오.
일회성 해결방안으로 (pycharm을 킬때마다 쳐야한다.)
Set-ExecutionPolicy Unrestricted -Scope Process
를 쳐봤지만 해결이 되지 않았다.
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
로 권한에러를 해결했다.
이제 보안상 깃허브에 올라가면 안되는 폴더 및 파일(venv/ 와 .idea/)을 .gitignore에 추가해준다.
이는 멘토분께서 다 해두셨다.
장고 버전을
pip install django==3.2.16
으로 깔아준다.
개발 서버
JW 디렉토리에서 터미널에
django-admin startproject mysite
를 치면
mysite/
manage.py
mysite/
__init__.py
settings.py #현재 장고 프로젝트의 환경 설정
urls.py #URL 선언을 저장
asgi.py #배포
wsgi.py #배포
가 생성된다.
mysite 디렉토리로 이동(CD)하여
python manage.py runserver
를 치면 개발 서버가 시작된다. 이제 본인의 웹 브라우져에서 http://127.0.0.1:8000/ 을 통해 접속할 수 있다.
이때, 윈도우가 8000포트를 연결하지 못한다면
python manage.py runserver 8080
로 포트넘버를 바꿔 연결한다.
앱을 모아둔 것이 프로젝트이다.
앱을 생성하기 위해 manage.py가 존재하는 디렉토리에서
python manage.py startapp polls
를 치면
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
가 생성된다. 이제 설문조사 어플리케이션(앱)을 만들 수 있다.
뷰 작성
《polls/view.py》는 controller의 역할을 한다. 뷰를 호출하기 위해서는 해당 뷰와 연결된 URL이 필요하다.
이때 URLconf가 사용된다. 이를 위해 urls.py라는 파일을 생성한다.
urlpatterns = [
path('', views.index, name='index'), #''시, index뷰로 보낸다.
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls), #include()를 사용하지 않는 유일한 예외
]
polls/5/result가 들어오면 include()함수는 5/result를 'polls.urls'로 보낸다. (하위 url)
이제 index 뷰가 URLconf에 연결되었다.
path() 함수에는 2개의 필수 인수인 route와 view, 2개의 선택 인수인 kwargs와 name이 전달될 수 있다.
- route 는 URL 패턴을 가진 문자열이다.
- view는 경로로부터 특정한 view 함수를 호출한다.
- URL에 name을 지으면, 템플릿을 포함한 Django 어디에서나 명확하게 참조할 수 있다.
데이터베이스 설치
mysite/settings.py 파일을 열면 Django 설정을 모듈 변수로 표현한 모듈이 있다.
기본적으로 SQLite를 제공하지만 mySQL로 변경해 진행했다.
pip install mysqlclient
를 깔아주고,
해당 mySQL 폴더에서
CREATE DATABASE jiwon;
을 써 데이터베이스를 설정해 준 후, mysite/settings.py에
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #기본설정
'NAME': 'jiwon', #데이터베이스 이름
'USER': 'root', #설정한 이름
'PASSWORD': '', #mySQL 비밀번호
'HOST': 'localhost',
'PORT': '3306', #포트번호
}
}
데이터베이스 속성값을 수정해준다. 그리고 gitignore 파일에
/JW/mysite/mysite/settings.py
를 추가한다. 이로써 보안까지 신경써서 작업할 수 있게 되었다.
+ 나중엔, .env파일로 뽑아내 import하는 식으로 구현했다.
(작년에 프로젝트할 때, 다른 팀 백엔드에서 준 인가코드(?)를 프론트가 받아 깃허브에 올렸다가 해킹당해 30만원 가량을 물어준 적이 있다. 그래서 gitignore파일은 항상 신경써야 한다.)
그 후,
python manage.py migrate
python manage.py runserver
가 잘 돌아간다면 Django와 mySQL 연동에 성공한 것이다.
물론, 나는 에러가 났다.
JW라는 경로에서 실행했기 때문이다. CD mysite로 들어가 다시 실행해보니
또 에러가 났다.
해당 데이터베이스가 없다고 나온다. 그래서 JW 데이터베이스를 지우고 소문자 jiwon 데이터베이스로 다시 생성하니 잘 돌아간다.
모델 생성
모델이란 데이터베이스의 구조를 말한다. 이 투표 앱에서는 Question 및 Choice 라는 두가지 모델을 클래스로 만든다.
Question 모델은 질문과 출판 날짜를, Choice 모델은 선택 텍스트와 투표 집계라는 필드(컬럼=열)를 가진다.
polls/models.py에
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200) #글자수 200제한
votes = models.IntegerField(default=0) #기본값 0으로 설정
를 써준다. 여기서 models. 뒤에 붙는 CharField나 DateTimeField는 각 필드가 어떤 자료형을 가지는지 알려준다.
cf. CharField는 max_length를 필수로 입력해줘야 한다.
Foreignkey(외래키)는 각각의 Choice가 Question에 관계된다는 것을 알려준다.
모델(스키마)의 활성화
우리는 모델을 이용해
- 데이터베이스 스키마를 생성
- Question과 Choice 객체에 접근하기 위한 Python 데이터베이스 접근 API를 생성
할 수 있다. 즉, 속성 및 속성값을 mySQL에 들어가 작업하지 않아도 된다는 뜻이다.
그 전에 프로젝트에게 polls 앱이 설치되어 있음을 알려야한다.
mysite/setting.py에
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
를 써주면 된다.
python manage.py makemigrations polls
이 코드를 통해 모델 클래스의 수정 및 생성을 mySQL의 jiwon DB에 적용했다.
모델(models.py)을 수정했다면 장고에게 makemigrations를 통해 알려주고 migrate로 데이터베이스에 반영해야 한다.
- makemigrations: models.py에서 적용한 변경사항이나 추가된 혹은 삭제된 사항들을 감지하여 파일로 생성 => 장고에서 파일만 생성
- migrate: 적용되지 않은 migrations들을(설정값들을) 적용시키는 역할 => mySQL까지 적용
python manage.py sqlmigrate polls 0001
를 치면,
BEGIN;
--
-- 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
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" serial NOT NULL PRIMARY KEY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL,
"question_id" integer NOT NULL
);
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
sqlmigrate 명령어로 금방 저장한 polls라는 migration이 어떤 SQL을 실행하는지 확인할 수 있다.
기본키랑 외래키를 설정한 적은 없는데, sqlmigrate는 자동으로 생성해주는 것인지?
python manage.py migrate
를 쳐,
아직 적용되지 않은 마이그레이션을 실제 데이터베이스에 적용해준다.
데이터베이스 API
필드에 속성값을 넣는 과정으로
python manage.py shell
를 통해 우선 Python shell을 실행시켜야 한다.
>>> from polls.models import Choice, Question
>>> Question.objects.all()
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save()
>>> q.id #1
>>> q.question_text #"What's new?"
>>> q.pub_date
>>> q.question_text = "What's up?"
>>> q.save()
>>> Question.objects.all() #<QuerySet [<Question: Question object (1)>]>
이렇게 객체를 생성하면 된다. (모델은 클래스이다.)
그러나, 마지막 줄에
<QuerySet [<Question: Question object (1)>]>
는 객체를 보는데 전혀 도움이 되지 않기 때문에,
polls/model.py에
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
__str__()을 추가해준다.
>>> from polls.models import Choice, Question
>>> Question.objects.all() #<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(id=1) #<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What') #<QuerySet [<Question: What's up?>]>
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
>>> Question.objects.get(id=2) #DoesNotExist: Question matching query does not exist.
>>> Question.objects.get(pk=1) #<Question: What's up?> id를 pk로 설정해두었다.
...
>>> q.choice_set.create(choice_text='Not much', votes=0) #<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0) #<Choice: The sky>
이제 what's up? 으로 잘 나온다. ORM
뷰(controller) 추가
스프링부트에는 뷰가 templete(html)이었지만, Django에서는 뷰가 controller이다.
그래서 각 뷰는 요청된 페이지의 내용이 담긴 HttpResponse 객체를 반환하거나, 혹은 Http404 같은 예외를 발생하게 해야한다. 프론트(클라이언트)와 백엔드 간의 관계를 간단히 살펴보면,
- 클라이언트로부터 HTTP request 요청을 받으면 URLconf를 이용하여 URL을 분석합니다.
- URL 분석 결과를 통해 해당 URL에 대한 처리를 담당할 View를 결정합니다.
- View는 자신의 로직을 실행하면서, 만일 데이터베이스 처리가 필요하면 Model을 통해 처리하고 그 결과를 반환받습니다.
- View는 자신의 로직 처리가 끝나면, Template을 사용하여 클라이언트에 전송할 HTML 파일을 생성합니다.
- View는 최종 결과로, HTML 파일을 클라이언트에게 보내 HTTP response 응답합니다.
라고 할 수 있다.
polls/views.py 에
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
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)
로 뷰를 추가했다.
뷰도 생겼으니 polls.urls 를
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
로 고쳐, 뷰와 연결해준다.
브라우저에 /polls/34/를 입력하면 detail() 함수를 호출해 "You're looking at question 34"라는 문장을 출력한다.
뷰 작성 심화
뷰는 데이터베이스의 기록를 읽을 수도 있고 Django나 Python에서 서드파티로 제공되는 템플릿 시스템을 사용할 수 있다.
(뷰는 PDF를 생성하거나, XML을 출력하거나, 실시간으로 ZIP 파일을 만들 수도 있다.)
위 '뷰 추가'에서는 뷰에서 templete의 역할까지 다 하고 있다. 이제 이를 분리해보자.
polls/templates/polls/index.html에
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
를 쓰고,
polls/views.py를
### 심화1 ###
from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request): #여기서 request가 어떻게 연결되는 것인지?
latest_question_list = Question.objects.order_by('-pub_date')[:5] #질문 개수
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
로 index 뷰를 수정했다.
이제 URLconf가 index함수를 부르면, polls/index.html 템플릿에 context를 전달한다.
request 는 HttpRequest 개체이다.
여기서 request가 어떻게 연결되는 것인지? 프론트에서 준 거.
템플릿에 context를 넣어 HttpResponse객체를 templete에 넘겨주는 구문은 흔하다.
따라서 Django는 render()이라는 단축기능을 제공한다.
### 심화2 ###
from django.shortcuts import render
from .models import Question
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)
더 이상 loader와 HttpResponse를 쓰지 않는다.
render() 함수는 request 객체를 첫번째 인수로 받고, 템플릿 이름을 두번째 인수로 받으며, context 객체를 선택적으로 받는다.
404 에러
polls/views.py에
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
를 작성한다. get_object_or_404() 함수는 Django 모델을 첫번째 인자로 받고, 몇개의 키워드 인수(pk)를 모델 관리자의 get() 함수에 넘긴다. 만약 객체가 존재하지 않을 경우(pk가 존재하지 않을 경우), Http404 예외가 발생한다.
여기서 pk가 없다 == 객체가 없다로 봐도 되는지? 네
템플릿 시스템 사용
polls/detail.html은
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %} #질문 개수 반복문
<li>{{ choice.choice_text }}</li> #질문 리스트로 출력
{% endfor %}
</ul>
으로 작성되어 있다.
하드 코딩이 뭔지? url 고정시켜 두는 것
URL의 이름공간
해당 프로젝트는 polls라는 앱 하나만을 가지고 진행했다. 하지만 여러개의 앱이 올 수 있기에 이를 구분하기 위해
URLconf에 namespace를 추가한다.
polls/urls.py 를
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
로 수정한다. name이라는 인자를 달아줬다.
이제, polls/index.html 템플릿의 기존 내용을
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
로 네이밍을 명확히 해준다.
Form
polls/detail.html를 수정해 템플릿에 HTML <form> 요소를
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
<fieldset>
<legend><h1>{{ question.question_text }}</h1></legend>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
</fieldset>
<input type="submit" value="Vote">
</form>
로 추가해준다.
for문을 돌며 데이터베이스에 저장된 선택지의 개수를 라디오 버튼으로 표현한다. 각 라디오 버튼의 name은 choice이며 이 중 하나를 선택하면 form요소는 서버에 choice = #id 을 post한다.
forloop.counter 는 for 태그가 반복한 횟수를 나타낸다.
내부 URL을 대상으로 하는 모든 POST 양식은 템플릿 태그를 사용해야 한다.{% csrf_token %}
polls/urls.py에
path('<int:question_id>/vote/', views.vote, name='vote'),
를 추가하고,
polls/views.py에
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from .models import Choice, Question
# ...
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
#선택된 choice의 ID를 문자열로 반환해 변수에 넣는다.
except (KeyError, Choice.DoesNotExist):
#choice가 없다면, request.POST['choice'] 는 KeyError가 일어나며
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
#에러메세지와 설문조사 폼을 다시 보여준다.
else: #제대로 선택이 되었다면,
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
#reverse()함수는 해당 뷰를 하드코딩하지 않고 가리키게 한다.
추가한다. 이제 어떤 이가 설문조사를 한 후에는 vote() 뷰는 설문조사 결과페이지로 리다이렉트한다.
결과페이지를 만들어보자.
polls/views.py에
from django.shortcuts import get_object_or_404, render
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
를 추가하고,
polls/results.html에
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
를 추가한다.
제네릭 뷰 사용
우리가 지금까지 작업했던 뷰는 URL에서 전달된 매개변수에 따라 데베에서 데이터를 가져오거나 템플릿을 로드하는 것이다. 이런 과정을 Django는 '제네릭 뷰'라는 시스템으로 지름길을 제공한다.
이는 일반적인 패턴을 추상화하여 앱을 작성할 때 Python코드를 작성하지 않도록 돕는다.
그러기 위해서는 3가지 단계가 필요하다.
- URLconf를 변환
- 불필요한 오래된보기 중 일부를 삭제
- Django의 제너릭 뷰를 기반으로 새로운 뷰를 도입
git ignore
[GIT] git 캐시 삭제
GIT 캐시를 삭제해야하는 이유?? 우리가 프로젝트를 만들고 gitignore을 하지않고 git에 add, commit을 해버릴 경우가 있다. 그러면 일단 git 자체에 쓰여진 상태이기 때문에 gitignore가 먹히지 않는다. 캐
gh402.tistory.com
git ignore 파일 확인
git status --ignored
특정 개수만큼의 커밋 기록을 제거
# 가장 최근의 커밋 기록을 1개 제거 (위와 동일)
git reset --hard HEAD~1
# 가장 최근의 커밋 기록을 2개 제거
git reset --hard HEAD~2
특정 커밋으로 복구
git reset --hard <commit id>
더 삽질한 건 밑에 정리해두었다^^
[Git] 커밋 기록 삭제 및 복구
CEOS 과제를 하던 중 데이터베이스 비밀번호가 적힌 파일을 gitingnore에 추가하지 않고 push 해버렸다. 첫 번째 시도(커밋 기록 삭제) 아래 사진처럼 .env(my_setting.py로 설정)을 따로 둔 뒤, settings.py에 i
jwkdevelop.tistory.com
Django REST Framework
웹 API를 구축할 수 있는 툴킷으로, Model 을 바탕으로 조건에 맞는 API를 개발할 수 있다.
pip install djangorestframework
pip install django-filter #Filtering support
mysite/settings.py을
INSTALLED_APPS = [
...
'product',
'rest_famework', #추가
]
로 바꿔준다.
Serializer는 queryset 과 model instance 같은 것들을 쉽게 JSON 또는 XML 의 데이터 형태로 렌더링 할 수 있게 해줍니다. 우리는 Product 모델을 serialize 해줘야 하기 때문에 ModelSerializer를 사용한다.
참조)
Django
The web framework for perfectionists with deadlines.
docs.djangoproject.com
Django - 기본적인 app 생성해 json 출력해 보기 (1)
아아주 기본적인, 내가 데이터를 전송해 입력하고 입력해둔 데이터를 출력하는 앱에 대해 작성하려고 한다. 어느 정도는 django 튜토리얼과 겹칠 것이다.
velog.io
장고(Django) - 마이그레이션(Migration)
마이그레이션(Migration)이란? 장고 공식 문서에서는 마이그레이션이 모델의 변경 내역을 DB *스키마에 적용시키는 장고의 방법이라고 설명하고 있습니다. 장고는 ORM을 사용하기 때문에 models.py와
tibetsandfox.tistory.com
django restful api 구현
settings.pyrest_framework와 user(app이름)를 추가해준다.일단 database에 저장하고 가져다 쓸 데이터의 schema를 만들어야한다. 기본적으로 schema안에는 무조건 primary key가 있어야 하기 때문에 간단하게 id를
velog.io
[Python] django 모델을 이용한 데이터베이스 처리
장고는 모델(Model)을 활용하여 데이터베이스를 처리합니다. sqlite 데이터베이스는 장고의 기본 데이터베이스입니다. 보통은 데이터베이스에 SQL 쿼리문을 이용하여 데이터를 조회하고 저장하고
yjshin.tistory.com