[Django] 장고 개념 공부 #part 1&2

seongbin·2024년 5월 15일

Django

목록 보기
1/2

개발 환경 설정 (window)

  1. python 설치
  2. python 가상 환경 설정하기

    pv -m venv project-name

  3. 가상환경 활성화

    project-name\Scripts\activate.bat (비활성화는 deactivate)

  4. Django 설치 (가상환경 실행 상태에서)

    py -m pip install Django

프로젝트 생성하기

django-admin startproject project-name-in (원하는 project-name 폴더 내부 && 가상환경)

vsc에서 폴더 열어보면 manage.py와 폴더 존재
manage.py는 장고 프로젝트를 터미널에서 조작할 수 있는 명령을 제공

생성한 프로젝트를 서버에서 실행하기 위해서는

python manage.py runserver
가상환경에서, mysite 폴더 내에서 실행되어야 함

APP 생성하기

(코드)
프로젝트는 하나의 웹사이트라고 생각 -> 여러개의 앱이 프로젝트를 구성한다고 생각

프로젝트 내 앱 생성하기 (polls란 앱을 생성)

python manage.py startapp polls

내가 입력하지 않은 url을 치고 들어가면 404 NOT FOUND error가 뜸 -> url 만들기

#polls/urls.py (새로 만들어줘야함)
#url이 비어서 주소가 들어왔을 때 view.index로 보내라 
from django.urls import path 
from . import views 

urlpatterns = [
    path('', views.index, name='index'),
]
#polls/views.py
#request가 들어왔으면 hello world를 출력하는 httpresponse 반환
from django.http import HttpResponse

def index(request):
    return HttpResponse("hello world")
#mysite/urls.py
#url로 polls/ , 즉 /뒤로 아무것도 안들어왔으면 polls.urls 로 보내라 
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('polls/',include('polls.urls')),
    path('admin/', admin.site.urls),
]

관계형 데이터베이스(RDB)

관계형 데이터베이스는 데이터를 행과 열로 이루어진 테이블의 형태로 구성하고, 테이블 간의 관계를 정의하는 데이터베이스

테이블이란 행과 열로 구성되어 있는 데이터의 집합을 의미함

열 : 테이블에 존재하는 필드 (테이블의 행에 저장될 내용들이 무엇인지 표시하는 기능)
primary key : 테이블의 각 행을 고유하기 식별할 수 있는 열
foregin key : 다른 테이블의 primary key를 참조하는 열

행 : 테이블에 저장된 데이터 레코드 (레코드: 하나의 단위로 표현되어지는 필드들의 집합)
각 레코드는 고유한 값을 가지는 primary key로 식별됨

모델 만들기

코드
모델은 database에서 값을 읽어내는 역할 (orm)

# polls/models.py
from django.db import models

