Docs Menu
Docs Home
/ /

유형 코덱으로 데이터 인코딩

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

PyMongo 에는 선택적으로 사용할 수 있는 몇 가지 내장 유형 코덱이 포함되어 있습니다. 이러한 내장 유형 코덱은 일반적인 데이터 유형을 자동으로 처리하다 . 예시 들어 운전자 Python의 decimal.Decimal 유형을 BSON Decimal128 값으로 인코딩 및 디코딩 활성화 있는 DecimalEncoderDecimalDecoder 클래스를 제공합니다.

DecimalEncoder 클래스는 Python decimal.Decimal 값을 BSON Decimal128 값으로 변환합니다. DecimalDecoder 클래스는 BSON Decimal128 값을 Python decimal.Decimal 값으로 변환합니다.

다음 코드는 DecimalEncoder 클래스를 사용하여 십진수 1.0를 인코딩합니다.

opts = CodecOptions(type_registry=TypeRegistry([DecimalEncoder()]))
bson.encode({"d": decimal.Decimal('1.0')}, codec_options=opts)

다음 코드는 DecimalDecoder 클래스를 사용하여 BSON 데이터를 디코딩합니다.

opts = CodecOptions(type_registry=TypeRegistry([DecimalDecoder()]))
bson.decode(data, codec_options=opts)

운전자 기본적으로 직렬화할 수 없는 데이터 유형 저장 하려는 경우 사용자 지정 유형을 정의해야 할 수 있습니다. 예시 들어, 다음 코드 예시 와 같이 PyMongo 사용하여 Enum 인스턴스 저장하려고 하면 InvalidDocument 예외가 발생합니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
status = Status.ACTIVE
db["coll"].insert_one({"status": status})
Traceback (most recent call last):
...
bson.errors.InvalidDocument: Invalid document {'status': <Status.ACTIVE:
'active'>, '_id': ObjectId('68bb6144862a5cfb94a9fd48')} | cannot encode
object: <Status.ACTIVE: 'active'>, of type: <enum 'Status'>
from enum import Enum
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
status = Status.ACTIVE
await db["coll"].insert_one({"status": status})
Traceback (most recent call last):
...
bson.errors.InvalidDocument: Invalid document {'status': <Status.ACTIVE:
'active'>, '_id': ObjectId('68bb6144862a5cfb94a9fd48')} | cannot encode
object: <Status.ACTIVE: 'active'>, of type: <enum 'Status'>

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

사용자 지정 유형을 인코딩하려면 먼저 유형 코덱을 정의해야 합니다. 유형 코덱은 사용자 지정 유형의 인스턴스가 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이 인코딩할 수 있는 유형으로 변환하는 함수

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

from bson.codec_options import TypeCodec
class EnumCodec(TypeCodec):
python_type = Status
bson_type = str
def transform_python(self, value):
return value.value
def transform_bson(self, value):
try:
return Status(value)
except ValueError:
return value

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

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

from bson.codec_options import TypeRegistry
enum_codec = EnumCodec()
type_registry = TypeRegistry([enum_codec])

참고

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

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

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

그런 다음 Status 클래스의 인스턴스를 인코딩 및 디코딩할 수 있습니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

import pprint
collection.insert_one({"status": Status.ACTIVE})
my_doc = collection.find_one({"status": "active"})
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'status': <Status.ACTIVE: 'active'>}
import pprint
await collection.insert_one({"status": Status.ACTIVE})
my_doc = await collection.find_one({"status": "active"})
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'status': <Status.ACTIVE: 'active'>}

MongoDB 사용자 정의 유형의 인스턴스 저장하는 방법을 확인하려면 사용자 정의 코덱 옵션 없이 새 컬렉션 객체 만든 다음 이를 사용하여 사용자 정의 유형이 포함된 문서 조회 . 다음 예시 는 PyMongo Status 클래스의 인스턴스 str 값으로 저장하는 것을 보여줍니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

import pprint
new_collection = database.get_collection("test")
pprint.pprint(new_collection.find_one())
{'_id': ObjectId('...'), 'status': 'active'}
import pprint
new_collection = database.get_collection("test")
pprint.pprint(await new_collection.find_one())
{'_id': ObjectId('...'), 'status': 'active'}

사용자 지정 유형에서 상속되는 하나 이상의 유형을 인코딩해야 할 수도 있습니다. 상태가 활성 상태 나타내는지 확인하는 메서드가 포함된 Status 열거형 형 클래스의 다음 하위 유형을 고려하세요.

