Skip to content

JSON

A mixin for converting models to a JSON string and back to a Model.

JsonMixin

A mixin for converting Model to a JSON-string and back to a Model.

Source code in src/ramifice/json.py
class JsonMixin:
    """A mixin for converting Model to a JSON-string and back to a Model."""

    def to_dict(self, only_value: bool = False) -> dict[str, Any]:
        """Convert Model instance to a dictionary.

        Agrs:
            only_value (bool): True if need without field attributes.
        """
        metadata = self.__class__.META
        DESCRIPTOR_FIELDS = metadata["all_descriptor_fields"]
        LANG_CODE = self._LANG_CODE
        UTC_TIMEZONE = self._UTC_TIMEZONE
        json_dict: dict[str, Any] = {"_lang_": LANG_CODE}

        for f_name in DESCRIPTOR_FIELDS:
            tmp__core = deepcopy(getattr(self, f"{f_name}__core"))
            field_type = tmp__core.field_type
            value = tmp__core.value

            if value is not None:
                if field_type == "IDField":
                    tmp__core.value = str(value)
                elif field_type == "PasswordField":
                    tmp__core.value = None
                elif field_type == "TextField":
                    tmp__core.value = value.get(LANG_CODE, "- -") if isinstance(value, dict) else value
                elif "Date" in field_type:
                    if "Time" in field_type:
                        tmp__core.value = format_datetime(
                            datetime=value,
                            format="medium",
                            tzinfo=UTC_TIMEZONE,
                            locale=LANG_CODE,
                        )
                    else:
                        tmp__core.value = format_date(
                            date=value,
                            format="medium",
                            locale=LANG_CODE,
                        )
            json_dict[f_name] = tmp__core.to_dict() if not only_value else tmp__core.value

        return json_dict

    def to_json(self, only_value: bool = False) -> str:
        """Convert Model instance to a JSON-string.

        Agrs:
            only_value (bool): True if need without field attributes.
        """
        return orjson.dumps(self.to_dict(only_value)).decode("utf-8")

    @classmethod
    def from_dict(
        cls,
        json_dict: dict[str, Any],
    ) -> Any:
        """Convert JSON-dictionary to a Model instance."""
        metadata = cls.META
        DESCRIPTOR_FIELDS = metadata["all_descriptor_fields"]
        lang = json_dict.get("_lang_")

        # If there is no `_lang_` language marker in the JSON-dictionary
        lang = json_dict.get("_lang_")
        if lang is None:
            err_msg = "The JSON-dictionary does not contain the `_lang_` marker."
            logger.critical(err_msg)
            raise ValueError(err_msg)

        # If fields not contain attributes
        if not isinstance(json_dict.get("created_at"), dict):
            return cls.from_ajax_json(json_dict, lang)

        # pyrefly: ignore [bad-argument-count]
        instance_model: Any = cls(lang)
        DATEPARSER_SETTINGS = instance_model.dateparser_settings

        for f_name in DESCRIPTOR_FIELDS:
            tmp__core_dict = deepcopy(json_dict[f_name])
            field_type = tmp__core_dict["field_type"]
            value = tmp__core_dict["value"]

            if value is not None:
                if field_type == "IDField":
                    tmp__core_dict["value"] = ObjectId(value)
                elif "Date" in field_type:
                    if "Time" in field_type:
                        tmp__core_dict["value"] = parse(
                            value,
                            settings=DATEPARSER_SETTINGS,
                        ).replace(microsecond=0)
                    else:
                        tmp__core_dict["value"] = parse(
                            value,
                            settings=DATEPARSER_SETTINGS,
                        ).date()
                else:
                    tmp__core_dict["value"] = value

            setattr(instance_model, f_name, tmp__core_dict["value"])
            f__core = getattr(instance_model, f"{f_name}__core")
            for key, val in tmp__core_dict.items():
                f__core.__dict__[key] = val

        return instance_model

    @classmethod
    def from_json(
        cls,
        json_str: str,
    ) -> Any:
        """Convert JSON-string of Model to a Model instance."""
        json_dict = orjson.loads(json_str)
        lang = json_dict.get("_lang_")
        # If there is no `_lang_` language marker in the JSON-dictionary
        if lang is None:
            return cls.from_ajax_json(json_dict, lang)
        # If fields contain attributes
        return cls.from_dict(json_dict)

    @classmethod
    def from_ajax_json(cls, json_str_or_dict: dict[str, Any] | str, lang_code: str) -> Any:
        """Convert JSON-string from web request to a Model instance.

        If the JSON-string does not contain the field attributes and the `_lang_` language marker.

        Hint:
        - `lang_code` - This is necessary for multilingual (is_multilingual=True) text fields.
        """
        metadata = cls.META
        DESCRIPTOR_FIELDS = metadata["all_descriptor_fields"]
        json_dict: dict[str, Any] = (
            orjson.loads(json_str_or_dict) if isinstance(json_str_or_dict, str) else json_str_or_dict
        )

        # If fields contain attributes
        if isinstance(json_dict.get("created_at"), dict):
            err_msg = "Fields should not contain attributes, only values."
            logger.critical(err_msg)
            raise ValueError(err_msg)

        # pyrefly: ignore [bad-argument-count]
        instance_model: Any = cls(lang_code)
        DATEPARSER_SETTINGS = instance_model.dateparser_settings

        for f_name in DESCRIPTOR_FIELDS:
            value = json_dict.get(f_name if f_name != "id" else "_id")

            if value is None:
                continue

            f__core = getattr(instance_model, f"{f_name}__core")
            field_type = f__core.field_type

            if field_type == "IDField":
                setattr(instance_model, f_name, ObjectId(value))
            elif "Date" in field_type:
                if "Time" in field_type:
                    setattr(
                        instance_model,
                        f_name,
                        parse(
                            value,
                            settings=DATEPARSER_SETTINGS,
                        ).replace(microsecond=0),
                    )
                else:
                    setattr(
                        instance_model,
                        f_name,
                        parse(
                            value,
                            settings=DATEPARSER_SETTINGS,
                        ).date(),
                    )
            else:
                setattr(instance_model, f_name, value)

        return instance_model