# 모델과 테이블은 1대1 연결
# 모델을 테이블에 써주기 위한 마이그레이션이라는 것을 만들고, 그에 맞는 테이블을 만듬

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('data published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) #for indexing
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

모델을 만들기 위해서는 app으로 등록을 해주어야 하기 때문에 setting.py에 app을 추가해줌(INSTALLED_APPS)
(polls의 apps.py에 정의되어있는 config를 등록)

migration을 만들기 위해서는 가상환경에서

python manage.py makemigrations polls

실행될 SQL 문장을 살펴보기 위해서

python manage.py sqlmigrates polls 0001
id라는 것을 지정해주지 않았지만 id는 장고에서 자동으로 생성

실제로 테이블을 만들기 위해서는

python manage.py migrate

장고의 다양한 모델 필드

django_official model_field

Django에서 기본으로 제공하는 SQLite 데이터베이스 파일을 관리하기 위해서 사용하는 명령어 :
sqlite3 db.sqlite3

데이터베이스가 열린 상태에서 모든 테이블 목록을 보기 위한 명령어 :
.tables

테이블 목록 중 한 table의 여러 migration을 보기 위해서 사용하는 명령어 :
SELECT * From django_migrations;

테이블의 구조를 알고 싶다면 사용하는 명령어
.schema table_name

마이그레이션 롤백하기 위해 사용하는 명령어
python manage.py migrate polls 0001 (polls 0001 은 버전)

데이터베이스, 마이그레이션을 직접 관리하기 위한 명령어들임(db를 조작할 때는 백업하기, db에서 지운내용을 모델에서 지우기등 주의)

Admin 계정 생성 및 접속

관리자 계정을 생성하기 위해 가상환경에서 사용하는 명령어
python manage.py createsuperuser

이후 서버를 실행시켜서 /admin으로 들어가면 Django가 제공해주는 admin 페이지를 볼 수 있음
페이지에서 멤버들을 관리, 생성등을 할 수 있는 기능들이 존재

모델 등록하기(admin)

코드
관리자 페이지에서 멤버 이외에 question 등의 모델을 편리하게 관리하고 싶음

# polls/admin.py
from .models import *

admin.site.register(Question)
admin.site.register(Choice)

django shell 사용하기

django shell를 사용하기 위해서는

python manage.py shell

# shell 내부
# models.py 파일에 정의된 모든 모델 가져오기 (shell은 프로젝트 기준이기에)
>>> from polls.models import *
>>> Question

#모든 Question,Choice 오브젝트 가져오기
>>> Question.objects.all()
>>> Choice.objects.all()

#첫번째 Choice 오브젝트 가져오기
>>> choice = Choice.objects.all()[0]
>>> choice.id
>>> choice.choice_text
>>> choice.votes

#첫번째 Choice와 연결된 Question 가져오기
>>> choice.question
>>> choice.question.pub_date
>>> choice.question.id


#해당 Question과 연결되어 있는 모든 Choice 가져오기 
>>> question.choice_set.all()

Django shell에서 현재 시간 구하기

from django.utils import timezone
timezone.now()

Django shell - 레코드 생성

>>> from polls.models import *

#"커피 vs 녹차" 라는 내용의 새로운 Question 오브젝트를 생성하고 'q1'이라는 변수에 저장하기
>>> q1 = Question(question_text = "커피 vs 녹차")

#timezone을 활용하여 새로운 오브젝트 'q1'의 생성시각을 설정하기
>>> from django.utils import timezone
>>> q1.pub_date = timezone.now()

#새로운 Question 오브젝트 'q1'을 데이터베이스에 저장하기
>>> q1.save()

>>> q3 = Question(question_text = "abc")

#create() 메서드를 활용하여 q3와 연결된 새로운 Choice 오브젝트를 생성하고, choice_text 필드에 값을 넣어주기
>>> q3.choice_set.create(choice_text = "b")

#새로운 Choice 오브젝트를 생성하고 question 필드에 q3 값을 넣어 연결하기
>>> choice_c = Choice(choice_text='c', question=q3)

#새로운 Choice 오브젝트를 데이터베이스에 저장하기
>>> choice_c.save()

admin page가 아닌 shell에서 레코드를 생성하는 방법
shell 에서 만든 것들은 save()를 해줘야 하는 것을 기억

Django shell - 레코드 수정 및 삭제

>>> from polls.models import * #shell에서 import 해주는 것 기억하기

#Question 오브젝트 중 가장 마지막으로 만들어진 것을 가져오기
>>> q = Question.objects.last()

#해당 오브젝트의 question_text에 새로운 내용을 더해 수정하기
>>> q.question_text = q.question_text + '???'

#Choice 오브젝트 중 가장 마지막으로 만들어진 것을 가져오기
>>> choice = Question.objects.last()

#해당 오브젝트에 연결된 Question을 통해서 choice set을 가져오기
>>> choice.queston.choice_set.all()

#해당 오브젝트를 삭제하기
>>> choice.delete()

Django shell - 모델 필터링

queryset api

>>> from polls.models import *

#get() 메서드를 사용하여 조건에 해당하는 오브젝트를 필터링하기
>>> Question.objects.get(id=1)
>>> q = Question.objects.get(question_text__startswith='휴가를')
>>> Question.objects.get(pub_date__year=2023) #get으로 여러가지 오브젝트를 가져오려고 한다면 에러발생 (polls.models.Question.MultipleObjectsReturned: get() returned more than one Question) 
#get은 하나의 객체만 가져올 수 있음 -> 조건에 맞는 여러 개 오브젝트를 가져오기 위해서는 필터링 사용

#filter() 메서드를 사용하여 조건에 해당하는 오브젝트를 필터링하기
>>> Question.objects.filter(pub_date__year=2023)
>>> Question.objects.filter(pub_date__year=2023).count() # 개수 파악

#쿼리셋의 SQL 쿼리 살펴보기
#Question에서 choice로 갈때는 여러개가 존재하기에 QuerySet, 반대의 경우(choice에서 Question)는 하나의 경우 밖에 없기에 QuerySet이 아님
>>> Question.objects.filter(pub_date__year=2023).query
>>> print(Question.objects.filter(pub_date__year=2023).query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."pub_date" BETWEEN 2023-01-01 00:00:00 AND 2023-12-31 23:59:59.999999

>>> Question.objects.filter(question_text__startswith='휴가를').query
>>> print(Question.objects.filter(question_text__startswith='휴가를').query)(출력 결과 : SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."question_text" LIKE 휴가를% ESCAPE '\' )

>>> q = Question.objects.get(pk=1)
>>> q.choice_set.all()
>>> print(q.choice_set.all().query)(출력결과: SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."question_id" = 1)

#contains 연산자를 활용하여 오브젝트를 필터링하기
>>> Question.objects.filter(question_text__contains='휴가') #특정 문자가 들어있는 object를 반환함

>>> Choice.objects.all()
>>> Choice.objects.filter(votes__gt=0)

#해당 쿼리셋에 대한 SQL 쿼리를 생성하기
>>> Choice.objects.filter(votes__gt=0).query #votes__gt=0 은 votes수가 0보다 큰 것들을 반환
>>> print(Choice.objects.filter(votes__gt=0).query)(출력결과:SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."votes" > 0)

#정규표현식을 활용하여 조건에 해당하는 오브젝트들을 필터링하기 #regex, 매우 강력한 기능
>>> Question.objects.filter(question_text__regex=r'^휴가.*어디')
>>> print(Question.objects.filter(question_text__regex=r'^휴가.*어디').query)(출력결과:SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date", "polls_question"."owner_id" FROM "polls_question" WHERE "polls_question"."question_text" REGEXP ^휴가.*어디)

모델 관계기반 필터링

#Question의 question_text 필드 값이 '휴가'로 시작하는 모든 Choice 오브젝트를 필터링하기
>>> Choice.objects.filter(question__question_text__startswith='휴가')
# 모델명__모델필드__조건

#exclude() 메서드를 사용하여 question_text 필드 값이 '휴가'로 시작하는 모든 Choice 오브젝트를 제외하고 필터링하기
>>> Question.objects.exclude(question_text__startswith='휴가') #결과값은 startwith만 쓰는 것의 정반대(exclude)

모델 메소드

코드
모델 메소드를 활용해서 question이 표시되는 것에 기능을 추가하여 표현함
자주 쓰는 기능이라면 메소드를 만들어서 사용하기!

뷰(views)와 템플릿(Templates)

코드
urls.py에는 url에 따라서 어떤 뷰를 표시해야하는지, views.py에는 뷰에 어떤 것이 표시되는지 임의로 구현을 해논 상태 -> 모델을 뷰로 가져와서 웹사이트에서 보여지게 해야함

템플릿이란 서로 다른 정보를 일정한 형태로 표시하기 위해 재사용 가능한 파일

context는 템플릿에 전달될 데이터를 포함. 여기서 'first_question'은 템플릿에서 사용될 변수 이름
render는 요청된 템플릿 렌더링, 이때 context 전달

제어문

코드
템플릿에서는 index를 위해서 '.'을 사용해줘야함 ex) questions.0

템플릿에서 제어문등을 사용하기 위해서는 {% %} 사용

상세 페이지 만들기

코드
템플릿에서 모든 목록을 불러오기 위해 사용하는 all은 ()가 없음

urls.py에서 숫자를 view로 전달해주기 위해서는 <int:숫자를 받을 이름>를 사용해야함
views.py에서는 (request, 숫자를 받을 이름)으로 뷰를 만들어줄 수 있음

상세 페이지 링크 추가

코드
템플릿에서 링크를 가져올 때 urls.py에서 지정해둔 app_name과 name으로 링크 주소를 가져올 수 있음
(app_name이 앱 이름과 같을 필요는 없음, 꼭 polls가 아니어도 됨, app_name으로 정한 것을 주소로 가져올 때 {% url 'app_name:path' %}로 가져와야 한다는 것을 기억하기)

404 에러 처리하기

코드
404 에러는 서버 자체는 존재하지만 서버에서 요청한 것을 찾을 수 없을 때 발생
서버 입장에서는 없는 페이지 url을 받으면 500에러 발생 -> 404에러로 바꿔줘야함
(원래는 try-except으로 처리를 해야하지만 자주 발생하는 에러이기 때문에 get_object_or_404로 처리)

from django.http import Http404
try: 
	question = Question.objects.get(pk=question_id)
except Question.DoesNotExist: #예외는 shell에서 확인 가능
	raise Http404("Question does not exist") #404 page에서 확인할 수 있는 문구 설정 가능

form

코드
웹에서 form을 받아서 서버에서 처리를 하기 위해 template에서 <form> 사용

#templates/.../detail.html
#(crsf_token으로 토큰을 임의 설정)
#{% if error_message %} 로 에러메세지가 있을 때 처리

view.py에서는 try-except문으로 아무것도 투표하지 않았을 때의 예외 처리
POST로 choice를 받으면 selected_choice 변수로 받아서 투표값 +1
(저장 save() 꼭 해주기!)
투표하면 다른 페이지로 보내야 하는데 이를 위해서 HttpResponseRedirect(reverse('주소'))를 사용

에러 방어하기

코드
웹을 사용하다 보면 많은 에러가 발생할 수 있음
좋은 웹을 만들기 위해서는 상상력을 발휘해서 갖가지 에러를 다 예외처리 해두어야 함

특히 서버가 여러개라면 받아들이고 연산해서 DB에 저장할 때, 싱크 오류가 생겨 제대로 동작하지 않을 수 있음 (DB는 하나)
->votes를 올리는 연산 같은 것들을 DB에서 값을 읽어 진행

from django.db.models import F

selected_choice.votes = F('votes') + 1

다양한 페이지

결과 조회 페이지 코드

admin 편집 페이지 코드

admin페이지를 수정하기 위해서는 admin.py에 코드를 작성
새로운 클래스를 만들어 기존의 admin을 상속받음
fieldsets을 이용해 필드를 분리하고, 자세히 보고 싶지 않다면 'classes':['collapse']도 추가해주기
수정할 것이 아니라면 readonly_fields로 읽기 전용 필드로 지정

admin 목록 페이지 코드

보여줄 목록을 정하기 위해 list_display 사용
검색기능을 제공하기 위해 list_filter, search_fields 사용

0개의 댓글