SlideShare ist ein Scribd-Unternehmen logo
1 von 36
레거시 시스템에
Django 들이밀기
정지용
발표자 소개
• P2P금융 렌딧에서 일합니다.
• 새로운 언어와 프레임워크를 좋아합니다.
• 읽기 편한 코드를 좋아합니다.
• 가장 좋은 코드는 아예 만들 필요가 없는 코드
2
Django로 향하는 길을 함께 열어준
Sam.Jo에게 감사를 전합니다.
3
Java 세상 이야기
“우리 시스템에 OO한 기능을 추가하고 싶어요.”
“일단 이.......만큼 코드를 쓰시고요.”
MyExampleRepository.java
MyExampleService.java
MyExampleServiceImpl.java
MyExampleController.java
MyExampleAdminController.java
MyExampleList.vue
MyExampleDetail.vue
4
자연스러운 수순
5
이어질 만한 수순
"이 이벤트 배너 종료일자는 어떻게 고치나요?"
"아.. 입력 폼 복붙하다가 필드를 하나 빼먹었네요."
6
막장 수순
빨리 고쳐야하는데...
급하니까 일단 터미널을 열고...
mysql에 접속해서...
UPDATE event_banner SET ends_at = '2018-08-19 13:35:00';
7
계기
• 높은 생산성을 갖는 어드민을 새로 만들고 싶다...
...
8
2017년 가을, 저희 개발팀의 상황
• 주요 개발 언어인 Java, Javascript가 코드 베이스의 대부분이고
Python을 일부분 사용
• 테이블 수는 200여개
• 기존 Java(Spring)으로 만든 거대한 어드민 운영중
 두 개의 어드민을 계속 유지보수 할 수 있을까?
