Back-end/Python

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

허도치 2021. 1. 19. 22:46

  지난 포스트에서는 간단한 매소드만 구현되어 있는 최상위 클래스인 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를 모두 구현해보았다. 문자열과 정수형은 딱히 어려운 부분이 없었는데, 날짜형은 계산과 데이터 형변환이 필요한 부분이 좀 있어서 복잡해지긴 했지만, 그래도 잘 돌아간다.

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