이전에 PostgreSQL에서 사용자가 원하는 정렬을 하는 로직을 정리했습니다. (https://brownbears.tistory.com/384) 여기서는 Djago ORM을 이용하여 사용자가 원하는 order by를 하는 방법을 설명합니다.


PostgreSQL 9.5 이상의 버전이라면 array_position 을 사용해 손쉽게 사용자가 원하는 정렬을 할 수 있지만 Django ORM은 해당 기능을 지원하지 않기 때문에 Case문을 사용해 정의합니다.

from django.db.models import Case, Q

# 아래의 pk 순서대로 정렬하고자 함
pk_list = [2, 10, 1]
preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(pk_list)])
queryset = MyModel.objects.filter(pk__in=pk_list).order_by(preserved)


배치(Batch)란?

배치는 일반 프로세스의 일종으로 일련의 작업을 지정한 특정 시간에 실행합니다. 지정한 시간 이후에는 자원을 거의 소비하지 않는 것이 특징입니다.

데몬(Deamon)이란?

특정 서비스를 위해 백그라운드 상태에서 계속 실행되는 서버 프로세스입니다. 일반적으로 각 서비스가 사용하는 port를 관리하는 관리하는 데몬이 존재합니다. 데몬은 서버가 부팅될 때 메모리에 로딩이 되고 서버가 죽을 때까지 계속 자원을 할당받고 있습니다. 데몬은 서버가 죽을 때까지 자원을 점유하고 있는 형태여서 많은 데몬이 실행된다면 자원 소비가 큽니다.

윈도우에서는 데몬을 서비스라 부릅니다.

'서버' 카테고리의 다른 글

데몬(Daemon), 배치(Batch)란?  (0) 2019.09.01
crontab 초 단위 실행하기  (0) 2019.02.02
프로비저닝(Provisioning)이란?  (1) 2019.02.02
엣지 컴퓨팅(Edge Computing) 이란?  (0) 2019.01.29
Jupyter Notebook이란?  (0) 2017.10.18
Ansible이란?  (1) 2017.10.18

소수란 1과 자기 자신만을 가지는 정수입니다. 소수를 구하는 알고리즘은 많지만 여기서 설명하는 알고리즘은 에라토스테네스의 체라고 불리는 알고리즘입니다. 에라토스테네스는 고대 그리스의 수학자로서 마치 체로 걸러 내는 것처럼 수를 걸러 낸다고 하여 에라토스테네스의 체라고 부릅니다.

이 방법은 아주 단순하지만 소수를 구하는데 효과적인 방법입니다.

원리

숫자 1 ~ 120 범위 안에 있는 소수를 모두 계산한다고 가정합니다.

  1. 1은 소수도, 합성수도 아닌 기초수이기 때문에 1을 제거합니다. 
  2. 2는 소수이므로 2를 다른 곳에 기록합니다.
  3. 자기 자신(2)를 제외한 2의 배수를 모두 지웁니다.
  4. 남아있는 수에서 2 다음인 수인 3부터 진행을 시작합니다.
  5. 3은 소수이므로 2를 기록한 곳에 기록합니다.
  6. 자기 자신(3)을 제외한 3의 배수를 모두 지웁니다.
  7. 3 다음의 수는 4이지만 2의 배수로 2번에서 지워졌으므로 남아있는 수에서 3 다음인 수인 5부터 진행을 시작합니다.
  8. 5은 소수이므로 2와 3을 기록한 곳에 기록합니다.
  9. 자기 자신(5)를 제외한 5의 배수를 모두 지웁니다.
  10. 위의 과정을 끝까지 반복하면 구하는 구간의 모든 소수가 남습니다.

위의 과정은 구하고자 하는 120까지의 수를 계속해서 지워나가는 방식입니다. 이 방식은 소수를 확실하게 구할 수 있지만 시간복잡도가 O(n^2)이 발생합니다. 여기서 에라토스테네스의 체를 이용하면 계산하는 범위를 줄일 수 있습니다.

에라토스테네스의 공식은 위 과정을 120까지 할 필요 없이 11^2 > 120 이므로 11보다 작은 수의 배수들만 지워도 소수를 구할 수 있습니다. 따라서 위의 경우, 2, 3, 5, 7의 배수를 지우고 남은 수는 모두 소수입니다.

