문제

https://school.programmers.co.kr/learn/courses/30/lessons/1844

 

풀이

그래프 형식의 문제이니만큼 그래프 탐색을 이용했고, BFS로 풀었습니다. 

 

우선 BFS에 필요한 큐를 구현하기 위해서 collections 라이브러리의 deque 자료구조를 사용했습니다.

 

일반 리스트로도 pop(0) 메소드를 통해 큐의 역할을 구현할 수는 있지만 이 경우 맨 앞의 원소를 제거할 때는 O(n) 시간이 걸린다는 단점이 있어서 deque() 라이브러리를 사용했습니다. 

 

from collections import deque

def solution(maps):
    
    m = len(maps)
    n = len(maps[0])
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]
    
    def bfs(start_x, start_y, end_x, end_y):
    	# 거리 정보를 저장하는 배열. 시작점에서 닿을 수 없는 경우 -1의 값을 가짐
        dist = [[-1] * n for _ in range(m)]
        Q = deque([(start_x, start_y)])
        dist[start_x][start_y] = 1
        
        while Q:
            cur_x, cur_y = Q.popleft()
            for k in range(4):
                nx = cur_x + dx[k]
                ny = cur_y + dy[k]
                
                # 배열 인덱스를 벗어나는 경우
                if nx < 0 or nx >= m or ny < 0 or ny >= n:
                    continue
                    
                # 벽인 경우
                elif maps[nx][ny] == 0:
                    continue
                    
                # 한 번도 방문하지 않은 경우만 큐에 추가
                # 최단거리로 방문하려면 같은 블럭을 두 번 이상 방문하는 일은 없어야 한다.
                if dist[nx][ny] == -1:
                    dist[nx][ny] = dist[cur_x][cur_y] + 1
                    Q.append((nx, ny))
                    
        return dist[end_x][end_y]
    
    return bfs(0, 0, m-1, n-1)

 

문제

https://school.programmers.co.kr/learn/courses/30/lessons/258705

 

참고한 가이드

카카오 기술 블로그에 나온 풀이 방법을 참고했습니다.

해당 블로그의 5번 문제에 풀이 과정이 잘 설명되어 있으니 궁금하신 분은 참고하시면 좋을 것 같습니다. 

https://tech.kakao.com/2023/12/27/2024-coding-test-winter-internship/

 

풀이

해당 풀이에서는 DP(dynamic programming) 방법을 설명해주셔서 배열을 선언한 뒤 각 인덱스에 점화식을 적용하는 방식으로 풀었습니다. 

 

def solution(n, tops):
    a = [0] * (n+1)
    b = [0] * (n+1)
    a[0] = 0
    b[0] = 1
    for i in range(1, len(a)):
        a[i] = (a[i-1] + b[i-1]) % 10007
        if tops[i-1] == 1:
            b[i] = (2 * a[i-1] + 3 * b[i-1]) % 10007
        else:
            b[i] = (a[i-1] + 2 * b[i-1]) % 10007
    answer = (a[n] + b[n]) % 10007
    return answer

 

⚠️

아래 공식문서를 읽고 인증 관련된 내용 중 일부만 발췌해서 정리했습니다.

자세한 내용이 궁금하시다면 공식문서를 참고하시는 것을 추천드립니다. 

https://docs.djangoproject.com/en/5.0/topics/auth/customizing/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

✅custom user model 설정하기

 

settings.py 파일의 AUTH_USER_MODEL 변수의 값을 "app이름.Model이름" 으로 설정하자. 

AUTH_USER_MODEL = "account.CustomUser"

 

또한 공식문서에서는 인증 모델의 정보에 접근할 때는 쿼리셋을 작성할 때 흔히 사용하는 방식(CustomUser.objects....) 보다는 django.contrib.auth 모듈의 get_user_model() 사용을 권장한다. 

 

이유는 다른 auth user model을 사용하는 다른 프로젝트에서 코드를 사용할 때 충돌이 날 수 있어서라고 한다. 

 

확장성 있는 코드를 작성하고 싶다면 공식문서대로 하는 것이 좋겠고, 다른 프로젝트에서 코드를 사용할 일이 없다면 크게 상관없지 않을까 싶다. 

 

