Проверка структуры str, возвращенной из данных через модель данных - PullRequest
0 голосов
/ 26 марта 2020

Я пытаюсь найти лучший способ проверить целостность данных, которые я получаю обратно от API.

Например, у меня есть набор данных, соответствующий модели (IContact).

Данные:

{
    'aemBounceBack': False,
    'aemOptOut': False,
    'altEmailAddress': None,
    'alternateCountryCode': None,
    'alternateExtension': None,
    'alternateMaskFormat': None,
    'alternatePhone': None,
    'amaScore': None,
    'birthday': None,
    'businessAddress': {
        'city': None,
        'country': None,
        'latitude': 0.0,
        'line1': None,
        'line2': None,
        'line3': None,
        'longitude': 0.0,
        'postalCode': None,
        'state': None},
    'businessCountryCode': None,
    'businessExtension': None,
    'businessMaskFormat': None,
    'businessPhone': None,
    'company': None,
    'companyID': '',
    'contactType': 'User',
    'created': '2020-03-20T03:36:51+00:00',
    'customFields': {
        'spouse': None,
        'user1': None,
        'user10': None,
        'user2': None,
        'user3': None,
        'user4': None,
        'user5': None,
        'user6': None,
        'user7': None,
        'user8': None,
        'user9': None
    },
    'department': None,
    'edited': '2020-03-26T00:36:12+00:00',
    'editedBy': 'Admin',
    'emailAddress': None,
    'faxCountryCode': None,
    'faxExtension': None,
    'faxMaskFormat': None,
    'faxPhone': None,
    'firstName': '',
    'fullName': 'Admin',
    'homeAddress': {
        'city': None,
        'country': None,
        'latitude': 0.0,
        'line1': None,
        'line2': None,
        'line3': None,
        'longitude': 0.0,
        'postalCode': None,
        'state': None
    },
    'homeCountryCode': None,
    'homeExtension': None,
    'homeMaskFormat': None,
    'homePhone': None,
    'id': '0a7e183d-7e72-4d68-87a3-478bdbbd2356',
    'idStatus': None,
    'importDate': None,
    'isFavorite': 'False',
    'isImported': False,
    'isPrivate': False,
    'isUser': True,
    'jobTitle': None,
    'lastAttempt': None,
    'lastEmail': None,
    'lastLetterSent': None,
    'lastMeeting': None,
    'lastName': 'Admin',
    'lastReach': '2020-03-25T17:30:00+00:00',
    'lastResults': None,
    'latitude': 0.0,
    'longitude': 0.0,
    'messengerID': None,
    'middleName': '',
    'mobileCountryCode': None,
    'mobileExtension': None,
    'mobileMaskFormat': None,
    'mobilePhone': None,
    'namePrefix': '',
    'nameSuffix': '',
    'pagerCountryCode': None,
    'pagerExtension': None,
    'pagerMaskFormat': None,
    'pagerPhone': None,
    'personalEmailAddress': None,
    'recordManager': 'Admin',
    'recordOwner': 'Admin',
    'referredBy': None,
    'salutation': '',
    'website': None
}

Модель:

NoneType = type(None)