class ExtendedStatus(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
PENDING = "pending"
def is_active(self):
return self == ExtendedStatus.ACTIVE

먼저 유형 코덱을 등록하지 않고 ExtendedStatus 클래스의 인스턴스 저장하려고 하면 PyMongo 오류가 발생합니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

collection.insert_one({"status": ExtendedStatus.ACTIVE})
Traceback (most recent call last):
...
bson.errors.InvalidDocument: Invalid document {'status': <ExtendedStatus.ACTIVE: 'active'>, '_id': ObjectId('...')} | cannot encode object: <ExtendedStatus.ACTIVE: 'active'>, of type: <enum 'ExtendedStatus'>
await collection.insert_one({"status": ExtendedStatus.ACTIVE})
Traceback (most recent call last):
...
bson.errors.InvalidDocument: Invalid document {'status': <ExtendedStatus.ACTIVE: 'active'>, '_id': ObjectId('...')} | cannot encode object: <ExtendedStatus.ACTIVE: 'active'>, of type: <enum 'ExtendedStatus'>

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

class ExtendedStatusCodec(EnumCodec):
@property
def python_type(self):
# The Python type encoded by this type codec
return ExtendedStatus

그런 다음 하위 클래스의 유형 코덱을 유형 레지스트리에 추가하고 사용자 지정 유형의 인스턴스를 인코딩할 수 있습니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

import pprint
from bson.codec_options import CodecOptions
extended_status_codec = ExtendedStatusCodec()
type_registry = TypeRegistry([enum_codec, extended_status_codec])
codec_options = CodecOptions(type_registry=type_registry)
collection = database.get_collection("test", codec_options=codec_options)
collection.insert_one({"status": ExtendedStatus.ACTIVE})
my_doc = collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'status': <Status.ACTIVE: 'active'>}
import pprint
from bson.codec_options import CodecOptions
extended_status_codec = ExtendedStatusCodec()
type_registry = TypeRegistry([enum_codec, extended_status_codec])
codec_options = CodecOptions(type_registry=type_registry)
collection = database.get_collection("test", codec_options=codec_options)
await collection.insert_one({"status": ExtendedStatus.ACTIVE})
my_doc = await collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'status': <Status.ACTIVE: 'active'>}

참고

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

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

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

from enum import Enum
def fallback_encoder(value):
if isinstance(value, Enum):
return value.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)

그런 다음 컬렉션 에 대한 이 참조를 사용하여 Status 클래스의 인스턴스를 저장 수 있습니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

import pprint
collection.insert_one({"status": Status.ACTIVE})
my_doc = collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'status': 'active'}
import pprint
await collection.insert_one({"status": Status.ACTIVE})
my_doc = await collection.find_one()
pprint.pprint(my_doc)
{'_id': ObjectId('...'), 'status': 'active'}

참고

폴백 인코더는 표준 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,)

열거형 인스턴스를 문자열 값으로 변환하여 처리하거나 저장 위해 다른 객체를 피클하는 폴백 인코더를 정의할 수 있습니다. 다음 예시 에서는 다양한 유형의 사용자 지정 객체를 처리하다 방법을 보여 줍니다.

import pickle
from enum import Enum
def fallback_pickle_encoder(value):
if isinstance(value, Enum):
return value.value
return pickle.dumps(value).decode('latin-1')
class PickledStringDecoder(TypeDecoder):
bson_type = str
def transform_bson(self, value):
try:
# Try to unpickle the string value
return pickle.loads(value.encode('latin-1'))
except:
# If unpickling fails, return the original string
return value

그런 다음 유형 레지스트리의 폴백 인코더를 사용하여 사용자 지정 유형을 인코딩 및 디코딩할 수 있습니다. Synchronous 또는 Asynchronous 탭 선택하여 해당 코드를 확인합니다.

from bson.codec_options import CodecOptions,TypeRegistry
codec_options = CodecOptions(
type_registry=TypeRegistry(
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
from bson.codec_options import CodecOptions,TypeRegistry
codec_options = CodecOptions(
type_registry=TypeRegistry(
fallback_encoder=fallback_pickle_encoder
)
)
collection = db.get_collection("test", codec_options=codec_options)
await collection.insert_one(
{"_id": 1, "str": MyStringType("hello world"), "num": MyNumberType(2)}
)
my_doc = await 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

  • Decimal128

돌아가기

직렬화

이 페이지의 내용