즉, 에라토스테네스의 체를 이용해 1~n까지의 소수를 알고 싶다면, n까지 모든 수의 배수를 다 나눠 볼 필요는 없습니다. 120에 루트를 씌우면 10.954451150103322의 값이 나오는데 이는 11보다 작으므로 11이하의 배수만 체크해도 소수를 구할 수 있습니다. 

코드

import math


def get_primes(n):
    # 구하고자 하는 수만큼 True를 갖는 리스트 생성
    is_primes = [True] * n
    # n의 최대 약수가 sqrt(n) 이하이므로 계산한 후, 소숫점이 있을 경우 올림으로 최대 반복 횟수 계산
    max_length = math.ceil(math.sqrt(n))

    for i in range(2, max_length):
        # True일 경우, 소수
        if is_primes[i]:
            # i이후 i의 배수들을 지워나감
            for j in range(i+i, n, i):
                is_primes[j] = False

    # 리스트의 True로 남아 있는 인덱스(소수)를 추출
    return [i for i in range(2, n) if is_primes[i]]


a = get_primes(10)
print(a)



파이썬에서 반올림은 보통 아래와 같이 구현합니다.

round(0.6)
# 1
round(2.4)
# 2


round()에 대해 찾아보면 자세한 내용이 있지만 간단하게 우리가 아는 반올림은 통계적인 반올림이고 파이썬의 round()는 수학적 반올림 값을 반환합니다. 이 때문에 나오는 결과에 대해서 사사오입 원칙이라고도 하는데 이를 설명하면 반올림 대상의 값이 5이고, 반올림 대상의 앞자리의 숫자가 짝수면 내림, 홀수면 올림을 진행합니다.

round(4.5)
# 4
round(0.5)
# 0
round(5.5)
# 6
round(1.5)
# 2


위 예시처럼 4.5는 반올림 대상의 앞자리가 짝수이므로 내림이 되었고 5.5에서는 5가 홀수이므로 올림이 되었습니다. 이러한 규칙을 모르고 반올림을 구한다면 시스템에 큰 오류를 범하고 문제 찾는거에 미궁이 빠지게 됩니다.

따라서 사람이 인식하고 있는 반올림을 구현하려면 내장 함수만으로는 불가능하고 반올림 함수를 직접 만들어야 합니다.

장고의 명령어 중, migrate, makemigrations 와 같은 명령어가 있습니다. 이러한 명령어는 models.py에 정의된 모델의 생성/변경 내역을 히스토리 관리데이터베이스에 적용 등과 같은 기능을 제공하여 손쉽게 데이터베이스의 구조를 바꿀 수 있습니다.

Migration 관련 명령어

# 마이그레이션 파일 생성
$ python manage.py makemigrations <app-name>

# 마이그레이션 적용
$ python manage.py migrate <app-name>

# 마이그레이션 적용 현황
$ python manage.py showmigrations <app-name>

# 지정 마이그레이션의 SQL 내역
 python manage.py sqlmigrate <app-name> <migration-name>

makemigrations

$ python manage.py makemigrations test

Migrations for 'test':
  test/migrations/0001_initial.py:
    - Create model Choice
    - Create model Question
    - Add field question to choice


makemigrations을 실행하면 모델을 변경시킨 사실 또는 새로 생성한 모델들과 같은 변경사항을 migrations로 저장하고자 Django에게 알려줍니다. migration은 Django가 모델의 변경사항을 저장하는 방법으로써, 디스크상의 파일로 존재합니다. 원한다면, <app-name>/migrations/0001_initial.py 파일로 저장된 새 모델에 대한 migration을 읽어볼 수 있습니다. 또 수동으로 Django의 변경점을 조정하고 싶을 때 직접 변경할 수 있습니다.


migration들을 실행시켜주고, 자동으로 데이터베이스 스키마를 관리해주는 migrate 명령어가 있습니다. 이 명령을 알아보기 전에 migration이 내부적으로 어떤 SQL 문장을 실행하는지 살펴봅시다. sqlmigrate 명령은 migration 이름을 인수로 받아, 실행하는 SQL 문장을 보여줍니다.

migrate

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, test, sessions
Running migrations:
  Rendering model states... DONE
  Applying test.0001_initial... OK