from_ajax_json(json_str_or_dict, lang_code) classmethod

Convert JSON-string from web request to a Model instance.

If the JSON-string does not contain the field attributes and the _lang_ language marker.

Hint: - lang_code - This is necessary for multilingual (is_multilingual=True) text fields.

Source code in src/ramifice/json.py
@classmethod
def from_ajax_json(cls, json_str_or_dict: dict[str, Any] | str, lang_code: str) -> Any:
    """Convert JSON-string from web request to a Model instance.

    If the JSON-string does not contain the field attributes and the `_lang_` language marker.

    Hint:
    - `lang_code` - This is necessary for multilingual (is_multilingual=True) text fields.
    """
    metadata = cls.META
    DESCRIPTOR_FIELDS = metadata["all_descriptor_fields"]
    json_dict: dict[str, Any] = (
        orjson.loads(json_str_or_dict) if isinstance(json_str_or_dict, str) else json_str_or_dict
    )

    # If fields contain attributes
    if isinstance(json_dict.get("created_at"), dict):
        err_msg = "Fields should not contain attributes, only values."
        logger.critical(err_msg)
        raise ValueError(err_msg)

    # pyrefly: ignore [bad-argument-count]
    instance_model: Any = cls(lang_code)
    DATEPARSER_SETTINGS = instance_model.dateparser_settings

    for f_name in DESCRIPTOR_FIELDS:
        value = json_dict.get(f_name if f_name != "id" else "_id")

        if value is None:
            continue

        f__core = getattr(instance_model, f"{f_name}__core")
        field_type = f__core.field_type

        if field_type == "IDField":
            setattr(instance_model, f_name, ObjectId(value))
        elif "Date" in field_type:
            if "Time" in field_type:
                setattr(
                    instance_model,
                    f_name,
                    parse(
                        value,
                        settings=DATEPARSER_SETTINGS,
                    ).replace(microsecond=0),
                )
            else:
                setattr(
                    instance_model,
                    f_name,
                    parse(
                        value,
                        settings=DATEPARSER_SETTINGS,
                    ).date(),
                )
        else:
            setattr(instance_model, f_name, value)

    return instance_model

from_dict(json_dict) classmethod

Convert JSON-dictionary to a Model instance.