9
inspectdb
• Integrating Django with a legacy database
$ python manage.py inspectdb > models.py
10
inspectdb
• DB 스키마를 읽어서 models.py를 생성해주는 기능
• legacy 시스템에 Django를 쉽게 도입할 수 있도록 도와줌
• 본격적으로 한 DB 두 살림을 운영해보자!
11
목표
1. 최소한의 코딩으로 최대한의 효과(어드민 기능)를!
2. 최대한 기존 DB를 활용하지만, legacy 프로젝트에는 전혀 영향
을 주지 않도록
12
발표 목차
• DB 연결
• 로그인 인증
• 모델 어드민 등록 및 커스터마이징
• inspectdb 확장하기
• BIT(1), BOOLEAN 문제
13
주의: 전방에 코드가 있습니다.
14
DB 연결
• 두 개의 DB를 연결합니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproj_django',
},
'myproj': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproj_development’,
},
}
DATABASE_ROUTERS = ['apps.router.DBRouter']
15
inspectdb 업데이트 스크립트
• 모델을 자주 업데이트하니 아예 스크립트를 만듭시다.
#!/bin/bash
set -e
TEMP_FILE=models.py.tmp
python manage.py inspectdb --database myproj | tee ${TEMP_FILE}
# 임시파일을 거쳐서 생성해야함.
mv -f ${TEMP_FILE} apps/core/models.py
16
로그인 인증
• django의 custom backend를 활용
• DB가 연동되어 있으니 legacy의 ID/PW 정보로 로그인
• 사용자, 권한 정보 등을 그대로 가져다 쓸 수 있음!
# settings에서...
AUTHENTICATION_BACKENDS = [
'apps.core.backends.MyProjLoginBackend',
]
17
로그인 인증
• 최초 로그인시 Django DB에도 staff user 생성
# backends.py
class MyProjLoginBackend(ModelBackend):
def authenticate(self, request=None, username=None, password=None):
myproj_user = MyProjUser.objects.filter(username=username).first()
# 암호 확인, 권한 확인 (생략)
user = User.objects.filter(username=username).first()
if not user:
user = User.objects.create_user(
username=myproj_user.username,
email=lendit_user.email,
is_staff=True,
)
user.save()
return user
18
모델 어드민 등록
• 모델이 있으니, 모델 어드민만 등록하면 된대요!
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(LoanContract)
admin.site.register(User)
admin.site.register(FeatureFlag)
admin.site.register(AdminUser)
admin.site.register(EventBanner)
...
19
모델 어드민 등록
• models.py에 있는 것을 모두 등록할 거니까요.
model_classes = [
x[1] for x in
inspect.getmembers(sys.modules["apps.core.models"],
inspect.isclass)
if models.Model in x[1].__bases__
]
for model_class in model_classes:
admin.site.register(model_class)
20
완성! 참 쉽죠?
21
짜잔!!!!!!!!!!!!!!!!!!!!!!!!
22
짜잔!!!!!!!!!!!!!!!!!!!!!!!!
23
Django 어드민 커스터마이징
• Django의 손 쉬운 커스터마이징
• 하지만 200개 넘는 모델을 다 대응하려면...
@admin.register(LoanContract)
class LoanContractAdmin(admin.ModelAdmin):
search_fields = ('=cust_nm',)
list_display = ('id', 'cust_nm', 'status', 'created_at')
list_filter = ('status',)
form = LoanContractForm
24
Django 어드민 커스터마이징
• list_display 만이라도 전부 적용해봅시다.
def generate_default_model_admin(model):
return type(f'{model.__name__}Admin', (admin.ModelAdmin,), {
'list_display': [x.name for x in
model._meta.get_fields()],
})
# (생략......inspect로 model_class 불러오는 부분)
for model_class in model_classes:
if model_class not in admin.site._registry.keys():
admin.site.register(model_class,
generate_default_model_admin(model_class))
25
Django 어드민 커스터마이징
26
“필수 항목입니다.”
• inspectdb는 문자열 필드를 만들 때, 모두 필수 필드라고 가정
• 모두 필수 아님 필드로 만들어봅시다.
python manage.py inspectdb --database myproj > $TEMP_FILE
sed "
s/some_field = models.CharField(max_length=200)/some_field =
models.CharField(max_length=200, blank=True)/; 
s/other_field = models.CharField(max_length=200)/other_field
= models.CharField(max_length=200, blank=True)/;
.... 
" $TEMP_FILE | tee $MODEL_FILE
27
inspectdb 확장하기
• sed 같은 외부 툴에 의존하지 않는 방법은 없을까?
• Github을 뒤적이던 도중....
🤔 흥미로운 파일명이군요.
28
inspectdb 확장하기
• Django문서 중 custom management commands
# apps/core/management/commands/inspectdb.py
from django.core.management.commands.inspectdb import (
Command as InspectDBCommand,
)
class Command(InspectDBCommand):
def get_field_type(self, connection, table_name, row):
field_type, field_params, field_notes =
super().get_field_type(connection, table_name, row)
if field_type == 'CharField':
field_params['blank'] = True
return field_type, field_params, field_notes
29
inspectdb 확장하기
• auto_now를 써서 생성, 수정 일자도 자동으로 넣어봅시다.
def get_field_type(self, connection, table_name, row):
field_type, field_params, field_notes =
super().get_field_type(connection, table_name, row)
if row.name == 'created_at':
field_params['auto_now_add'] = True
elif row.name == 'updated_at':
field_params['auto_now'] = True
if field_type == 'CharField':
field_params['blank'] = True
return field_type, field_params, field_notes
30
mysql, 그리고 BIT(1)
• 기존 시스템은 Mysql을 사용
• Boolean 값을 BIT(1)으로 표시
• inspectdb는 BIT(1)을 어떻게 생각할까?
old_bit_field = models.TextField() # This field type is a guess.
31
커스텀 Boolean Field
• 사용자 필드 생성 매뉴얼을 정독하고, 만들어봅시다.
class LBooleanField(BooleanField):
def from_db_value(self, value, expression, connection,
context):
if value is None:
return False
return self.to_python(value)
def to_python(self, value):
# BIT(1)은 b'x00' b'x01'로 떨어짐. 변환필요.
if isinstance(value, bytes):
return bool(value[0])
return super(BooleanField, self).to_python(value)
32
커스텀 Boolean Field
• inspectdb에서 불러다 씁시다.
• row.null_ok 를 사용하여 NullBooleanField도 확장하면 됩니다.
def get_field_type(self, connection, table_name, row):
field_type, field_params, field_notes =
super().get_field_type(connection, table_name, row)
if (row.type_code == FIELD_TYPE.TINY or row.type_code ==
FIELD_TYPE.BIT) and row.internal_size == 1:
field_type = 'LBooleanField'
field_notes = []
if row.name == 'created_at':
field_params['auto_now_add'] = True
...(생략)
33
여기까지 쓴 Python 코드
• 로그인 처리: 10여줄
• 모델 별 admin 등록: 10여줄
• inspectdb 확장: 30여줄
• DB 라우터: 20여줄
34
돌아보면...
• Legacy와의 공존은 성공
• Django기반 시스템을 발전시킬 수 있는 기반을 마련함
• 가장 힘들었던 부분: 배포 환경 설정
35
돌아보면...
• 날로 먹으면 기분이 좋다.
• 거의 모든 부분이 확장 가능한 Django.
• 지금 복사 붙여넣기를 하고 있다면, 분명 더 나은 방법이 있다.
36

