Как импортировать сложный json do django объект модели - PullRequest
0 голосов
/ 21 февраля 2020

Я получил из списка сетевых служб следующую команду:

{
    '_id': 'bcEsX4Mhridf7Fdz59nYcm',
    'stats': {
        'acqs': 0,
        'alerts': 0,
        'blocks': 0,
        'false_positive_alerts': 0,
        'false_positive_alerts_by_source': {},
        'malware_false_positive_alerts': 0
    },
    'hostname': 'SomePC',
    'domain': 'local',
    'url': '/hx/api/v3/hosts/bcEsX4Mhridf7Fdz59nYcm',
    'sysinfo': {
        'url': '/hx/api/v3/hosts/bcEsX4Mhridf7Fdz59nYcm/sysinfo'
    },
    'os': {
        'product_name': 'Windows 10 Pro',
        'patch_level': None,
        'bitness': '64-bit',
        'platform': 'win',
        'kernel_version': None
    },
}

Я хочу импортировать ее в django объект модели. Мой models.py содержит следующий код:

class Host(models.Model):
    _id = models.CharField(max_length=25, primary_key=True)
    hostname = models.CharField(max_length=25)
    domain = models.CharField(max_length=253)
    url = models.CharField(max_length=50)
    def __str__(self):
        """String for representing the Model object."""
    return '{0}_({1})'.format(self.hostname, self._id)

class stats(models.Model):
    host = models.OneToOneField(Host, on_delete=models.CASCADE, null=True)
    acqs = models.IntegerField(default=0)
    alerts = models.IntegerField(default=0)
    blocks = models.IntegerField(default=0)
    false_positive_alerts = models.IntegerField(default=0)
    malware_false_positive_alerts = models.IntegerField(default=0)
    def __str__(self):
        """String for representing the Model object."""
        return '{0}/{1}/{2}'.format(
            self.acqs, 
            self.alerts, 
            self.blocks
            )

class sysinfo(models.Model):
    host = models.OneToOneField(Host, on_delete=models.CASCADE, null=True)
    url = models.CharField(max_length=45, blank=True)
    def __str__(self):
        """String for representing the Model object."""
        return '{0}'.format(self.url)

class os(models.Model):
    host = models.OneToOneField(Host, on_delete=models.CASCADE, null=True)
    product_name = models.CharField(max_length=253)
    patch_level = models.CharField(max_length=253, blank=True, null=True)
    bitness = models.CharField(max_length=10, blank=True, null=True)
    platform = models.CharField(max_length=10, blank=True, null=True)
    kernel_version = models.CharField(max_length=10, blank=True, null=True)
    def __str__(self):
        """String for representing the Model object."""
        return '{0}'.format(self.product_name)

Если упомянутый dict будет плоским, я могу импортировать его в объект модели следующим кодом:

h = Host(**my_dict)
h.save()

Но не могу найти путь поставить вложенный / сложный диктат моим модельным объектам.
Когда я пытаюсь h = Host(**my_dict) я получаю сообщение об ошибке:

ValueError                                Traceback (most recent call last)
<ipython-input-37-1250f570aeb3> in <module>
----> 1 h_new2 = Host(**my_dict2)
C:\Program Files\Python37\lib\site-packages\django\db\models\base.py in __init__(self, *args, **kwargs)
    493                     if prop in property_names or opts.get_field(prop):
    494                         if kwargs[prop] is not _DEFERRED:
--> 495                             _setattr(self, prop, kwargs[prop])
    496                         del kwargs[prop]
    497                 except (AttributeError, FieldDoesNotExist):

C:\Program Files\Python37\lib\site-packages\django\db\models\fields\related_descriptors.py in __set__(self, instance, value)
    462                     instance._meta.object_name,
    463                     self.related.get_accessor_name(),
--> 464                     self.related.related_model._meta.object_name,
    465                 )
    466             )
ValueError: Cannot assign "{'url': '/hx/api/v3/hosts/po1qmpQ2L3jdwQKKWS1CJm/sysinfo'}": "Host.sysinfo" must be a "sysinfo" instance.

Что не так с моими моделями?

Ответы [ 2 ]

1 голос
/ 21 февраля 2020

Django не обрабатывает построение связанных экземпляров модели автоматически. Вы должны позаботиться об этом сами.

Я бы сделал следующее в вашем случае.

# Assume "data" holds your data structure

stats_data = data.pop('stats')
os_data = data.pop('os')  # Clashes with os module - please don't
sysinfo_data = data.pop('sysinfo')

# store the FK in data
data['stats'] = stats(**stats_data)
data['os'] = os(**os_data)
data['sysinfo'] = sysinfo(**sysinfo_data)

host_instance = Host(**data)

Я также рекомендую использовать менеджер контекста транзакций atomi c, так что вы не Если во время создания экземпляра Host что-то пойдет не так, у вас не останется никаких данных.

from django.db import transaction


with transaction.atomic():

    # Code from above

Чтобы повысить удобочитаемость вашего кода, вы должны следовать рекомендациям по кодам PEP8 и Django. Это также облегчает вашу помощь, иначе люди могут запутаться в том, что такое модель, метод и т. Д. c. является. https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#model стиль

Например: имена моделей должны быть CamelCase

0 голосов
/ 21 февраля 2020

Я могу предложить вам создать новый класс модели, такой как: StatsItem

, то есть:

class StatsItem:
def __init__(self, data):
    self.acqs = data.get("acqs")
    self.alerts = data.get("alerts")
    ..
    ..
    self.blocks = data.get("blocks")

, затем передайте json файл конструктору:

stats = StatsItem(data.get('stats'))

теперь у вас есть переменная статистики, и вы можете легко получить любое значение:

blocks_value = stats.blocks

, и если вы создадите функцию внутри StatsItem (я назвал ее request_data):

def to_request_data(self):
    return dict(
        acqs=self.acqs if self.acqs else None,
        alerts=self.alerts,
        ..
        ..
        blocks=self.blocks
    )

this способ возврата dict, к которому также легко получить доступ:

data = stats.requested_data() ## stats is the variable that we initialize above.
blocks = data.get('blocks')

надеюсь, что это поможет,

...