class IContact:
    id = (NoneType, str)
    idStatus = (NoneType, str)
    isUser = (NoneType, bool)
    company = (NoneType, str)
    department = (NoneType, str)
    companyID = (NoneType, str)
    contactType = (NoneType, str)
    namePrefix = (NoneType, str)
    firstName = (NoneType, str)
    middleName = (NoneType, str)
    lastName = (NoneType, str)
    nameSuffix = (NoneType, str)
    fullName = (NoneType, str)
    isFavorite = (NoneType, str)
    isImported = (NoneType, bool)
    importDate = (NoneType, str)
    isPrivate = (NoneType, bool)
    lastResults = (NoneType, str)
    lastEmail = (NoneType, str)
    lastAttempt = (NoneType, str)
    lastReach = (NoneType, str)
    lastMeeting = (NoneType, str)
    lastLetterSent = (NoneType, str)
    latitude = (NoneType, float)
    longitude = (NoneType, float)
    messengerID = (NoneType, str)
    referredBy = (NoneType, str)
    salutation = (NoneType, str)
    jobTitle = (NoneType, str)
    amaScore = (NoneType, int)
    emailAddress = (NoneType, str)
    altEmailAddress = (NoneType, str)
    personalEmailAddress = (NoneType, str)
    website = (NoneType, str)
    birthday = (NoneType, str)
    businessAddress = (NoneType, dict)
    businessPhone = (NoneType, str)
    businessExtension = (NoneType, str)
    businessCountryCode = (NoneType, int)
    businessMaskFormat = (NoneType, str)
    mobilePhone = (NoneType, str)
    mobileExtension = (NoneType, str)
    mobileCountryCode = (NoneType, int)
    mobileMaskFormat = (NoneType, str)
    faxPhone = (NoneType, str)
    faxExtension = (NoneType, str)
    faxCountryCode = (NoneType, int)
    faxMaskFormat = (NoneType, str)
    homeAddress = (NoneType, dict)
    homePhone = (NoneType, str)
    homeExtension = (NoneType, str)
    homeCountryCode = (NoneType, int)
    homeMaskFormat = (NoneType, str)
    alternatePhone = (NoneType, str)
    alternateExtension = (NoneType, str)
    alternateCountryCode = (NoneType, int)
    alternateMaskFormat = (NoneType, str)
    pagerPhone = (NoneType, str)
    pagerExtension = (NoneType, str)
    pagerCountryCode = (NoneType, int)
    pagerMaskFormat = (NoneType, str)
    aemOptOut = (NoneType, bool)
    aemBounceBack = (NoneType, bool)
    customFields = (NoneType, object)
    created = (NoneType, str)
    edited = (NoneType, str)
    editedBy = (NoneType, str)
    recordOwner = (NoneType, str)
    recordManager = (NoneType, str)

В моих тестах я oop через ключи, которые я получаю от API (json), и подтверждаю, что они соответствуют модели. Я использую NoneType, чтобы указать, что возвращаемый объект также может быть None - не уверен, что это хороший подход.

for k, v in json.items():
    assert isinstance(v, getattr(IContact, k))

Я хотел бы иметь возможность проверить значения для ключей со строками, структурированными указанными c способами, такими как datetime (формат iso), guids и т. д. c.

Каков наилучший подход для проверки этих структур данных в Python? Я использую Python 3.8.2, поэтому у меня есть все новейшие функции. Я кратко посмотрел на https://docs.python.org/3/library/dataclasses.html, но я не уверен, что это то, что мне нужно.

Ответы [ 2 ]

0 голосов
/ 03 мая 2020
from dataclasses import dataclass
from typing import Optional

from validated_dc import ValidatedDC


@dataclass
class IContact(ValidatedDC):
    id: Optional[str]
    idStatus: Optional[str]
    isUser: Optional[bool]


# Let's say you got a json-string and loaded it into a dictionary
data = {
    'id': '123',
    'idStatus': 'new',
    'isUser': True
}

icontact = IContact(**data)
assert icontact.is_valid()

data = {
    'id': 123,  # <-- Error, not str and not NoneType
    'idStatus': 'new',
    'isUser': True
}

icontact = IContact(**data)

assert not icontact.is_valid()

print(icontact.get_errors())
# {'id': [BasicValidationError(value_repr='123', value_type=<class 'int'>, 
# annotation=<class 'str'>, exception=None), BasicValidationError(
# value_repr='123', value_type=<class 'int'>, annotation=<class 'NoneType'>, 
# exception=None)]}

https://github.com/EvgeniyBurdin/validated_dc

0 голосов
/ 26 марта 2020

Я думаю, вы ищете JSONDecoder , использующий параметр object_hook для настройки декодирования. Вы можете добавить свою логику проверки c там. Эта поддержка простых типов из коробки. Может пройти большую часть пути. Я бы предложил утку набрать ваш результат. Если в нем есть данные, которые вам нужны, предположите, что запись правильная.

Если критично, что запись имеет правильный тип, то может быть проще проверить объект декодирования python, чем json сам по себе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...