✅인증 정보 구분하기: profile model, proxy model

공식 문서에서는 인증 관련 정보는 user 모델에, 유저와 관련된 그 외 다른 정보는 다른 모델(profile model)에 저장하는 것을 권장한다. 

 

예를 들어 profile model이 Employee라고 하면, Employee와 User 모델의 관계를 1:1 또는 외래키로 설정하는 것이다. 

class Employee(models.Model):
	user = models.OneToOneField(User)
	...

 

반면 인증 관련 정보 외에 추가로 정보를 저장할 필요는 없지만 User 모델이 동작하는 방식을 바꾸고 싶다면 proxy model을 사용하는 것도 좋다. 

 

프록시 모델(proxy model)을 사용하면 유저 모델에서 사용 가능한 메소드나 쿼리셋의 정렬 등을 유저 모델과 다르게 설정할 수 있다. 

class Member(CustomUser):

	class Meta:
		proxy = True

 

✅User model Customizing

공식문서에서는 유저 모델을 정의할 때 django.contrib.auth.models 모듈의 AbstractBaseUser를 상속해서 사용하는 것을 권장한다. 

 

AbstractUser는 장고의 기본 유저 모델인 만큼 인증에서 사용할 수 있는 여러 메소드가 정의되어 있기 때문이다. 

 

물론 AbstractUser를 상속하지 않고도 유저 모델을 새로 정의해서 사용하는 것도 가능하다. 

 

하지만 이 경우 AbstractUser에 있던 기본 메소드들을 따로 정의해 주어야 하기 때문에 불편할 수도 있다. 

 

AbstractUser 모델을 상속해서 커스터마이징 할 경우, 다음과 같은 필드를 설정할 때는 주의해야 한다. 

 

USERNAME_FIELD

각 유저를 unique하게 인식하기 위해 필요한 필드이다. 이 값으로 선언한 필드는 반드시 unique 해야 한다. 

 

REQUIRED_FIELDS

createsuperuser 커맨드를 호출할 때는 이 필드들의 값을 모두 입력해야 한다. 이 필드값들은 unique 할 필요는 없지만 blank=False 이어야 한다. 

class CustomUser(AbstractUser):
	...
	email = models.CharField(max_length=40, unique=True)
    
	USERNAME_FIELD = "email"
	REQUIRED_FIELDS = ["email", "address"]

 

☑️blank와 null의 차이

null은 DB에 값을 저장할 때 null을 허용할지를 결정하고, blank은 DB와는 관련 없는 validation의 영역이다. null=True이어도 blank=False라면 DB에 넣을 값을 검사할 때 허용되지 않는다. 

 

그 외에도 AbstractBaseUser에는 세션이나 비밀번호 설정 등과 관련한 여러 메소드를 제공한다. 

 

is_authenticated

요청을 한 유저가 인증된 유저인지를 boolean 값으로 리턴한다. 

다만 이 값이 True라고 해도 이 유저가 어떤 특정 권한이 있거나 이 유저에 대한 세션이 있는지는 보장하지 않는다. 

 

set_password()

장고에서는 유저의 비밀번호 값을 그대로 저장하지 않고 암호화하여 저장하기 때문에 password의 값을 그대로 바꾸면 로그인이 되지 않는다. 

그 대신 set_password() 함수를 사용하면 해당 값이 암호화되어 저장된다. 

 

set_unusable_password()

장고에서 비밀번호를 사용하지 않을 때도 이 메소드를 사용하면 좋다. 

비밀번호에 공백 값을 넣는 것과 비밀번호를 사용하지 않는 것은 다른데, 이 메소드는 후자일 경우에 사용하자. 

 

get_session_auth_hash()

유저의 비밀번호를 HMAC 방식으로 암호화한 값을 리턴한다. (정확히 어떤 데 쓰이는지는 아직 모르겠다...)

 

 

☑️AbstractUser

만약 유저 모델의 특성을 거의 바꿀 것 같지 않다면 AbstractBaseUser를 상속한 AbstractUser 모델을 그대로 사용하는 것도 가능하다. 

 