Weitere ähnliche Inhalte

Was ist angesagt?

[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?
[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?
[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?Sang-ho Choi
 
Laravel 로 배우는 서버사이드 #2
Laravel 로 배우는 서버사이드 #2Laravel 로 배우는 서버사이드 #2
Laravel 로 배우는 서버사이드 #2성일 한
 
Angular2 가기전 Type script소개
 Angular2 가기전 Type script소개 Angular2 가기전 Type script소개
Angular2 가기전 Type script소개Dong Jun Kwon
 
테스트가 뭐예요?
테스트가 뭐예요?테스트가 뭐예요?
테스트가 뭐예요?Kyoung Up Jung
 
Php faker 를 활용한 의미있는 테스트 데이타 생성
Php faker 를 활용한 의미있는 테스트 데이타 생성Php faker 를 활용한 의미있는 테스트 데이타 생성
Php faker 를 활용한 의미있는 테스트 데이타 생성KwangSeob Jeong
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4성일 한
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1성일 한
 
파이썬 언어 기초
파이썬 언어 기초파이썬 언어 기초
파이썬 언어 기초beom kyun choi
 
처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3성일 한
 
막하는스터디 두번째만남 Express(20151025)
막하는스터디 두번째만남 Express(20151025)막하는스터디 두번째만남 Express(20151025)
막하는스터디 두번째만남 Express(20151025)연웅 조
 
React로 TDD 쵸큼 맛보기
React로 TDD 쵸큼 맛보기React로 TDD 쵸큼 맛보기
React로 TDD 쵸큼 맛보기Kim Hunmin
 
Javascript 교육자료 pdf
Javascript 교육자료 pdfJavascript 교육자료 pdf
Javascript 교육자료 pdfHyosang Hong
 
Web Components 101 polymer & brick
Web Components 101 polymer & brickWeb Components 101 polymer & brick
Web Components 101 polymer & brickyongwoo Jeon
 
자바야 놀자 PPT
자바야 놀자 PPT자바야 놀자 PPT
자바야 놀자 PPTJinKyoungHeo
 
Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3성일 한
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃Kwangyoun Jung
 

Was ist angesagt? (20)

[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?
[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?
[커빙 아키텍쳐] 커빙은 어떻게 소셜 컨텐츠를 모아올까요?
 
Laravel 로 배우는 서버사이드 #2
Laravel 로 배우는 서버사이드 #2Laravel 로 배우는 서버사이드 #2
Laravel 로 배우는 서버사이드 #2
 
Angular2 가기전 Type script소개
 Angular2 가기전 Type script소개 Angular2 가기전 Type script소개
Angular2 가기전 Type script소개
 
테스트가 뭐예요?
테스트가 뭐예요?테스트가 뭐예요?
테스트가 뭐예요?
 
플라스크 템플릿
플라스크 템플릿플라스크 템플릿
플라스크 템플릿
 
Php faker 를 활용한 의미있는 테스트 데이타 생성
Php faker 를 활용한 의미있는 테스트 데이타 생성Php faker 를 활용한 의미있는 테스트 데이타 생성
Php faker 를 활용한 의미있는 테스트 데이타 생성
 
처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4처음배우는 자바스크립트, 제이쿼리 #4
처음배우는 자바스크립트, 제이쿼리 #4
 
처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1처음배우는 자바스크립트, 제이쿼리 #1
처음배우는 자바스크립트, 제이쿼리 #1
 
Javascript
JavascriptJavascript
Javascript
 
파이썬 언어 기초
파이썬 언어 기초파이썬 언어 기초
파이썬 언어 기초
 
Nexacro
NexacroNexacro
Nexacro
 
처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3처음배우는 자바스크립트, 제이쿼리 #3
처음배우는 자바스크립트, 제이쿼리 #3
 
막하는스터디 두번째만남 Express(20151025)
막하는스터디 두번째만남 Express(20151025)막하는스터디 두번째만남 Express(20151025)
막하는스터디 두번째만남 Express(20151025)
 
React로 TDD 쵸큼 맛보기
React로 TDD 쵸큼 맛보기React로 TDD 쵸큼 맛보기
React로 TDD 쵸큼 맛보기
 
Introduce php7
Introduce php7Introduce php7
Introduce php7
 
Javascript 교육자료 pdf
Javascript 교육자료 pdfJavascript 교육자료 pdf
Javascript 교육자료 pdf
 
Web Components 101 polymer & brick
Web Components 101 polymer & brickWeb Components 101 polymer & brick
Web Components 101 polymer & brick
 
자바야 놀자 PPT
자바야 놀자 PPT자바야 놀자 PPT
자바야 놀자 PPT
 
Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3Laravel 로 배우는 서버사이드 #3
Laravel 로 배우는 서버사이드 #3
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
 

Ähnlich wie 레거시 시스템에 Django 들이밀기

Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Ryan Park
 
Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)beom kyun choi
 
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]Jaeman An
 
Express framework tutorial
Express framework tutorialExpress framework tutorial
Express framework tutorial우림 류
 
Swift3 subscript inheritance initialization
Swift3 subscript inheritance initializationSwift3 subscript inheritance initialization
Swift3 subscript inheritance initializationEunjoo Im
 
Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs기동 이
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)익성 조
 
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로Oracle Korea
 
파이썬 스터디 15장
파이썬 스터디 15장파이썬 스터디 15장
파이썬 스터디 15장SeongHyun Ahn
 
막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)연웅 조
 
Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기Kyoung Up Jung
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차Han Sung Kim
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기NAVER Engineering
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 SangIn Choung
 
06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)Hankyo
 

