본문 바로가기
Back-end/Python

[Python] Data Model 만들기 (2) - Data Type Field

by 허도치 2021. 1. 19.

  지난 포스트에서는 간단한 매소드만 구현되어 있는 최상위 클래스인 BaseField를 구현하고 테스트를 해보았다. 이번 포스트에서는 BaseField를 상속받는 문자열 타입의 StringField, 정수형 타입의 IntegerField, 날짜형 타입의 DatetimeField를 만들어보도록 하겠다.

 

 

 

1. 문자열 타입 객체
1-1. StringField 객체 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# fields.py
"""
BaseField 밑에 이어서 작성
"""
 
class StringField(BaseField):
  def __init__(self, maxlength=None**kwargs):
    super(StringField, self).__init__(str**kwargs)
    
    # 유효성검사 옵션값
    self.__maxlength = maxlength
  
  # Overriding: 유효성검사
  def validate(self, value=None):
    exists = value is not None
    value = value if exists else self.getValue()
    length = len(value) if exists else 0
 
    
    # 유효성검사: 필수값
    if bool(self.required) and not exists:
      raise ValueError("This value was Required, but it is None.")
    
    # 유효성검사: 최대길이
    if bool(self.__maxlength) and exists and length > self.__maxlength:
      raise ValueError("Expected value length %d, but %d." % ( self.__maxlength, length ))
 
cs

 

 

1-2. StringField 객체 테스트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# test.fields.py
"""
TestBaseField 밑에 이어서 작성
"""
 
from fields import StringField
 