이 모델에서는 USERNAME_FIELDREQUIRED_FIELDS 옵션도 이미 정의되어 있다.

 

AbstractBaseUser의 설정을 대부분 따르되 그 중 일부만 변경하고 싶다면 AbstractUser를 사용하면 코드를 간결하게 작성할 수 있다. 

class CustomUser(AbstractUser):
	pass

 

✅User manager Customizing

기본적으로, 각 모델에는 objects라는 이름을 가진 manager 클래스가 있다. 

 

이 manager 클래스는 모델이 어떤 쿼리를 통해서 데이터베이스와 어떻게 상호작용할지를 결정하는 일종의 인터페이스 역할을 한다. 

 

 

☑️BaseManager

 

유저 모델의 manager 클래스는 django.contrib.auth.models 모듈의 BaseManager 클래스를 상속한다. 

 

get_by_natural_key()

BaseManager에서 유저를 찾는 데 사용하는 메소드이다. 

createsuperuser 등의 커맨드에서도 호출된다. 

유저에서 USERNAME_FIELD로 선언한 필드의 값을 사용해서 유저를 찾는다.

 

 

☑️CustomManager

 

이 클래스를 상속해서 커스텀 manager 클래스를 만들고 싶다면 두 가지 메소드를 재정의해야 한다. 

 

create_user()

유저를 생성할 때 호출되는 메소드이다.

유저 모델에서 USERNAME_FIELDREQUIRED_FIELDS로 선언한 필드들은 모두 매개변수로 받아야 한다. 

 

create_superuser()

createsuperuser 커맨드를 사용할 때 호출된다. 

마찬가지로 USERNAME_FIELDREQUIRED_FIELDS로 선언한 필드들은 모두 매개변수로 받아야 한다. 

 

 

참고한 포스트

https://docs.djangoproject.com/en/5.0/ref/models/fields/#:~:text=null%20is%20purely%20database%2Drelated,the%20field%20will%20be%20required.

https://docs.djangoproject.com/en/5.0/topics/db/managers/

 

'server-side > Django' 카테고리의 다른 글

django customizing authentication  (0) 2024.01.07
django sessions  (0) 2024.01.06
django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20

⚠️

아래 공식문서를 읽고 인증 관련된 내용 중 일부만 발췌해서 정리했습니다.

자세한 내용이 궁금하시다면 공식문서를 참고하시는 것을 추천드립니다. 

https://docs.djangoproject.com/en/5.0/topics/auth/customizing/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

✅django authentication

장고에서는 인증을 수행하기 위한 여러 백엔드 클래스(authentication backends)들이 있다. 

 

settings.py 설정 파일에서 AUTHENTICATION_BACKENDS 라는 변수 값으로 이 인증 백엔드 클래스를 어떻게 설정할지를 지정할 수 있다. 

 

인증 백엔드가 여러 개일 경우, 맨 위에 선언한 클래스부터 순차적으로 인증이 실행된다. 상위 클래스에서 인증이 완료되면 하위 클래스는 실행되지 않는다. 

 

기본값은 ModelBackend이다. 

AUTHENTICATION_BACKENDS = [
	"django.contrib.auth.backends.ModelBackend"
]

 

인증 백엔드는 세션과도 연관이 있다. 하나의 세션에서는 인증에 사용하는 백엔드를 캐싱해 두고 인증할 때 그 클래스를 사용한다. 

 

django.contrib.auth 모듈의 authenticate()를 호출하면 장고의 모든 인증 백엔드에서 순차적으로 인증을 시도한다. 

 

☑️login() vs authenticate()

인증을 수행한다는 공통점이 있다.

authenticate(): 인증 정보가 유효한지를 한 번 확인한다.

login(): 인증 정보가 유효한지를 확인하고 유효하다면 세션을 만드는 등의 후처리를 한다. 그 결과 한 번 인증된 유저를 여러 번 인증할 필요가 없게 한다. 

 

✅Custom Authentication

기본 인증을 사용하지 않고 새로운 인증 클래스를 만들어서 사용해야 할 때도 있다.

 

