Django: Создание модели на основе разбора поля другой модели
У меня есть модель A с несколькими полями. Одно из полей ("results") является диктоподобной строкой в моей базе данных (которая едва ли читаема для человека). Могу ли я создать отдельную модель, которая будет разбирать это поле "результаты" на свои собственные поля, чтобы у меня была отдельная таблица с полями, соответствующими ключам и значениям поля "результаты" из модели A? Конечная цель - сделать таблицу результатов, которая показывает всю информацию в красивой и легко читаемой форме.
class ModelA(models.Model):
language = models.CharField(max_length=30)
date = models.DateTimeField()
results = models.CharField(max_length=255)
Вот как поле "результаты" выглядит в моей базе данных (я не могу изменить этот формат):
OrderedDict([('Name', 'Bob'), ('Phone', '1234567890'), ('born', '01.01.1990')]))
Я хочу создать что-то вроде этого:
class Results(models.Model):
name = model.Charfield(max_length=100)
phone= model.IntegerField()
born = model.DateTimeField()
Каким образом лучше всего это сделать? Как мне взять информацию из поля "результаты" из моделиА и "поместить" ее в модель Results?
Вы можете сделать функцию, которая преобразует его обратно в словарь с помощью:
from ast import literal_eval
from datetime import datetime
def results_process(text):
if not (text.startswith('OrderedDict(') and text.endswith(')')):
raise ValueError('Invalid format')
data = dict(literal_eval(text[12:-1]))
return Results.objects.create(
name=data['Name'],
phone=data['Phone'],
born=datetime.strptime(data['born'], '%d.%m.%Y')
)
для заданной строки, содержащейся в results объекте ModelA, он таким образом построит Results объект модели, хранящийся в базе данных.
Вы можете создать свойства модели, которые заставят пары ключ:значение в этой дикте выглядеть так, как будто это обычные поля модели для многих целей кодирования (но не querysets или ModelForms)
Вообще-то я бы очень очень хотел изменить результаты на JSON-поле, но я предположу, что вы не используете PostgreSQL ...
class ModelA(models.Model):
results = models.CharField(max_length=255)
@property
def name(self):
if not hasattr(self, _rdict):
self._parse_results()
return self._rdict['name']
@name.setter
def name( self, value):
if not hasattr(self, _rdict):
self._parse_results()
self._rdict['name'] = value
# similarly for the other keys in results
# if thre are lots of keys, to be DRY investigate the property builtin
# field = property( getter, setter)
def _parse_results(self):
d = OrderedDict()
# use self.results to turn results back into an OrderedDict
# code (use python re module? ) tbs
# set d['name'] = parsed_name etc in the right order!
self._rdict = d
def save( self, *args, **kwargs):
# rewrite results to reflect _rdict contents
# this is a hack implementation
# (but I suspect it's how results is generated)
self.results = str( self._rdict)
super().save(*args, **kwargs)
Вы могли бы сэкономить все эти hasattr тесты, если бы вместо этого декодировали результаты в __init__ модели после вызова super().__init__. Я не знаю почему, но Django doc предостерегает от этого (или раньше предостерегал). Я сделал это с одной из своих моделей, и пока что никаких плохих последствий я не заметил. Вмешательство до того, как суперкласс сделает свою магию, может быть тем, от чего он предостерегает. Сейчас я не могу найти это предупреждение.
Если вы хотите использовать (скажем) модельформ, основанный на "полях" в результатах. есть другой способ, который может помочь: наследование по нескольким таблицам. Я не знаю достаточно, чтобы рекомендовать это. Он создает автоматическое отношение OneToOne между текущей таблицей DB и вашей новой моделью. Затем вам придется подклассифицировать метод __init__ вашей модели, чтобы расшифровать результаты. Удачи, если вы попытаетесь сделать это (и если у вас получится, опубликуйте то, что вы сделали, здесь в качестве ответа на свой вопрос, потому что я хотел бы знать! )
Я бы сказал связать две модели с foreignKey, чтобы получить доступ к ним как к словарю в вашем представлении. Свяжите так:
class ModelA(models.Model):
language = models.CharField(max_length=30)
date = models.DateTimeField()
results = models.CharField(max_length=255)
class Results(models.Model):
# Here we are creating a relationship between the two model
# so that we can get their data by related names.
model_a = models.ForeignKey(MadelA,on_delete=models.CASCADE)
name = model.Charfield(max_length=100)
phone= model.IntegerField()
born = model.DateTimeField()
В нашем представлении мы можем получить данные для обеих моделей, используя их связанное имя, так как django предоставляет связанное имя по умолчанию, если оно не предоставлено, как в моем решении, поэтому связанное имя для двух моделей будет results_set, которое будет использоваться для получения данных для обеих моделей в нашем представлении, будь то словарь, список или кортеж, когда мы запрашиваем данные из бэкенда.
getting both data by related name
ModelA.objects.prefetch_related('results_set').dict()