Ähnlich wie 레거시 시스템에 Django 들이밀기 (20)

Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005
 
Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)Ji 개발 리뷰 (신림프로그래머)
Ji 개발 리뷰 (신림프로그래머)
 
Light Tutorial Django
Light Tutorial DjangoLight Tutorial Django
Light Tutorial Django
 
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
Advanced Python Testing Techniques (Pycon KR 2019) [Korean Ver.]
 
Express framework tutorial
Express framework tutorialExpress framework tutorial
Express framework tutorial
 
Swift3 subscript inheritance initialization
Swift3 subscript inheritance initializationSwift3 subscript inheritance initialization
Swift3 subscript inheritance initialization
 
Nodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjsNodejs, PhantomJS, casperJs, YSlow, expressjs
Nodejs, PhantomJS, casperJs, YSlow, expressjs
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
 
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
[Main Session] 미래의 Java 미리보기 - 앰버와 발할라 프로젝트를 중심으로
 
Java.next
Java.nextJava.next
Java.next
 
파이썬 스터디 15장
파이썬 스터디 15장파이썬 스터디 15장
파이썬 스터디 15장
 
Java8 람다
Java8 람다Java8 람다
Java8 람다
 
ES6 for Node.js Study 2주차
ES6 for Node.js Study 2주차ES6 for Node.js Study 2주차
ES6 for Node.js Study 2주차
 