예를 들면 서로 다른 두 앱(django app)의 유저를 통합하거나 할 때, 두 앱에서 모두 사용할 수 있는 인증 클래스가 필요할 것이다. 

 

새로운 인증 백엔드 클래스(Authentication Backend)를 만들기 위해서는 크게 두 가지가 필요하다. 

  1. django.contrib.auth.backends 모듈의 BaseBackend 클래스를 상속하는 새 클래스 만들기
  2. 해당 클래스에서 두 가지 메소드 구현하기
  • authenticate: 요청 및 필요시 추가 정보(credentials)를 받아서 인증 되면 user, 아니면 None을 리턴한다. 
  • get_user: user 인스턴스의 pk 값을 받아서 user를 리턴한다. 
from django.contrib.auth.backends import BaseBackend

class CustomBackend(BaseBackend):

	def authenticate(self, request, credentials=None):
		...
        
	def get_user(self, user_id):
		...
        
   ...

 

 

✅Authentication Backend에서 권한(Permissions) 부여하기

BaseBackend 클래스에서는 User 모델 및 UserManager 클래스에서 정의하는 여러 권한 조회 함수들(permission lookup functions)을 사용할 수 있다. 

  • get_user_permissions: 해당 유저가 직접적으로 가진 권한들을 리턴한다. 
  • get_group_permissions: 해당 유저가 속한 그룹이 가진 권한들을 리턴한다. 
  • get_all_permissions: 위의 두 메소드의 합집합을 리턴한다. 
  • has_perm(perm): 유저가 해당 권한을 가지고 있는지를 boolean 값으로 리턴한다. 
  • has_module_perms(module_name): 유저가 해당 모듈(django app)에 해당하는 권한을 하나라도 갖고 있다면 True를 리턴한다. 

만약 시행 중 Permission Denied 에러가 리턴되면, 미들웨어처럼 이후의 인증 백엔드를 거치지 않고 에러를 리턴한다. 

 

✅특정 모델 인스턴스에 대해 권한 생성하기

모델 클래스의 Meta 옵션에 permissions 속성을 추가하고, 그 값으로 추가하고 싶은 권한들을 작성한다. 

권한들도 DB를 사용하는 경우 하나의 테이블로 맵핑되기 때문에, 이렇게 추가한 권한 값들은 migrate 명령어를 실행한 이후에 생성되므로 그 이후에 사용할 수 있다. 

class Task(models.Model):
	...
	class Meta:
		permissions = [
			("change_task_status", "Can change the status of tasks"),
			("close_task", "Can close the status of tasks"),
		]

 

 

참고한 포스트

https://stackoverflow.com/questions/28249276/whats-the-difference-between-authenticate-and-login

 

'server-side > Django' 카테고리의 다른 글

django customizing user  (0) 2024.01.07
django sessions  (0) 2024.01.06
django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20

아래 공식문서를 읽고 세션에 관련된 기본 내용만 발췌하여 정리했습니다. 자세한 내용이 궁금하시다면 공식문서를 참고하시는 것을 추천드립니다. 

https://docs.djangoproject.com/en/5.0/topics/http/sessions/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

Session

  • 세션은 인증에서 사용되는 방법 중 하나로, 서버에 접속한 개별 클라이언트마다 인증에 필요한 정보를 서버에 저장해 두고 관리하는 방식이다.
  • 인증 정보를 서버 측에서 관리하기 때문에 보안 면에서 더 안전하고 정보가 탈취될 가능성이 더 적다.
  • 반면 사용자가 많아질수록 그 많은 사용자의 정보를 서버에서 모두 관리해야 하기 때문에 서버의 부하가 커질 수 있고, DB 등 정보를 저장할 곳이 필요하기 때문에 확장성이 떨어진다는 단점도 있다. 

 

Django Session

장고에서는 세션을 미들웨어(middleware)로 구현한다.

장고의 설정 파일(settings.py)의 middleware 변수에는 django.contrib.sessions.middleware.SessionMiddleware 클래스가 기본으로 추가되어 있는데, 이 미들웨어가 장고에서 기본 세션을 구현하는 역할을 한다. 

