Docs Menu

Docs Home애플리케이션 개발Python 드라이버PyMongo

사용자 지정 유형

이 페이지의 내용

  • 개요
  • 사용자 지정 유형 인코딩
  • 유형 코덱 클래스 정의
  • 유형 레지스트리에 코덱 추가
  • 컬렉션 참조 가져오기
  • 하위 유형 인코딩
  • 폴백 인코더 정의
  • 알 수 없는 유형 인코딩
  • 제한 사항
  • API 문서

이 가이드에서는 PyMongo를 사용하여 사용자 지정 유형을 인코딩 및 디코딩하는 방법을 설명합니다.

드라이버가 이해할 수 없는 데이터 유형을 저장하려면 사용자 지정 유형을 정의해야 할 수 있습니다. 예를 들어, BSON 라이브러리의 Decimal128 유형은 Python의 내장 Decimal 유형과 다릅니다. 다음 코드 예시와 같이 PyMongo를 사용하여 Decimal 인스턴스를 저장하려고 하면 InvalidDocument 예외가 발생합니다.

from decimal import Decimal
num = Decimal("45.321")
db["coll"].insert_one({"num": num})
Traceback (most recent call last):
...
bson.errors.InvalidDocument: cannot encode object: Decimal('45.321'), of
type: <class 'decimal.Decimal'>

다음 섹션에서는 이 Decimal 유형에 대한 사용자 지정 유형을 정의하는 방법을 보여줍니다.

사용자 지정 유형을 인코딩하려면 먼저 유형 코덱을 정의해야 합니다. 유형 코덱은 사용자 지정 유형의 인스턴스가 bson 모듈이 이미 인코딩할 수 있는 유형으로 변환되거나 그 반대로 변환되는 방법을 설명합니다.

유형 코덱을 정의할 때 클래스는 codec_options 모듈의 기본 클래스 중 하나에서 상속되어야 합니다. 다음 표에서는 이러한 기본 클래스와 이를 구현하는 시기와 방법에 대해 설명합니다.

기본 클래스
사용 시기
구현할 멤버
codec_options.TypeEncoder
이 클래스에서 상속하여 사용자 지정 Python 유형을 알려진 BSON 유형으로 인코딩하는 코덱을 정의합니다.
  • python_type attribute: 이 유형 코덱으로 인코딩되는 사용자 지정 Python 유형

  • transform_python() 메서드: 사용자 지정 유형 값을 BSON이 인코딩할 수 있는 유형으로 변환하는 함수

codec_options.TypeDecoder
이 클래스에서 상속하여 지정된 BSON 유형을 사용자 지정 Python 유형으로 디코딩하는 코덱을 정의합니다.
  • bson_type attribute: 이 유형 코덱에 의해 디코딩되는 BSON types

  • transform_bson() 메서드: 표준 BSON 유형 값을 사용자 지정 유형으로 변환하는 함수

codec_options.TypeCodec
이 클래스에서 상속하여 사용자 지정 유형을 인코딩하고 디코딩할 수 있는 코덱을 정의합니다.
  • python_type attribute: 이 유형 코덱으로 인코딩되는 사용자 지정 Python 유형

  • bson_type attribute: 이 유형 코덱에 의해 디코딩되는 BSON types

  • transform_bson() 메서드: 표준 BSON 유형 값을 사용자 지정 유형으로 변환하는 함수

  • transform_python() 메서드: 사용자 지정 유형 값을 BSON이 인코딩할 수 있는 유형으로 변환하는 함수

예시의 Decimal 사용자 지정 유형은 Decimal128 인스턴스와 상호 변환할 수 있으므로 이 유형을 인코딩 및 디코딩하는 방법을 정의해야 합니다. 따라서 Decimal 유형 코덱 클래스는 TypeCodec 기본 클래스에서 상속해야 합니다.

from bson.decimal128 import Decimal128
from bson.codec_options import TypeCodec
class DecimalCodec(TypeCodec):
python_type = Decimal
bson_type = Decimal128
def transform_python(self, value):
return Decimal128(value)
def transform_bson(self, value):
return value.to_decimal()

사용자 지정 유형 코덱을 정의한 후에는 드라이버가 인코딩 및 디코딩할 수 있는 유형 목록을 PyMongo 의 유형 레지스트리 에 추가해야 합니다. 이렇게 하려면 TypeRegistry 클래스의 인스턴스를 만들고 목록 내에 유형 코덱 클래스의 인스턴스를 전달합니다. 사용자 지정 코덱을 여러 개 만드는 경우 해당 코덱을 모두 TypeRegistry 생성자에 전달할 수 있습니다.