class TestStringfield(object):
  
  # 문자열 필드
  field = StringField(required=True, maxlength=10)
  
  def __init__(self):
    print("Stringfield 테스트")
    print("="*30)
    for attrname in dir(self):
      testcase = getattr(self, attrname)
      if attrname.startswith("test"and callable(testcase):
        print("=========", attrname, "========")
        try:
          testcase()
        except Exception as e:
          print("Error: "+str(e))
        print("="*30)
        
  def test_case_1(self):
    print("문자열 필수값 오류 확인")
    print("Input: None")
    self.field.setValue(None)
    print("Value: "+str(self.field.getValue()))
      
  def test_case_2(self):
    print("문자열 타입 오류 확인")
    print("Input: 1234")
    self.field.setValue(1234)
    print("Value: "+str(self.field.getValue()))
  
  def test_case_3(self):
    print("문자열 최대길이 오류 확인")
    print("Input: '1234567890#'")
    self.field.setValue('1234567890#')
    print("Value: "+str(self.field.getValue()))
  
  def test_case_4(self):
    print("문자열 정상")
    print("Input: 'Hello!!'")
    self.field.setValue("Hello!!")
    print("Value: "+str(self.field.getValue()))
 
# 테스트 실행
TestStringfield()
 
cs

 

 

1-3. StringField 객체 테스트 결과

 

 

 

2. 정수형 타입 객체
2-1. IntegerField 객체 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# fields.py
"""
StringField 밑에 이어서 작성
"""
 
class IntegerField(BaseField):
  def __init__(self, min=None, max=None**kwargs):
    super(IntegerField, self).__init__(int**kwargs)
 
    # 유효성검사 옵션값
    self.__min = min
    self.__max = max
  
  # Overriding: 유효성검사
  def validate(self, value=None):
    exists = value is not None
    value = value if exists else self.getValue()
 
    
    # 유효성검사: 최솟값
    if bool(self.__min) and exists and value < self.__min:
      raise ValueError("Expected minimum %d, but %d." % ( self.__min, value ))
 
    # 유효성검사: 최댓값
    if bool(self.__max) and exists and value > self.__max:
      raise ValueError("Expected maximum %d, but %d." % ( self.__max, value ))
 
cs

 

 

2-2. IntegerField 객체 테스트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# test.fields.py
"""
TestStringField 밑에 이어서 작성
"""
 
from fields import IntegerField
 
 
class TestIntegerField(object):
  
  # 정수형 필드
  field = IntegerField(required=True, min=10, max=100)
  
  def __init__(self):
    print("IntegerField 테스트")
    print("="*30)
    for attrname in dir(self):
      testcase = getattr(self, attrname)
      if attrname.startswith("test"and callable(testcase):
        print("=========", attrname, "========")
        try:
          testcase()
        except Exception as e:
          print("Error: "+str(e))
        print("="*30)
        
  def test_case_1(self):
    print("정수형 필수값 오류 확인")
    print("Input: None")
    self.field.setValue(None)
    print("Value: "+str(self.field.getValue()))
      
  def test_case_2(self):
    print("정수형 타입 오류 확인")
    print("Input: '100'")
    self.field.setValue('100')
    print("Value: "+str(self.field.getValue()))
  
  def test_case_3(self):
    print("정수형 최솟값 오류 확인")
    print("Input: 3")
    self.field.setValue(3)
    print("Value: "+str(self.field.getValue()))
  
  def test_case_4(self):
    print("정수형 최댓값 오류 확인")
    print("Input: 999")
    self.field.setValue(999)
    print("Value: "+str(self.field.getValue()))
  
  def test_case_4(self):
    print("정수형 정상")
    print("Input: 100")
    self.field.setValue(100)
    print("Value: "+str(self.field.getValue()))
 
# 테스트 실행
TestIntegerField()
 
cs

 

 

2-3. IntegerField 객체 테스트 결과

 

 

3. 날짜형 타입 객체
3-1. DatetimeField 객체 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# fields.py
"""
IntegerField 밑에 이어서 작성
"""
 
from datetime import datetime, timedelta
 
class DatetimeField(BaseField):
  def __init__(selfformat="%Y-%m-%d %H:%M:%S", min=None, max=None*args, **kwargs):
    super(DatetimeField, self).__init__(datetime, *args, **kwargs)
 
    self.__value = None
    self.__format = format
    self.__min = None
    self.__max = None
 
    # 제한시간 설정: 최소
    if min is not None:
      if min == 'now':
        self.__min = datetime.now()
      elif isinstance(min, str):
        self.__min = datetime.strptime(min, format)
      else:
        self.__min = min
 
    # 제한시간 설정: 최대
    if max is not None:
      if max == 'now':
        self.__max = datetime.now()
      elif isinstance(max, str):
        self.__max = datetime.strptime(max, format)
      else:
        self.__max = max
 
  # 값 설정
  def setValue(self, value, format=None, validate=True):
    self.__value = None
 
    # 유효성검사: 문자열 포맷 확인
    if isinstance(value, str):
      value = datetime.strptime(value, format or self.__format)
 
    if validate:
      # 유효성검사
      self.validate(value)
    
    self.__value = value
 
  # 값 반환, datetime
  def getValue(selfformat=None):
    return self.__value
 
  # Overriding: 유효성검사
  def validate(self, value=None):
    exists = value is not None
    value = value if exists else self.getValue()
 
    # 유효성검사: 타입체크
    if exists and not isinstance(value, self.type):
      raise TypeError("Expected data type '%s', but '%s'." %( self.type.__name__, value.__class__.__name__ ))
 
    # 유효성검사: 필수값
    if bool(self.required) and not exists:
      raise ValueError("This value was Required, but it is None.")
    
    # 유효성검사: 최소시간
    if bool(self.__min) and exists:
      
      # 최소시간 데이터 형변환
      min = None
      if isinstance(self.__min, timedelta):
        min = datetime.now()+self.__min
      else:
        min = self.__min
 
      if value < min:
        raise ValueError("Expected datetime less than (%s), but %s." % ( min, value ))
 
    # 유효성검사: 최대시간
    if bool(self.__max) and exists:
      
      # 최대시간 데이터 형변환
      max = None
      if isinstance(self.__max, timedelta):
        max = datetime.now()+self.__max
      else:
        max = max
  
      if value > max:
        raise ValueError("Expected datetime greater than (%s), but %s." % ( max, value ))
 
cs

 

 

3-2. DatetimeField 객체 테스트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# test.fields.py
"""
TestIntegerField 밑에 이어서 작성
"""
 
from fields import DatetimeField
from datetime import datetime, timedelta
 
 
class TestDatetimeField(object):
  
  # 날짜형 필드
  field = DatetimeField(required=Trueformat="%Y-%m-%d %H:%M:%S", min='now', max=timedelta(hours=1))
  
  def __init__(self):
    print("DatetimeField 테스트")
    print("="*30)
    for attrname in dir(self):
      testcase = getattr(self, attrname)
      if attrname.startswith("test"and callable(testcase):
        print("=========", attrname, "========")
        try:
          testcase()
        except Exception as e:
          print("Error: "+str(e))
        print("="*30)
        
  def test_case_1(self):
    print("날짜형 필수값 오류 확인")
    print("Input: None")
    self.field.setValue(None)
    print("Value: "+str(self.field.getValue()))
      
  def test_case_2(self):
    print("날짜형 포맷 오류 확인")
    print("Input: '2021/01/19 23:59:59'")
    self.field.setValue('2021/01/19 23:59:59')
    print("Value: "+str(self.field.getValue()))
      
  def test_case_3(self):
    print("날짜형 타입 오류 확인")
    print("Input: 1000")
    self.field.setValue(1000)
    print("Value: "+str(self.field.getValue()))
  
  def test_case_4(self):
    value = datetime.now() - timedelta(minutes=5)
    print("날짜형 최소시간 오류 확인")
    print("Input: %s (현재시간 - 5분)" %( value ))
    self.field.setValue(value)
    print("Value: "+str(self.field.getValue()))
  
  def test_case_5(self):
    value = datetime.now() + timedelta(hours=2)
    print("날짜형 최댓값 오류 확인")
    print("Input: %s (현재시간 + 2시간)" %( value ))
    self.field.setValue(value)
    print("Value: "+str(self.field.getValue()))
  
  def test_case_6(self):
    value = datetime.now() + timedelta(minutes=30)
    print("날짜형 정상")
    print("Input: %s (현재시간 + 30분)" %( value ))
    self.field.setValue(value)
    print("Value: "+str(self.field.getValue()))
 
# 테스트 실행
TestDatetimeField()
 
cs

 

 

3-3. DatetimeField 객체 테스트 결과

 

 

 

마치며

  - 이것으로 StringField, IntegerField, DatetimeField를 모두 구현해보았다. 문자열과 정수형은 딱히 어려운 부분이 없었는데, 날짜형은 계산과 데이터 형변환이 필요한 부분이 좀 있어서 복잡해지긴 했지만, 그래도 잘 돌아간다.

  - 다음 포스트에서는 지금까지 만든 데이터 타입 객체들을 이용한 데이터 모델을 만들어 보도록 하겠다.

 

댓글