만약 장고에서 세션을 사용하고 싶지 않다면 이 미들웨어를 middleware 변수에서 빼 주자. 

 

앞서 세션은 인증에 필요한 정보를 서버 측에 저장하는 방식이라고 했다. 

장고에서는 이 정보를 어디에 저장하는지에 따라서 다양한 세션 엔진(session engine)의 종류가 있다. 

 

DB

  • 장고 세션에서 사용하는 기본 설정이다.
  • 사용하는 DB에 세션 정보를 저장하는 전용 테이블을 만들어서 그 안에 세션 정보를 저장한다.
  • 이 경우 Session 객체를 통해 개별 세션의 정보에 접근할 수 있다. 

Cache

  • 공식문서에서는 Redis나 MemCached 같은 인메모리 데이터 저장소를 사용하는 경우에만 사용하는 것을 추천한다. 
  • 로컬 메모리 캐시를 사용할 경우 데이터를 오랫동안 저장해두지 못할 수 있고, 로컬 메모리 캐시는 멀티 프로세스 환경에 적합하지 않기 때문이다(not multi-process safe, 멀티 프로세스 환경에서 발생하는 동시성 에러 등으로 데이터가 변경될 수 있다). 
  • 인메모리 데이터 저장소에서는 데이터를 key-value 쌍으로 저장하기 때문에, 이 경우 key는 각 세션을 구분하는 session key가 될 것이고 세션에 저장할 데이터가 value 값이 된다. 

Cookie

  • http 통신에 사용하는 쿠키 형태로 세션 데이터를 저장하는 방식이다. 
  • 다만 쿠키 데이터를 cryptography 방식으로 암호화하여 저장한다. 

File

  • 파일 형태로 로컬 디렉토리에 저장한다.
  • 기본 값으로는 /tmp 디렉토리에 저장되고, 기본 디렉토리를 바꾸고 싶다면 설정 파일의 SESSION_FILE_PATH 변수 값을 따로 지정하면 된다. 

 

Session object 특징

  • session_key: 이 값으로 각 세션을 구분한다. session key는 40글자 이내의 문자열이다. 
  • expiry_date:  각 세션이 언제 만료되는지를 초 값으로 저장한다. 
  • session_data: dictionary 형태로 인증에 필요한 정보를 저장한다.

 

Sesssion 특징

서버에서 브라우저와 통신할 때는 쿠키를 사용하는데, 이 쿠키에 현재 세션을 unique하게 구분하기 위한 session key 값이 들어 있다. 만약 session key 값이 잘못되었거나 해당 세션의 만료 시점이 지났다면 인증이 정상적으로 시행되지 않는다. 

 

세션 데이터는 사용하는 세션 엔진에 따라서, 또는 커스텀 세션의 경우는 직접 정의한 encoder 및 decoder에 따라서 값 그대로 저장되지 않고 인코딩 되어 저장된다. 

 

세션 데이터의 값이 수정될 때마다 세션 값이 새로 저장되는 것이 기본적이다. 만약 그렇게 하지 않고 매번 요청할 때만 값을 새로 저장하고 싶다면, 설정 파일에서 SESSION_SAVE_EVERY_REQUEST 변수 값을 True로 바꿔주자. 

 

세션은 세션 정보를 저장하는 쿠키의 만료 시점까지 지속되는 것이 기본 설정이다. 그러나 브라우저를 닫을 때 관련 세션이 닫히게 하고 싶다면, 설정 파일의 SESSION_EXPIRE_AT_BROWSER_CLOSE 변수 값을 True로 바꿔주자. 

 

세션이 만료된 경우에도 DB 등에 저장된 세션 데이터는 자동으로 사라지지 않고 남아있다. 이 경우 clearsessions 명령어를 통해 만료된 세션 데이터를 주기적으로 지워 주어야 한다. 

 

 

그 외 참고한 포스트들

https://docs.djangoproject.com/en/5.0/topics/http/sessions/

https://zangzangs.tistory.com/72

https://superfastpython.com/process-safe-in-python/

 

'server-side > Django' 카테고리의 다른 글