다음 코드 예제에서는 DecimalCodec 유형 코덱의 인스턴스를 유형 레지스트리에 추가합니다.

from bson.codec_options import TypeRegistry
decimal_codec = DecimalCodec()
type_registry = TypeRegistry([decimal_codec])

참고

일단 인스턴스화된 레지스트리는 변경할 수 없으며 레지스트리에 코덱을 추가하는 유일한 방법은 새 코덱을 만드는 것입니다.

마지막으로 codec_options.CodecOptions 인스턴스를 정의하여 TypeRegistry 객체를 키워드 인수로 전달합니다. CodecOptions 객체를 get_collection() 메서드에 전달하여 사용자 지정 유형을 사용할 수 있는 컬렉션을 가져옵니다.

from bson.codec_options import CodecOptions
codec_options = CodecOptions(type_registry=type_registry)
collection = db.get_collection("test", codec_options=codec_options)

그런 다음 Decimal 클래스의 인스턴스를 인코딩 및 디코딩할 수 있습니다.

import pprint
collection.insert_one({"num": Decimal("45.321")})
my_doc = collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'num': Decimal('45.321')}

MongoDB가 사용자 정의 유형의 인스턴스를 저장하는 방법을 확인하려면 사용자 정의 코덱 옵션 없이 새 컬렉션 객체를 만든 다음 이를 사용하여 사용자 정의 유형이 포함된 문서를 검색합니다. 다음 예시는 PyMongo가 Decimal 클래스의 인스턴스를 Decimal128 값으로 저장하는 것을 보여줍니다.

import pprint
new_collection = db.get_collection("test")
pprint.pprint(new_collection.find_one())
{'_id': ObjectId('...'), 'num': Decimal128('45.321')}

사용자 지정 유형에서 상속되는 하나 이상의 유형을 인코딩해야 할 수도 있습니다. 값을 정수로 반환하는 메서드가 포함된 Decimal 클래스의 다음 하위 유형을 고려하세요.

class DecimalInt(Decimal):
def get_int(self):
return int(self)

먼저 유형 코덱을 등록하지 않고 DecimalInt 클래스의 인스턴스를 저장하려고 하면 PyMongo에서 오류가 발생합니다.

collection.insert_one({"num": DecimalInt("45.321")})
Traceback (most recent call last):
...
bson.errors.InvalidDocument: cannot encode object: Decimal('45.321'),
of type: <class 'decimal.Decimal'>

DecimalInt 클래스의 인스턴스를 인코딩하려면 클래스에 대한 유형 코덱을 정의해야 합니다. 이 유형의 코덱은 다음 예제와 같이 상위 클래스의 코덱인 DecimalCodec 에서 상속되어야 합니다.

class DecimalIntCodec(DecimalCodec):
@property
def python_type(self):
# The Python type encoded by this type codec
return DecimalInt

그런 다음 하위 클래스의 유형 코덱을 유형 레지스트리에 추가하고 사용자 지정 유형의 인스턴스를 인코딩할 수 있습니다.

import pprint
from bson.codec_options import CodecOptions
decimal_int_codec = DecimalIntCodec()
type_registry = TypeRegistry([decimal_codec, decimal_int_codec])
codec_options = CodecOptions(type_registry=type_registry)
collection = db.get_collection("test", codec_options=codec_options)
collection.insert_one({"num": DecimalInt("45.321")})
my_doc = collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'num': Decimal('45.321')}

참고

DecimalCodec 클래스의 transform_bson() 메서드를 사용하면 이러한 값이 DecimalInt Decimal 로 디코딩됩니다.

콜러블 폴백 인코더 를 등록하여 BSON에서 인식할 수 없고 유형 코덱이 등록되지 않은 유형을 인코딩할 수도 있습니다. 폴백 인코더는 인코딩할 수 없는 값을 매개 변수로 허용하고 BSON 인코딩 가능한 값을 반환합니다.

다음 폴백 인코더는 Python의 Decimal 유형을 Decimal128 로 인코딩합니다.

def fallback_encoder(value):
if isinstance(value, Decimal):
return Decimal128(value)
return value

폴백 인코더를 선언한 후 다음 단계를 수행합니다.

  • TypeRegistry 클래스의 새 인스턴스를 생성합니다. fallback_encoder 키워드 인수를 사용하여 폴백 인코더를 전달합니다.

  • CodecOptions 클래스의 새 인스턴스를 생성합니다. type_registry 키워드 인수를 사용하여 TypeRegistry 인스턴스를 전달합니다.

  • get_collection() 메서드를 호출합니다. codec_options 키워드 인수를 사용하여 CodecOptions 인스턴스를 전달합니다.

다음 코드 예시에서는 이 프로세스를 보여줍니다.