적용되지 않은생성/변경된 migrations들의 파일을 데이터베이스에 적용합니다. 이때 Django는 django_migrations 테이블을 두고 마이그레이션 적용 여부를 추적합니다. 이를 사용하면 데이터베이스를 직접 접근하지 않고도 모델의 반복적인 변경을 가능하게 해줍니다. 이처럼 마이그레이션을 만드는 명령과 적용하는 명령이 분리된 것은 버전 관리 시스템에 마이그레이션을 커밋하고 앱과 함께 출시할 수 있도록 하기 위해서라고 장고 공식 문서에 나와 있습니다.

모델의 생성/변경을 적용하는 단계는 아래와 같습니다.

  1. models.py 추가 및 변경
  2. python3 manage.py makemigrations를 실행해 변경사항에 대한 마이그레이션 파일 생성
  3. python3 manage.py migrate를 실행해 변경사항을 데이터베이스에 적용

showmigration

현재 적용된 마이그레이션 파일을 보여줍니다. 

$ python manage.py showmigrations

auth
[ ] 0001_initial
[ ] 0002_alter_permission_name_max_length
[ ] 0003_alter_user_email_max_length
[ ] 0004_alter_user_username_opts
[ ] 0005_alter_user_last_login_null
[ ] 0006_require_contenttypes_0002
[ ] 0007_alter_validators_add_error_messages
[ ] 0008_alter_user_username_max_length
contenttypes
[ ] 0001_initial
[ ] 0002_remove_content_type_name
sessions
[ ] 0001_initial


sqlmigrate

생성된 migrations파일들이 어떤 sql 문장을 실행하는지 보여줍니다.

$ python manage.py sqlmigrate test 0001

BEGIN;
--
-- Create model Choice
--
CREATE TABLE "test_temp" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(200) NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "test_temp2" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(200) NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "test_temp" ADD COLUMN "temp2_id" integer NOT NULL;
ALTER TABLE "test_temp" ALTER COLUMN "temp2_id" DROP DEFAULT;
CREATE INDEX "test_temp_7aa0f6ee" ON "test_temp" ("temp2_id");
ALTER TABLE "test_temp"
ADD CONSTRAINT "test_temp_temp2_id_246c99a640fbbd72_fk_test_temp2_id"
FOREIGN KEY ("temp2_id")
REFERENCES "test_temp2" ("id")
DEFERRABLE INITIALLY DEFERRED;

COMMIT;

만들어진 migrations의 sql은 아래와 같은 규칙이 있습니다.

  1. 테이블의 이름은 앱의 이름과 모델의 이름(소문자)가 조합되어 자동으로 생성. 위의 예시는 test 앱과 temp, temp2 모델명이 합쳐진 케이스
  2. 만약 pk가 지정되어 있지 않으면 자동으로 id라는 컬럼을 생성하고 pk로 지정
  3. 관례적으로 Djanngo는 외래키 컬럼의 마지막에 _id를 자동으로 추가
  4. sqlmigrate 명령어를 실행해도 실제로 데이터베이스의 마이그레이션을 실행하지 않음.


Django FilterSet 라이브러리는 GET 요청을 받고 쿼리 파라미터를 제어하는 기능을 제공합니다.

아래는 기본적으로 filterset을 사용한 예제입니다.

http://127.0.0.1:8000/test?name=kim


class NameFilter(filters.FilterSet):
    name = django_filters.CharFilter(
        name='name', lookup_expr='icontains'
    )


만약 위와 같은 구조가 아닌 여러 컬럼에 접근해야 한다면 아래와 같이 method를 지정하여 처리를 하도록 합니다.

http://127.0.0.1:8000/test?name=kim


class NameFilter(filters.FilterSet):
    name = django_filters.CharFilter(
        method='custom_name_filter'
    )

    def custom_name_filter(self, queryset, value, *args):
        return queryset.filter(name=args[0], nickname=args[0])


또는 아래와 같이 하나의 필드에 여러 값을 받은 다음 처리할 수 있습니다.

http://127.0.0.1:8000/test?multi=kim,5123



class MultiFilter(filters.FilterSet):
    multi = django_filters.CharFilter(
        method='custom_multi_filter'
    )

    def custom_multi_filter(self, queryset, value, *args):
        temp = args[0].split(',')
        name_list = []
        no_list = []
        
        for data in temp:
            if data.isdigit():
                no_list.append(int(data))
            else:
                name_list.append(data)

        return queryset.filter(name__in=name_list, no__in=no_list)


+ Random Posts