django customizing user  (0) 2024.01.07
django customizing authentication  (0) 2024.01.07
django migrations  (0) 2023.12.31
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20

직관적으로는 모든 값을 이중 for문으로 순회하면서 능력치를 계산하는 방법이 있다. 

그러나 이 방법으로 하면 시간초과가 난다. 

 

리스트의 전체 값을 순회해야 하는데 정석대로 하면 시간초과가 나는 경우, 투 포인터를 사용해서 접근해볼 수 있다. 

능력치는 (두 개발자 사이에 있는 개발자 수) * (두 개발자의 능력치 중 작은 값)으로 계산되므로, left와 right이라는 두 포인터를 리스트의 양 끝에 배치시켜서 두 개발자 사이에 있는 개발자 수를 최대로 만들어 보자. 

 

그리고 두 포인터가 가리키는 값 중에서 누가 더 작은지를 비교해서, 더 작은 쪽의 값을 변화시키는 방식으로 포인터를 이동해 나간다. 

left 쪽의 값이 더 작다면 left를 한 칸 오른쪽으로, right의 값이 작다면 right를 한 칸 왼쪽으로 이동하는 식이다. 

 

그러면 이중 for문의 풀이는 O(n^2)였던 데 비해, 투 포인터로 O(n)에 풀 수 있어서 시간초과가 나지 않는다. 

 

import sys

input = sys.stdin.readline

N = int(input())
arr = list(map(int, input().split()))

answer = 0
left, right = 0, N-1
while left < right:
    cur = (right-left-1) * min(arr[left], arr[right])
    answer = max(cur, answer)
    if arr[left] > arr[right]:
        right -= 1
    else:
        left += 1

print(answer)

 

migration(마이그레이션)이란

장고에서 모델과 연결된 DB의 버전을 관리하는 방법이다. migration은 파이썬 파일 형식으로 관리된다. 

 

migration의 특징

  1. 실제 DB에서 변경되는 내용이 없어도 migration 파일이 만들어질 수 있다. 
  2. 어떤 DB에는 적용 가능한 내용이 다른 DB에는 적용 불가능할 수 있다. (MySQL의 경우 PostgreSQL보다 컬럼에 사용하는 max_length 속성의 최댓값이 작다)
  3. 직접 migration 파일을 수정할 수 있고 빈 migration 파일을 만들 수도 있다. 
  4. transaction 개념이 있는 DB(postgresql, sqlite)의 경우, migration은 하나의 transaction 안에서 실행된다. 그렇지 않은 경우는 transaction 없이 실행되고, 이 경우 migration 도중 failure가 발생하면 rollback이 되지 않는다. 
  5. 맨 처음 만들어진 migration(initial migration)이 아닌 경우, migration은 다른 migration에 의존한다. 

 

migration이 만들어질 때

python manage.py makemigrations 커맨드를 입력하면 migration 파일이 만들어진다. 

migration 파일에는 여러 속성들이 있지만, 그 중에서도 dependencies와 operations가 대표적이다. 

dependencies는 해당 migration이 다른 어떤 migration에 의존하고 있는지를 나타낸다. 

operations는 해당 migration이 해당 앱의 모델을 어떻게 변형할지를 나타낸다. 

 

makemigrations 명령어에 따라 migration 파일이 만들어지는 원리

  1. 장고는 이전에 있던 migration들을 순서대로 실행한다. 
  2. 현재 모델의 상태와 1번의 과정에서 얻은 모델의 상태를 비교한다. 만약 변화된 점이 없다면 'detected no changes'와 유사한 문구가 뜬다. 
  3. 2번에서 현재 모델과 migration 파일들을 순차적으로 실행하면서 얻은 모델의 상태에 차이가 있다면, 그 차이점을 바탕으로 migration 파일을 만든다. 

