# Django 2.2.9.dev20191202104323 文档

# django.contrib.postgres.fields.hstore 源代码

    import json

    from django.contrib.postgres import forms, lookups
    from django.contrib.postgres.fields.array import ArrayField
    from django.core import exceptions
    from django.db.models import Field, TextField, Transform
    from django.utils.translation import gettext_lazy as _

    from .mixins import CheckFieldDefaultMixin

    __all__ = ['HStoreField']

    class HStoreField(CheckFieldDefaultMixin, Field):
        empty_strings_allowed = False
        description = _('Map of strings to strings/nulls')
        default_error_messages = {
            'not_a_string': _('The value of "%(key)s" is not a string or null.'),
        }
        _default_hint = ('dict', '{}')

        def db_type(self, connection):
            return 'hstore'

        def get_transform(self, name):
            transform = super().get_transform(name)
            if transform:
                return transform
            return KeyTransformFactory(name)

        def validate(self, value, model_instance):
            super().validate(value, model_instance)
            for key, val in value.items():
                if not isinstance(val, str) and val is not None:
                    raise exceptions.ValidationError(
                        self.error_messages['not_a_string'],
                        code='not_a_string',
                        params={'key': key},
                    )

        def to_python(self, value):
            if isinstance(value, str):
                value = json.loads(value)
            return value

        def value_to_string(self, obj):
            return json.dumps(self.value_from_object(obj))

        def formfield(self, **kwargs):
            return super().formfield(**{
                'form_class': forms.HStoreField,
                **kwargs,
            })

        def get_prep_value(self, value):
            value = super().get_prep_value(value)

            if isinstance(value, dict):
                prep_value = {}
                for key, val in value.items():
                    key = str(key)
                    if val is not None:
                        val = str(val)
                    prep_value[key] = val
                value = prep_value

            if isinstance(value, list):
                value = [str(item) for item in value]

            return value

    HStoreField.register_lookup(lookups.DataContains)
    HStoreField.register_lookup(lookups.ContainedBy)
    HStoreField.register_lookup(lookups.HasKey)
    HStoreField.register_lookup(lookups.HasKeys)
    HStoreField.register_lookup(lookups.HasAnyKeys)

    class KeyTransform(Transform):
        output_field = TextField()

        def __init__(self, key_name, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.key_name = key_name

        def as_sql(self, compiler, connection):
            lhs, params = compiler.compile(self.lhs)
            return '(%s -> %%s)' % lhs, tuple(params) + (self.key_name,)

    class KeyTransformFactory:

        def __init__(self, key_name):
            self.key_name = key_name

        def __call__(self, *args, **kwargs):
            return KeyTransform(self.key_name, *args, **kwargs)

    @HStoreField.register_lookup
    class KeysTransform(Transform):
        lookup_name = 'keys'
        function = 'akeys'
        output_field = ArrayField(TextField())

    @HStoreField.register_lookup
    class ValuesTransform(Transform):
        lookup_name = 'values'
        function = 'avals'
        output_field = ArrayField(TextField())