type_registry = TypeRegistry(fallback_encoder=fallback_encoder)
codec_options = CodecOptions(type_registry=type_registry)
collection = db.get_collection("test", codec_options=codec_options)

그런 다음 컬렉션에 대한 이 참조를 사용하여 Decimal 클래스의 인스턴스를 저장할 수 있습니다.

import pprint
collection.insert_one({"num": Decimal("45.321")})
my_doc = collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'num': Decimal128('45.321')}

참고

폴백 인코더는 표준 BSON 인코더 및 구성된 유형의 인코더를 사용하여 지정된 값을 인코딩하려는 시도가 실패한 후에 호출됩니다. 따라서 동일한 사용자 지정 유형을 대상으로 하는 유형 인코더와 대체 인코더로 구성된 유형 레지스트리에서는 유형 인코더에 지정된 동작이 우선합니다.

폴백 인코더는 인코딩하는 유형을 미리 선언할 필요가 없으므로 TypeEncoder 가 작동하지 않는 경우에 사용할 수 있습니다. 예를 들어 폴백 인코더를 사용하여 임의의 객체를 MongoDB에 저장할 수 있습니다. 다음과 같은 임의의 사용자 지정 유형을 고려하세요.

class MyStringType(object):
def __init__(self, value):
self.__value = value
def __repr__(self):
return "MyStringType('%s')" % (self.__value,)
class MyNumberType(object):
def __init__(self, value):
self.__value = value
def __repr__(self):
return "MyNumberType(%s)" % (self.__value,)

수신한 객체를 피클링하고 사용자 지정 하위 유형이 있는 Binary 인스턴스로 반환하는 폴백 인코더를 정의할 수 있습니다. 이 사용자 지정 하위 유형을 사용하면 검색 시 절인 아티팩트를 식별하고 이를 Python 객체로 다시 투명하게 디코딩하는 TypeDecoder 클래스를 정의할 수 있습니다.

import pickle
from bson.binary import Binary, USER_DEFINED_SUBTYPE
from bson.codec_options import TypeDecoder
def fallback_pickle_encoder(value):
return Binary(pickle.dumps(value), USER_DEFINED_SUBTYPE)
class PickledBinaryDecoder(TypeDecoder):
bson_type = Binary
def transform_bson(self, value):
if value.subtype == USER_DEFINED_SUBTYPE:
return pickle.loads(value)
return value

그런 다음 컬렉션의 코덱 옵션에 PickledBinaryDecoder 를 추가하고 이를 사용하여 사용자 지정 유형을 인코딩 및 디코딩할 수 있습니다.

from bson.codec_options import CodecOptions,TypeRegistry
codec_options = CodecOptions(
type_registry=TypeRegistry(
[PickledBinaryDecoder()], fallback_encoder=fallback_pickle_encoder
)
)
collection = db.get_collection("test", codec_options=codec_options)
collection.insert_one(
{"_id": 1, "str": MyStringType("hello world"), "num": MyNumberType(2)}
)
my_doc = collection.find_one()
print(isinstance(my_doc["str"], MyStringType))
print(isinstance(my_doc["num"], MyNumberType))
True
True

PyMongo 유형 코덱 및 폴백 인코더에는 다음과 같은 제한 사항이 있습니다.

  • intstr 와 같이 PyMongo가 이미 이해하고 있는 Python 유형의 인코딩 동작을 사용자 지정할 수 없습니다. 내장 유형에 따라 작동하는 하나 이상의 코덱을 사용하여 유형 레지스트리를 인스턴스화하려고 하면 PyMongo가 TypeError 을(를) 발생시킵니다. 이 제한은 표준 유형의 모든 하위 유형에도 적용됩니다.

  • 유형 인코더를 연결할 수 없습니다. 코덱의 transform_python() 메서드로 변환된 사용자 지정 유형 값은 기본적으로 BSON 인코딩 가능하거나 폴백 인코더에 의해 BSON 인코딩 가능한 유형으로 변환될 수 있어야 합니다. 다른 유형의 코덱으로 두 번째로 변환 할 수 없습니다 .

  • Database.command() 메서드는 명령 응답 문서를 디코딩하는 동안 사용자 지정 유형 디코더를 적용하지 않습니다.

  • gridfs 클래스는 수신하거나 반환하는 문서에 사용자 지정 유형 인코딩 또는 디코딩을 적용하지 않습니다.

사용자 지정 유형 인코딩 및 디코딩에 대한 자세한 내용은 다음 API 설명서를 참조하세요.

  • TypeCodec

  • TypeEncoder

  • TypeDecoder

  • TypeRegistry

  • CodecOptions

← 특수 데이터 형식