migration 관련 명령어들

  • makemigrations: 위의 과정으로 migration 파일을 만든다. 
  • migrate: makemigrations으로 만들어진 파일이 있는 경우 해당 내용을 실행해서 DB 스키마를 바꾼다. 만약 모델에 변화가 있는데 makemigrations 없이 migrate를 실행한 경우, migration 파일이 만들어지고 그 파일에 대해서 바로 migrate 명령어가 이어서 실행된다. 
  • sqlmigrate: migrate와 똑같이 동작하는데, 다만 동작 과정에서 어떤 SQL문이 실행되는지를 보여준다. 
  • showmigrations: 현재 어떤 migration들이 있는지를 보여준다. migration 파일은 있는데 DB에 반영되지 않은 경우에는 체크 표시가 없고, migration 파일도 있고 DB에 정상적으로 반영된 경우 체크 표시가 있다. 
  • squashmigrations: 기존에 있던 여러 개의 migration 파일을 압축하는 커맨드이다. 

 

apply migration

python manage.py migrate {app_label}

특정 앱에 대해서만 migration을 적용하고 싶을 때 사용한다. 

 

reverse migration 

python manage.py migrate {app_label} {number/migration name}

특정 앱에 대해서 이전 migration으로 진행 상태를 돌리고 싶을 때 사용한다. migration의 번호를 입력하면 해당 번호까지의 migration이 실행된 상태로 돌려 준다. 

물론 항상 가능한 것은 아니고, 되돌리지 못하는 migration의 경우는 IrreversibleError가 발생할 수 있다. 

 

squash migration

python manage.py squashmigrations {app_label} {number/migration name}

기존 여러 개의 migration 파일을 1개 혹은 몇 개로 줄이는 작업이다. 최초 migration부터 입력한 번호까지의 migration을 압축한다고 볼 수 있다. 기존 migration보다 파일의 개수나 operation의 개수는 줄어들지만 DB에 적용되는 내용은 똑같다. 

 

작업 원리

  1. squash 대상이 되는 모든 migration에서 operation을 추출해서 순서대로 정렬한다. 
  2. 1번 결과에 대해 optimizer를 실행해서, 중복되는 operation은 생략하는 등 operation의 개수를 줄인다. (ex. CreateModel과 DeleteModel이 같이 있다면 두 operation 모두 삭제하기)
  3. 2번의 결과를 migration 파일로 만든다. 

 

squashed migration -> normal migration

squashed migration(squashmigration의 결과로 생성된 파일)은 해당 파일의 replace 속성이 있어서 일반 migration과는 달리 "기존의 migration을 대체한" 파일이다. 

이 파일이 일반 migration처럼 동작하게 하려면 추가 작업이 필요하다. 

  1. squashmigrations 명령어 실행하기
  2. 1번의 migration 파일이 대체한 기존 migration 파일들 삭제하기
  3. 2번에서 삭제한 migration 파일에 의존하는 다른 파일들의 dependencies 속성을 1번에서 새로 생성된 squashed migration 파일로 수정하기
  4. 1번 파일에 표시된 replaces 속성을 삭제하기

 

fake migration

이미 DB에는 SQL문이 실행되어 테이블과 컬럼이 있으나 장고 migration에서는 반영되지 않았을 때 --fake 옵션을 사용한다. 

즉 장고에서 관리하는 migration의 진행 상태만 진행한 것으로 변화시키고, 실제로 DB에 SQL문은 실행하지 않는 것을 fake migration이라고 한다. 

 

initial migration

맨 처음 실행되는 mgiration으로, 다른 migration에 의존하지 않는다. 

보통 initial migration은 1개이지만 2개 이상의 initial migration을 만들 수도 있다. 

--fake-initial 옵션을 붙이면 이미 DB에서 테이블과 컬럼이 만들어진 경우 초기 migration을 적용하지 않고 스킵할 수 있다. 다만 이 옵션은 적용하려고 했던 initial migration에 선언된 모델이 DB에 존재하는지가 확인되어야 적용할 수 있다. (적용하려는 모델이 없는데 --fake-initial 옵션을 쓸 수는 없다.)

 

RunPython

기존의 migration 파일을 작성하는 방법이 아니라, python 코드로 DB 내부의 내용을 변경할 수 있다. 

(RunPython과 비슷하게는 RunSQL도 있다. 이 경우 SQL문을 직접 장고에서 실행할 수 있다.)

 

주의 사항