막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)막하는 스터디 네 번째 만남 AngularJs (20151108)
막하는 스터디 네 번째 만남 AngularJs (20151108)
 
Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기Django를 Django답게, Django로 뉴스 사이트 만들기
Django를 Django답게, Django로 뉴스 사이트 만들기
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
 
06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)06.실행환경 실습교재(easy company,해답)
06.실행환경 실습교재(easy company,해답)
 
Java_10 람다
Java_10 람다Java_10 람다
Java_10 람다
 

레거시 시스템에 Django 들이밀기

  • 2. 발표자 소개 • P2P금융 렌딧에서 일합니다. • 새로운 언어와 프레임워크를 좋아합니다. • 읽기 편한 코드를 좋아합니다. • 가장 좋은 코드는 아예 만들 필요가 없는 코드 2
  • 3. Django로 향하는 길을 함께 열어준 Sam.Jo에게 감사를 전합니다. 3
  • 4. Java 세상 이야기 “우리 시스템에 OO한 기능을 추가하고 싶어요.” “일단 이.......만큼 코드를 쓰시고요.” MyExampleRepository.java MyExampleService.java MyExampleServiceImpl.java MyExampleController.java MyExampleAdminController.java MyExampleList.vue MyExampleDetail.vue 4
  • 6. 이어질 만한 수순 "이 이벤트 배너 종료일자는 어떻게 고치나요?" "아.. 입력 폼 복붙하다가 필드를 하나 빼먹었네요." 6
  • 7. 막장 수순 빨리 고쳐야하는데... 급하니까 일단 터미널을 열고... mysql에 접속해서... UPDATE event_banner SET ends_at = '2018-08-19 13:35:00'; 7
  • 8. 계기 • 높은 생산성을 갖는 어드민을 새로 만들고 싶다... ... 8
  • 9. 2017년 가을, 저희 개발팀의 상황 • 주요 개발 언어인 Java, Javascript가 코드 베이스의 대부분이고 Python을 일부분 사용 • 테이블 수는 200여개 • 기존 Java(Spring)으로 만든 거대한 어드민 운영중  두 개의 어드민을 계속 유지보수 할 수 있을까? 9
  • 10. inspectdb • Integrating Django with a legacy database $ python manage.py inspectdb > models.py 10
  • 11. inspectdb • DB 스키마를 읽어서 models.py를 생성해주는 기능 • legacy 시스템에 Django를 쉽게 도입할 수 있도록 도와줌 • 본격적으로 한 DB 두 살림을 운영해보자! 11
  • 12. 목표 1. 최소한의 코딩으로 최대한의 효과(어드민 기능)를! 2. 최대한 기존 DB를 활용하지만, legacy 프로젝트에는 전혀 영향 을 주지 않도록 12
  • 13. 발표 목차 • DB 연결 • 로그인 인증 • 모델 어드민 등록 및 커스터마이징 • inspectdb 확장하기 • BIT(1), BOOLEAN 문제 13
  • 14. 주의: 전방에 코드가 있습니다. 14
  • 15. DB 연결 • 두 개의 DB를 연결합니다. DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myproj_django', }, 'myproj': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myproj_development’, }, } DATABASE_ROUTERS = ['apps.router.DBRouter'] 15
  • 16. inspectdb 업데이트 스크립트 • 모델을 자주 업데이트하니 아예 스크립트를 만듭시다. #!/bin/bash set -e TEMP_FILE=models.py.tmp python manage.py inspectdb --database myproj | tee ${TEMP_FILE} # 임시파일을 거쳐서 생성해야함. mv -f ${TEMP_FILE} apps/core/models.py 16
  • 17. 로그인 인증 • django의 custom backend를 활용 • DB가 연동되어 있으니 legacy의 ID/PW 정보로 로그인 • 사용자, 권한 정보 등을 그대로 가져다 쓸 수 있음! # settings에서... AUTHENTICATION_BACKENDS = [ 'apps.core.backends.MyProjLoginBackend', ] 17
  • 18. 로그인 인증 • 최초 로그인시 Django DB에도 staff user 생성 # backends.py class MyProjLoginBackend(ModelBackend): def authenticate(self, request=None, username=None, password=None): myproj_user = MyProjUser.objects.filter(username=username).first() # 암호 확인, 권한 확인 (생략) user = User.objects.filter(username=username).first() if not user: user = User.objects.create_user( username=myproj_user.username, email=lendit_user.email, is_staff=True, ) user.save() return user 18
  • 19. 모델 어드민 등록 • 모델이 있으니, 모델 어드민만 등록하면 된대요! from django.contrib import admin from .models import * # Register your models here. admin.site.register(LoanContract) admin.site.register(User) admin.site.register(FeatureFlag) admin.site.register(AdminUser) admin.site.register(EventBanner) ... 19
  • 20. 모델 어드민 등록 • models.py에 있는 것을 모두 등록할 거니까요. model_classes = [ x[1] for x in inspect.getmembers(sys.modules["apps.core.models"], inspect.isclass) if models.Model in x[1].__bases__ ] for model_class in model_classes: admin.site.register(model_class) 20
  • 24. Django 어드민 커스터마이징 • Django의 손 쉬운 커스터마이징 • 하지만 200개 넘는 모델을 다 대응하려면... @admin.register(LoanContract) class LoanContractAdmin(admin.ModelAdmin): search_fields = ('=cust_nm',) list_display = ('id', 'cust_nm', 'status', 'created_at') list_filter = ('status',) form = LoanContractForm 24
  • 25. Django 어드민 커스터마이징 • list_display 만이라도 전부 적용해봅시다. def generate_default_model_admin(model): return type(f'{model.__name__}Admin', (admin.ModelAdmin,), { 'list_display': [x.name for x in model._meta.get_fields()], }) # (생략......inspect로 model_class 불러오는 부분) for model_class in model_classes: if model_class not in admin.site._registry.keys(): admin.site.register(model_class, generate_default_model_admin(model_class)) 25
  • 27. “필수 항목입니다.” • inspectdb는 문자열 필드를 만들 때, 모두 필수 필드라고 가정 • 모두 필수 아님 필드로 만들어봅시다. python manage.py inspectdb --database myproj > $TEMP_FILE sed " s/some_field = models.CharField(max_length=200)/some_field = models.CharField(max_length=200, blank=True)/; s/other_field = models.CharField(max_length=200)/other_field = models.CharField(max_length=200, blank=True)/; .... " $TEMP_FILE | tee $MODEL_FILE 27
  • 28. inspectdb 확장하기 • sed 같은 외부 툴에 의존하지 않는 방법은 없을까? • Github을 뒤적이던 도중.... 🤔 흥미로운 파일명이군요. 28
  • 29. inspectdb 확장하기 • Django문서 중 custom management commands # apps/core/management/commands/inspectdb.py from django.core.management.commands.inspectdb import ( Command as InspectDBCommand, ) class Command(InspectDBCommand): def get_field_type(self, connection, table_name, row): field_type, field_params, field_notes = super().get_field_type(connection, table_name, row) if field_type == 'CharField': field_params['blank'] = True return field_type, field_params, field_notes 29
  • 30. inspectdb 확장하기 • auto_now를 써서 생성, 수정 일자도 자동으로 넣어봅시다. def get_field_type(self, connection, table_name, row): field_type, field_params, field_notes = super().get_field_type(connection, table_name, row) if row.name == 'created_at': field_params['auto_now_add'] = True elif row.name == 'updated_at': field_params['auto_now'] = True if field_type == 'CharField': field_params['blank'] = True return field_type, field_params, field_notes 30
  • 31. mysql, 그리고 BIT(1) • 기존 시스템은 Mysql을 사용 • Boolean 값을 BIT(1)으로 표시 • inspectdb는 BIT(1)을 어떻게 생각할까? old_bit_field = models.TextField() # This field type is a guess. 31
  • 32. 커스텀 Boolean Field • 사용자 필드 생성 매뉴얼을 정독하고, 만들어봅시다. class LBooleanField(BooleanField): def from_db_value(self, value, expression, connection, context): if value is None: return False return self.to_python(value) def to_python(self, value): # BIT(1)은 b'x00' b'x01'로 떨어짐. 변환필요. if isinstance(value, bytes): return bool(value[0]) return super(BooleanField, self).to_python(value) 32
  • 33. 커스텀 Boolean Field • inspectdb에서 불러다 씁시다. • row.null_ok 를 사용하여 NullBooleanField도 확장하면 됩니다. def get_field_type(self, connection, table_name, row): field_type, field_params, field_notes = super().get_field_type(connection, table_name, row) if (row.type_code == FIELD_TYPE.TINY or row.type_code == FIELD_TYPE.BIT) and row.internal_size == 1: field_type = 'LBooleanField' field_notes = [] if row.name == 'created_at': field_params['auto_now_add'] = True ...(생략) 33
  • 34. 여기까지 쓴 Python 코드 • 로그인 처리: 10여줄 • 모델 별 admin 등록: 10여줄 • inspectdb 확장: 30여줄 • DB 라우터: 20여줄 34
  • 35. 돌아보면... • Legacy와의 공존은 성공 • Django기반 시스템을 발전시킬 수 있는 기반을 마련함 • 가장 힘들었던 부분: 배포 환경 설정 35
  • 36. 돌아보면... • 날로 먹으면 기분이 좋다. • 거의 모든 부분이 확장 가능한 Django. • 지금 복사 붙여넣기를 하고 있다면, 분명 더 나은 방법이 있다. 36