Source code in src/ramifice/json.py
@classmethod
def from_dict(
    cls,
    json_dict: dict[str, Any],
) -> Any:
    """Convert JSON-dictionary to a Model instance."""
    metadata = cls.META
    DESCRIPTOR_FIELDS = metadata["all_descriptor_fields"]
    lang = json_dict.get("_lang_")

    # If there is no `_lang_` language marker in the JSON-dictionary
    lang = json_dict.get("_lang_")
    if lang is None:
        err_msg = "The JSON-dictionary does not contain the `_lang_` marker."
        logger.critical(err_msg)
        raise ValueError(err_msg)

    # If fields not contain attributes
    if not isinstance(json_dict.get("created_at"), dict):
        return cls.from_ajax_json(json_dict, lang)

    # pyrefly: ignore [bad-argument-count]
    instance_model: Any = cls(lang)
    DATEPARSER_SETTINGS = instance_model.dateparser_settings

    for f_name in DESCRIPTOR_FIELDS:
        tmp__core_dict = deepcopy(json_dict[f_name])
        field_type = tmp__core_dict["field_type"]
        value = tmp__core_dict["value"]

        if value is not None:
            if field_type == "IDField":
                tmp__core_dict["value"] = ObjectId(value)
            elif "Date" in field_type:
                if "Time" in field_type:
                    tmp__core_dict["value"] = parse(
                        value,
                        settings=DATEPARSER_SETTINGS,
                    ).replace(microsecond=0)
                else:
                    tmp__core_dict["value"] = parse(
                        value,
                        settings=DATEPARSER_SETTINGS,
                    ).date()
            else:
                tmp__core_dict["value"] = value

        setattr(instance_model, f_name, tmp__core_dict["value"])
        f__core = getattr(instance_model, f"{f_name}__core")
        for key, val in tmp__core_dict.items():
            f__core.__dict__[key] = val

    return instance_model

from_json(json_str) classmethod

Convert JSON-string of Model to a Model instance.

Source code in src/ramifice/json.py
@classmethod
def from_json(
    cls,
    json_str: str,
) -> Any:
    """Convert JSON-string of Model to a Model instance."""
    json_dict = orjson.loads(json_str)
    lang = json_dict.get("_lang_")
    # If there is no `_lang_` language marker in the JSON-dictionary
    if lang is None:
        return cls.from_ajax_json(json_dict, lang)
    # If fields contain attributes
    return cls.from_dict(json_dict)

to_dict(only_value=False)

Convert Model instance to a dictionary.

Agrs

only_value (bool): True if need without field attributes.

Source code in src/ramifice/json.py
def to_dict(self, only_value: bool = False) -> dict[str, Any]:
    """Convert Model instance to a dictionary.

    Agrs:
        only_value (bool): True if need without field attributes.
    """
    metadata = self.__class__.META
    DESCRIPTOR_FIELDS = metadata["all_descriptor_fields"]
    LANG_CODE = self._LANG_CODE
    UTC_TIMEZONE = self._UTC_TIMEZONE
    json_dict: dict[str, Any] = {"_lang_": LANG_CODE}

    for f_name in DESCRIPTOR_FIELDS:
        tmp__core = deepcopy(getattr(self, f"{f_name}__core"))
        field_type = tmp__core.field_type
        value = tmp__core.value

        if value is not None:
            if field_type == "IDField":
                tmp__core.value = str(value)
            elif field_type == "PasswordField":
                tmp__core.value = None
            elif field_type == "TextField":
                tmp__core.value = value.get(LANG_CODE, "- -") if isinstance(value, dict) else value
            elif "Date" in field_type:
                if "Time" in field_type:
                    tmp__core.value = format_datetime(
                        datetime=value,
                        format="medium",
                        tzinfo=UTC_TIMEZONE,
                        locale=LANG_CODE,
                    )
                else:
                    tmp__core.value = format_date(
                        date=value,
                        format="medium",
                        locale=LANG_CODE,
                    )
        json_dict[f_name] = tmp__core.to_dict() if not only_value else tmp__core.value

    return json_dict

to_json(only_value=False)

Convert Model instance to a JSON-string.

Agrs

only_value (bool): True if need without field attributes.

Source code in src/ramifice/json.py
def to_json(self, only_value: bool = False) -> str:
    """Convert Model instance to a JSON-string.

    Agrs:
        only_value (bool): True if need without field attributes.
    """
    return orjson.dumps(self.to_dict(only_value)).decode("utf-8")