1. RunPython으로 현재 앱이 아닌 다른 앱에 있는 모델에 접근할 경우, migration 파일의 dependencies 속성에다 다음 2가지를 꼭 추가해야 한다. 

  • 현재 app의 initial migration
  • 접근하는 다른 앱의 가장 최신 migration

그렇지 않으면 LookupError 에러가 발생한다. 

 

2. RunPython으로 data migration 작업을 할 수 있다. 

  1. python manage.py migrate {app_label} --empty 커맨드로 빈 migration 파일을 만든다. 
  2. 해당 파일의 dependencies 속성에는 해당 앱의 initial migration만 추가한다. 
  3. operations에는 migrations.RunPython()을 명시하고, 괄호 안에는 callable(실행 가능한 함수의 이름)을 적는다. data migration 작업을 하는 함수를 만들고 그 함수의 이름을 callable로 적으면 된다. 

 

'server-side > Django' 카테고리의 다른 글

django customizing authentication  (0) 2024.01.07
django sessions  (0) 2024.01.06
django routers  (1) 2023.12.21
django apps  (0) 2023.12.20
models and databases  (0) 2023.09.16

Routing(라우팅)

서버로 들어온 요청에 맞는 리소스나 페이지로 요청을 이동시키는 것이다. 

 

Routing 방법들

1. path 사용

path("경로", Viewset)

from django.urls import path

urlpatterns = [
	path("/path", ViewSet.as_view()),
]

 

2. router 사용

router.register("경로", Viewset)

from rest_framework import routers

router = routers.SimpleRouter()
router.register(r"path", ViewSet)
urlpatterns = router.urls

 

3. @action decorator 사용

path, router과는 조금 다른 방법으로, 기본적인 url mapping을 따르지 않거나, view의 역할이 기본적인 CRUD가 아닐 때, 또는 추가적인 permission 등을 지정하고 싶을 때 사용한다. 

class TempViewSet(ModelViewSet):
	
    @action(methods=['post'], detail=True, permission_classes=[IsAdminOnly], url_path="tempapi")
    def temp_view(self, request):
		...

methods: 해당 view에 접근 가능한 http method를 리스트 형식으로 지정한다. 

detail: 해당 view가 모델 개별 인스턴스에 대한 정보를 다루는지, 모델 전체 인스턴스들에 대한 정보를 다루는지를 boolean 값으로 표현한다. 

permission_classes: 해당 view에 어떤 permission 클래스에 해당해야 접근할 수 있는지를 리스트 형식으로 지정한다. 

url_path: @action을 사용하는 경우 보통 url 경로는 메소드의 이름으로 정해진다. 만약 url 경로를 메소드의 이름과 다르게 설정하고 싶다면 설정해주면 된다. 

 

Router: SimpleRouter와 DefaultRouter

1. 차이점

- DefaultRouter는 프로젝트의 모든 url이 전부 명시되어 있는 기본 페이지를 제공한다. 

- DefaultRouter는 api를 제공할 때 .json 형식으로도 제공한다. 

 

2. url path, http method, action에 따라 url을 mapping 하는 방식

url style http method action
기본 path/ GET LIST
기본 path/ POST CREATE
기본 path/해당 url path/ 지정 X @action(detail=False)
기본 path/pk/ GET RETRIEVE
기본 path/pk/ PUT UPDATE
기본 path/pk/ PATCH PARTIAL-UPDATE
기본 path/pk/ DELETE DESTROY
기본 path/pk/해당 url path/ 지정 X @action(detail=True)

 

+

또한 django에서 제공하는 router들은 기본적으로 모든 url 뒤에 '/'를 붙이는 것이 기본이다. 

그러나 router를 선언할 때 trailing_slash 옵션을 False로 선언하면 url의 맨 뒤에 '/'가 붙지 않도록 할 수 있다. 

router = routers.SimpleRouter(trailing_slash=False)

 

'server-side > Django' 카테고리의 다른 글

django sessions  (0) 2024.01.06
django migrations  (0) 2023.12.31
django apps  (0) 2023.12.20
models and databases  (0) 2023.09.16
python - poetry 사용하기  (0) 2023.07.12

+ Recent posts