- 뷰 레이어 주요 함수 ============
1.1 HttpResponse
HttpResponse는 문자열 데이터를 반환하는 데 사용됩니다. 예를 들어, 브라우저에 직접 텍스트 정보를 반환해야 할 때 사용할 수 있습니다.
코드 예시:
(1) user 앱의 views.py에서 해당 모듈을 가져오고 login 함수 정의
(2) urls.py에서 login을 가져오고 해당 URL 매핑 추가
(3) 장고 프로젝트 실행
1.2 render
render 함수는 HTML 파일을 반환하고 템플릿 문법을 지원하며 동적 페이지 렌더링을 구현할 수 있습니다. 이를 통해 HTML 템플릿에 데이터를 전달하고 템플릿 내에서 이 데이터를 사용하여 최종 HTML 페이지를 생성할 수 있습니다.
코드 예시:
(1) user 앱 아래에 templates 폴더를 생성하고 index.html 파일 생성
(2) user의 views.py에서 index 함수 정의
(3) urls.py에서 함수를 가져오고 URL 매핑 추가
(4) 프로젝트 실행
(5) 보충 학습 내용
[5.1] 뷰 함수 정의
def index(request):
# 페이지 객체 반환
return render(request, 'index.html')
각 뷰 함수는 반드시 request라는 위치 매개변수를 가져야 합니다
render(request, template_name, context=None, content_type=None, status=None, using=None)
request: 현재 요청 객체 template_name: 현재 렌더링할 템플릿 파일 이름, templates 폴더 내에 위치 context: jinja2 문법으로 프론트엔드에 데이터 전달 render({"key":"value"}) === context content_type: 응답 문서 유형 정의 text/html / json status: 응답 상태 코드, 기본값은 200 OK using: 사용할 템플릿 엔진 지정 (기본값 사용 - 모두 선택 사항)
[5.2] 템플릿 파일 정의
프로젝트 루트 디렉토리에 static 폴더를 수동으로 생성하여(CSS, JavaScript, 이미지 등) 정적 파일을 저장할 수 있습니다
템플릿 파일 경로: user/templates/index.html
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>제목</title>
<script src="../../static/jquery.min.js"></script>
<link href="../../static/bootstrap.min.css" rel="stylesheet">
<script src="../../static/bootstrap.min.js"></script>
</head>
<body>
<h1>이것은 index 페이지입니다</h1>
</body>
</html>
템플릿 파일에서 정적 파일을 가져올 때, 위 코드의 점(.)은 해당 템플릿 파일을 나타내며, 점을 하나 추가할 때마다 상위 디렉토리로 한 단계 올라갑니다. 네 개의 점은 프로젝트 루트 디렉토리를 의미합니다
1.3 redirect
redirect는 리다이렉션을 구현하여 사용자를 한 페이지에서 다른 페이지로 이동시킬 수 있습니다. 괄호 안에는 다른 웹사이트의 전체 URL을 작성할 수도 있고, 자신의 웹사이트의 특정 라우터 접미사를 작성할 수도 있습니다.
코드 예시:
(1) 함수 정의
(2) 해당 URL 매핑 추가
(3) 프로젝트 실행
브라우저에 register를 입력하면 자동으로 login으로 리다이렉션됩니다
(4) 보충 학습 내용
[4.1] 매개변수: 대상 주소
redirect(to, *args, permanent=False, **kwargs)
to: 리다이렉션하려는 대상 주소
상세 주소 작성: https://www.ox.ac.uk, 해당 주소로 리다이렉션됩니다
def register(request):
print('환영합니다! 등록 함수입니다')
# 라우트 리다이렉션
return redirect('https://www.ox.ac.uk/')
/index/와 같이 간단하게 작성하면 포트 뒤에 자동으로 연결되며, 127.0.0.1:8080/register/를 127.0.0.1:8080/index/로 리다이렉션합니다
def register(request):
print('환영합니다! 등록 함수입니다')
# 라우트 리다이렉션
return redirect('/index/')
index/와 같이 작성하면 127.0.0.1:8080/register/를 기준으로 index가 연결됩니다
127.0.0.1:8080/register/index/
def register(request):
print('환영합니다! 등록 함수입니다')
# 라우트 리다이렉션
return redirect('index/')
[4.2] 임시 리다이렉션, 영구 리다이렉션
임시 리다이렉션(응답 상태 코드: 302)과 영구 리다이렉션(응답 상태 코드: 301)은 일반 사용자에게는 큰 차이가 없으며, 주로 검색 엔진 로봇을 대상으로 합니다. A 페이지가 임시로 B 페이지로 리다이렉션되면, 검색 엔진은 A 페이지를 수집합니다. # 뒤로 가기로 돌아갈 수 있습니다 A 페이지가 영구적으로 B 페이지로 리다이렉션되면, 검색 엔진은 B 페이지를 수집합니다. # 뒤로 가기로 돌아갈 수 없습니다
- 정적 파일 =======
2.1 개념
장고 프로젝트에는 기본적으로 static이라는 이름의 폴더가 존재하지 않습니다. 일반적으로 정적 파일(CSS, JavaScript, 이미지 img, 비디오 video, bootstrap 프레임워크 등)은 수동으로 static 폴더를 생성하여 저장하며, 이 폴더는 프로젝트 루트 디렉토리에 위치할 수 있습니다.
2.2 정적 파일 구성
[1] 정적 파일 구문 없이 파일 가져오기
1.2 render 참조
<script src="../../static/jquery.min.js"></script>
<link href="../../static/bootstrap.min.css" rel="stylesheet">
<script src="../../static/bootstrap.min.js"></script>
../를 두 번 사용했습니다 ### [2] 정적 파일 구문으로 파일 가져오기 -- 백엔드
settings.py에는 기본적으로 STATIC_URL = '/static/'이 설정되어 있으며, 이 구성은 정적 파일에 접근하기 위한 토큰과 유사합니다
settings.py에 매개변수를 추가합니다: STATICFILES_DIRS = ["static"], 이 static 디렉토리는 루트 디렉토리에 있습니다
백엔드 구성 후, 전체 경로를 통해 접근할 수 있습니다
[3] 프론트엔드
프론트엔드에서는 구문을 사용하여 경로를 간단하게 작성할 수 있습니다
{% load static %}
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>제목</title>
<script src="{% static 'plugins/jQuery.js'%}"></script>
</head>
<body>
<p>인덱스 페이지</p>
</body>
</html>
- 요청 객체 ============
3.1 GET/POST 요청 전송
[1] GET 요청 전송
static의 plugins 디렉토리에 bootstrap, jquery 파일을 추가합니다
user 앱의 templates 폴더에 login.html 파일을 생성하고, bootstrap, jquery를 가져옵니다
views.py에 login 함수를 정의하고 login.html 페이지 객체를 반환합니다
urls.py에 해당 URL 매핑을 추가합니다
form 예제를 사용하여 완전한 login.html 코드를 작성합니다
{% load static %}
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>제목</title>
<script src="{% static 'plugins/jQuery.js' %}"></script>
<script src="{% static 'plugins/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static 'plugins/bootstrap.css' %}">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">로그인 페이지에 오신 것을 환영합니다</h1>
{# action : 데이터 전송 주소, 작성하지 않으면 기본값으로 현재 주소에 전송, 작성하면 지정된 주소로 전송 #}
{# method : 요청 방식, get, post #}
<form action="" method="get">
<div class="form-group">
<label for="inputUsername">사용자 이름</label>
<input type="text" class="form-control" id="inputUsername" name="username" placeholder="사용자 이름">
</div>
<div class="form-group">
<label for="inputPassword">비밀번호</label>
<input type="password" class="form-control" id="inputPassword" name="password"
placeholder="비밀번호">
</div>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox1" name="hobby" value="music"> 음악
</label>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox2" name="hobby" value="run"> 달리기
</label>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox3" name="hobby" value="dance"> 춤
</label>
<br>
<button type="submit" class="btn btn-default">제출</button>
</form>
</div>
</div>
</div>
</body>
</html>
코드 보기위 코드는 GET 요청으로, 요청 매개변수가 요청 경로에 포함됩니다
[2] POST 요청 전송
요청이 POST인 경우:
브라우저에서 일시적으로 POST로 변경합니다
POST 요청 경로에는 매개변수가 없습니다
[3] POST 요청 오류 해결 방법
위 오류가 발생하면, settings.py의 MIDDLEWARE에서 csrf 관련 부분을 주석 처리합니다
주석 처리 후 POST 요청으로 사용자 이름과 비밀번호를 입력하여 제출하면 더 이상 오류가 발생하지 않습니다
3.2 요청 객체 속성
[1] request 속성 확인
views.py에서 request의 속성을 출력합니다
브라우저에서 login을 트리거할 때:
[2] 특정 요청 정보 확인
view.py에서 request의 method를 출력합니다
프론트엔드에서 login에 접속하여 login 함수가 트리거될 때, 해당 메서드가 출력됩니다
[3] GET 요청 본문 매개변수 가져오기
login.html 코드:
{% load static %}
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>제목</title>
<script src="{% static 'plugins/jQuery.js' %}"></script>
<script src="{% static 'plugins/bootstrap.js' %}"></script>
<link rel="stylesheet" href="{% static 'plugins/bootstrap.css' %}">
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">로그인 페이지에 오신 것을 환영합니다</h1>
{# action : 데이터 전송 주소, 작성하지 않으면 기본값으로 현재 주소에 전송, 작성하면 지정된 주소로 전송 #}
{# method : 요청 방식, get, post #}
<form action="" method="get">
<div class="form-group">
<label for="inputUsername">사용자 이름</label>
<input type="text" class="form-control" id="inputUsername" name="username" placeholder="사용자 이름">
</div>
<div class="form-group">
<label for="inputPassword">비밀번호</label>
<input type="password" class="form-control" id="inputPassword" name="password"
placeholder="비밀번호">
</div>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox1" name="hobby" value="music"> 음악
</label>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox2" name="hobby" value="run"> 달리기
</label>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox3" name="hobby" value="dance"> 춤
</label>
<br>
<button type="submit" class="btn btn-default">제출</button>
</form>
</div>
</div>
</div>
</body>
</html>
하나의 키에 하나의 값인 경우:
프론트엔드에서 데이터를 제출하면 요청 본문 매개변수를 가져올 수 있습니다
하나의 키에 여러 값인 경우:
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox1" name="hobby" value="music"> 음악
</label>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox2" name="hobby" value="run"> 달리기
</label>
<label class="checkbox-inline">
<input type="checkbox" id="inlineCheckbox3" name="hobby" value="dance"> 춤
</label>
하나의 키에 여러 값인 경우, get은 목록의 마지막 요소만 가져오며, getlist는 목록을 직접 가져옵니다(다중 선택)
- 장고와 MySQL 데이터베이스 연결 ===================
4.1 기본 데이터베이스 확인
settings.py에서 데이터베이스를 구성하는 항목이 있습니다
장고는 기본적으로 sqlite3 데이터베이스를 사용합니다
4.2 데이터베이스 지정
[1] MySQL 데이터베이스로 변경
sqlite3 데이터베이스를 MySQL 데이터베이스로 변경합니다
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # MySQL 데이터베이스 엔진 사용
'NAME': "django_db", # 데이터베이스 이름
"USER": "root", # 실제 데이터베이스 사용자 이름
"PASSWORD": "123456789", # 실제 데이터베이스 비밀번호
"HOST": "127.0.0.1", # 데이터베이스 IP
"PORT": 3306,
"CHARSET": "utf8mb4", # 데이터베이스 인코딩
}
}
변경 후 장고 프로젝트를 시작하면 오류가 발생합니다
[2] 위 오류 해결 방법
(1) 원숭이 패치(py 모니키 패치)
pymsql 설치: pip install pymysql (이미 설치된 경우 이 단계는 건너뜁니다)
init.py에 다음 코드를 추가합니다
import pymysql
pymysql.install_as_MySQLdb()
프로젝트를 다시 시작하면 라이브러리를 찾을 수 없다는 오류가 발생합니다
데이터베이스를 생성하고 장고 프로젝트를 다시 시작하면 더 이상 오류가 발생하지 않습니다
(2) 서드 파티 모듈 설치
pip install mysqlclient
macOS에서는 pip로 mysqlclient를 설치할 수 없습니다
Windows에 설치 후 장고 프로젝트를 다시 시작하면 더 이상 오류가 발생하지 않습니다
- ORM 소개 ========
5.1 개념
[1] ORM이란 무엇인가
ORM은 객체 관계형 데이터베이스 간의 매핑 기술로, 주로 다음 세 가지 측면의 기능을 구현합니다:
데이터베이스의 테이블을 Python의 클래스로 매핑
데이터베이스의 필드를 Python의 속성으로 매핑
데이터베이스의 레코드를 Python의 인스턴스로 매핑
ORM의 주요 장점은 개발자가 반복적인 SQL 문 작성 시간과 노동력을 줄이고, SQL 문 조정 및 변경으로 인한 오류를 줄일 수 있다는 점입니다.
[2] 장고 ORM의 장점
다른 ORM 프레임워크에 비해 장고 ORM은 다음과 같은 장점을 가집니다: 사용하기 쉬움: 장고 ORM API는 매우 간단하여 배우고 사용하기 쉽습니다. 풍부한 API: 장고 ORM은 일반적인 데이터베이스 작업(삽입, 삭제, 수정, 조회 등)을 수행하는 풍부한 API를 제공하며, 고급 쿼리와 집계 쿼리 등도 지원합니다. 좋은 확장성: 장고 ORM은 Django REST framework, Django-Oscar 등 다른 서드 파티 라이브러리와 원활하게 통합될 수 있습니다. 자동 매핑: 장고 ORM은 자동 매핑을 지원하며, 개발자는 데이터베이스 테이블 구조만 정의하면 관련 Python 클래스를 자동으로 생성할 수 있어 개발 과정의 반복 코드 양을 줄일 수 있습니다.
5.2 ORM 테이블 작업
[1] 모델 테이블 정의
개념에 따라 Python에서 클래스를 정의하여 데이터베이스의 테이블로 변환합니다
앱 -- models.py 파일에 모델 테이블을 정의합니다
from django.db import models
# 모델을 여기에 정의합니다
class Student(models.Model): # 학생 클래스(학생 테이블) 정의, 이 파일의 클래스는 반드시 models.Model을 상속해야 합니다
name = models.CharField(max_length=32) # 데이터베이스의 name varchar(32)와 유사
age = models.IntegerField() # 데이터베이스의 age int()와 유사
SQL 문에서는 기본 키 id가 생성되며, 장고에서는 settings.py에 기본적으로 id 필드가 구성됩니다
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
[2] 데이터베이스 마이그레이션
Python 코드로 작성된 데이터베이스 구조를 SQL 문 구조로 변환합니다
방법 1: 명령줄
(1) 마이그레이션 파일 생성
이 단계는 작업을 migrations 폴더에 기록하며, 폴더에 관련 파일이 있지만 데이터베이스와 동기화되지 않으므로 데이터베이스에는 데이터가 없습니다
명령을 실행하려면 manage.py가 있는 경로에서 수행해야 합니다
0001_initial.py 파일이 생성됩니다
settings.py에는 기본적으로 데이터베이스 id가 구성됩니다
(2) 마이그레이션 작업 시작
작업을 데이터베이스와 동기화합니다
방법 2: 작업 작업
Tools -> run manage.py task -> makemigrations -> migrate
새로 생성된 club 테이블이 데이터베이스에 동기화됩니다
[3] 테이블 이름
위에서 Python 클래스를 데이터베이스로 동기화한 후 테이블 이름은: 앱이름_클래스명
Python 클래스에 Meta 클래스를 구성하면 데이터베이스의 테이블 이름은 Meta 클래스에서 정의한 대로 됩니다
class Student(models.Model): # 학생 클래스(학생 테이블) 정의, 이 파일의 클래스는 반드시 models.Model을 상속해야 합니다
name = models.CharField(max_length=32) # 데이터베이스의 name varchar(32)와 유사
age = models.IntegerField() # 데이터베이스의 age int()와 유사
class Meta:
db_table = "student10" # db_table은 고정된 작성 방식
5.3 ORM 필드 작업
[1] 필드 추가
grade 필드를 추가합니다
class Student(models.Model): # 학생 클래스(학생 테이블) 정의, 이 파일의 클래스는 반드시 models.Model을 상속해야 합니다
name = models.CharField(max_length=32) # 데이터베이스의 name varchar(32)와 유사
age = models.IntegerField() # 데이터베이스의 age int()와 유사
grade = models.IntegerField()
데이터베이스를 다시 마이그레이션합니다: makemigrations -- migrate
다음과 같은 메시지가 나타납니다:
- 현재 기본값을 제공합니다 (이 열의 모든 기존 행에 빈 값을 설정)
- 종료하여 models.py에 기본값을 추가하도록 합니다
2를 선택하여 종료하고 grade에 기본값을 설정합니다
참고: 문자열 유형의 기본값은 default="", 숫자 유형의 기본값은 default=None
class Student(models.Model): # 학생 클래스(학생 테이블) 정의, 이 파일의 클래스는 반드시 models.Model을 상속해야 합니다
name = models.CharField(max_length=32) # 데이터베이스의 name varchar(32)와 유사
age = models.IntegerField() # 데이터베이스의 age int()와 유사
grade = models.IntegerField(default='')
다시 마이그레이션: makemigrations -- migrate
[2] 필드 삭제
models.py에서 삭제할 필드를 삭제하고 다시 마이그레이션합니다: makemigrations -- migrate
예시: 위 데이터 테이블에서 c 필드 삭제
class Student(models.Model): # 학생 클래스(학생 테이블) 정의, 이 파일의 클래스는 반드시 models.Model을 상속해야 합니다
name = models.CharField(max_length=32) # 데이터베이스의 name varchar(32)와 유사
age = models.IntegerField() # 데이터베이스의 age int()와 유사
a = models.IntegerField(default=None)
b = models.CharField(max_length=32, default="")
# c = models.IntegerField(default=None)
[3] 제약 조건
| 의미 | SQL 문 | ORM |
|---|---|---|
| 비어 있을 수 있음 | null | null=True |
| 비어 있을 수 없음 | not null | null=False |
5.4 장고 임시 환경 생성
장고에서 모델 테이블을 작동시키려면 반드시 장고를 시작해야 합니다
모델 테이블 작동은 뷰 함수 내에서 수행해야 합니다
증감 삭제 검색 데이터 작업을 편리하게 테스트하기 위해 다음 단계로 장고 임시 환경을 생성할 수 있습니다
첫 번째: 장고 프로젝트에서 scripts 디렉토리를 정의하여 사용자 정의 스크립트를 저장하고, 이 디렉토리에 증감 삭제 검색 데이터를 테스트할 py 파일을 생성합니다
두 번째: manage.py 파일을 열고 main 함수의 첫 번째 줄 코드를 복사합니다 (프로젝트 이름이 다르면 매개변수가 다름) os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj2.settings')
이 줄 코드를 테스트 파일의 main 진입점 아래에 배치합니다
세 번째: django 모듈을 가져옵니다
네 번째: django 모델 테이블을 가져옵니다
다섯 번째: django 시작 명령을 실행합니다
여섯 번째: 모델 테이블을 작동시킵니다
5.5 ORM 데이터 작업
[1] 데이터 추가
5.4의 임시 환경을 사용하여 테스트합니다
방법 1 (일반적으로 사용됨):
모델테이블.objects.create(필드명=필드값)
장고에서 데이터베이스의 필드가 문자열 유형인 경우, 생성 문에서 문자열 유형에 할당된 숫자 유형을 강제 변환합니다
방법 2 (일반적으로 사용되지 않음):
모델테이블(필드명=필드값)
[2] 데이터 조회
(1) 문법
현재 테이블의 모든 데이터 가져오기: 모델테이블명.objects.all()
지정된 조건으로 데이터 필터링: 방법 1: 모델테이블명.objects.get(필터링필드명=필터링필드값) 방법 2: 모델테이블명.objects.filter(필터링필드명=필터링필드값)
지정된 조건의 데이터 제외: 모델테이블명.objects.exclude(필터링필드명=필터링필드값)
(2) 코드
all 메서드로 모든 데이터 가져오기
import os
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj2.settings')
django.setup()
from user.models import Student
result = Student.objects.all()
print(result) # 반환값은 QuerySet 객체이며, 목록에는 각 레코드가 저장됩니다
student_one = result[0]
print(student_one, type(student_one)) # Student object (1) <class 'user.models.Student'>
print(student_one.name)
print(student_one.age)
get 메서드는 매개변수가 존재하면 가져오고, 존재하지 않으면 오류를 발생시키며, 1개 이상이면 오류를 발생시킵니다
student_one = Student.objects.get(id=1) # 존재하면 가져옵니다
print(student_one.name)
student_two = Student.objects.get(id=2) # 존재하지 않으면 오류 발생
student_three = Student.objects.get(name='avril') # 1개 이상이면 오류 발생
filter 메서드는 매개변수가 존재하면 가져오고, 존재하지 않아도 오류를 발생시키지 않으며, 1개 이여도 오류를 발생시키지 않습니다
import os
import django
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dj2.settings')
django.setup()
from user.models import Student
result = Student.objects.filter(id=1) # 존재하면 가져옵니다
print(result)
print(result[0].name)
result = Student.objects.filter(name='avril').first() # 첫 번째 일치하는 레코드 가져오기
print(result)
print(result.id)
result = Student.objects.filter(name='avril').last() # 마지막 일치하는 레코드 가져오기
print(result)
print(result.id)
result = Student.objects.filter(id=20) # 존재하지 않아도 오류 발생하지 않음
print(result)
result = Student.objects.filter(name='avril') # 1개 이여도 오류 발생하지 않음
print(result)
exclude는 조건에 맞는 레코드를 제외합니다
result = Student.objects.exclude(id=14) # id가 14인 레코드를 제외합니다
print(result)
[3] 데이터 삭제
방법 1: 필터링 후 직접 삭제
모델테이블.objects.filter(속성명=속성값).delete()
괄호 필터 매개변수에 여러 레코드가 일치하면 모든 일치하는 레코드가 삭제됩니다
방법 2: 먼저 필터링하여 객체를 가져온 후 객체 삭제
obj = Student.objects.get(id=15)
obj.delete()
[4] 데이터 수정
방법 1: 쿼리하여 직접 수정
모델테이블.objects.filter(속성명=속성값).update(새로운 매개변수)
result = Student.objects.filter(id=4).update(name='ronaldo')
print(result)
result2 = Student.objects.filter(name='avril').update(age=19)
print(result2)
반환값은 수정된 레코드 수입니다
방법 2: 먼저 쿼리하여 객체를 가져온 후 객체 수정
obj = Student.objects.get(id=4)
obj.name = 'cristiano'
obj.save() # save() 저장은 생략할 수 없습니다