Hinweis der Redaktion

  1. - 기존 DB에 전혀 영향이 없도록 django는 DB를 분리 - router에서는 model이 속한 app을 보고 라우팅
  2. Inspect라는 단어가 좋아지려고 하네요. ㅎㅎ Getmembers는 (name, value) pair list를 반환. apps.core.models 모듈에서 클래스인 것 중, models.Model을 상속받은 클래스 전체를 등록
  3. 참고로 Django 2.0에서는 User object(42) 같이 id 정보가 더 나옵니다.
  4. type()에 대한 설명
  5. t -> inspectdb 쳤는데!!
  6. 경로와 파일명을 정확히 맞추어야합니다.
  7. 경로와 파일명을 정확히 맞추어야합니다.
  8. - 심지어는 b’\x00’, b’\x01’ 값이 브라우저에 따라 다르게 표시되고 혼돈이 밀려옴
  9. - 참고) `Django-mysql` 패키지의 Bit1BooleanField 를 사용해도 좋을 것 같습니다. - Django 2.1에서는 NullBooleanField말고 BooleanField 하나로 모두 처리해야겠네요!
  10. - pip로 모듈이 안 깔릴 때는 easy_install을 해보세요.
  11. 더 낫다는 것은 상황에 따라 다를 수 있지만...