1

I have a model A with several fields. One of the fields ("results") is a dict-like string in my database (which is hardly readable for a human being). Can I create a separate Model that would parse that "results" field into its fields to have a different table with fields corresponding to the keys and values from my "results" field from model A? The final goal is to make a Results table that shows all the information pretty and easy-to-read manner.

class ModelA(models.Model):
    language = models.CharField(max_length=30)
    date = models.DateTimeField()
    results = models.CharField(max_length=255)

This is how "results" field looks in my database (I cannot change this format):

OrderedDict([('Name', 'Bob'), ('Phone', '1234567890'), ('born', '01.01.1990')])

I want to create something like this:

class Results(models.Model):
    name = model.Charfield(max_length=100)
    phone= model.IntegerField()
    born = model.DateTimeField()

What is the best way to do this? How do I take the info from the "results" field from ModelA and "put" it into the Results model?

3
  • Are you using DRF or do you want to achieve it in clear Django? Commented Jan 28, 2022 at 10:54
  • I want to do it in pure Django. Commented Jan 28, 2022 at 10:58
  • Just link the two models with a foreignKey .... Commented Jan 28, 2022 at 11:16

3 Answers 3

1

You can make a function that transforms it back to a dictionary with:

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')
    )

for a given string contained in results of a ModelA object, it will thus construct a Results model object stored in the database.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the response, Willem. Do I create this function inside the ModelA class or the Results class? If it's the former, do I need to create an empty Results class first (so that Results.objects.create can happen); if it's the latter, the function does not reference to the ModelA class in any way. I want to see the Results model (with all the pretty fields) in the Django Admin. Sorry for stupid questions, I'm still learning.
0

I would say to link the two models with a foreignKey so accessing them as a dictionary in your view. link so:

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()

In our view we can get data to both models by using its related name, as django would provide a default related name if one is not provided as in my solution, so the related name for the two models would be results_set which would be use to retrieved our data for both model in our view, whether in dictionary,list or tuple when we query the data from the backend.


getting both data by related name

ModelA.objects.prefetch_related('results_set').dict()



Comments

0

You can create properties on the model, which will make the key:value pairs in that dict appear as if they are ordinary model fields for a lot of coding purposes (but not querysets or ModelForms)

In passing I'd really really want to change results to a JSONfield, but I'll assume you aren't using 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)

If you just want to be able to read these values as if they were fields, but not update them back to the database, you can dispense with a lot of this:

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']

    def _parse_results(self):
       # as above

    # def save( ...) is not needed if the properties are read-only

You might save all those hasattr tests if you instead decoded results in the model's __init__ after super().__init__ called. I don't know why, but the Django doc warns against this (or used to). I have done it with one of my models, and so far no ill consequences I am aware of. Interfering before the superclass does its magic may be what it was warning against. I can't find the warning now.

If you wanted to use (say) modelforms based on the "fields" in results. there's another way that might help: multi-table inheritance. I don't know enough to recommend this. It creates an automagical OneToOne relation between the current DB table and your new model. You'd then have to subclass the __init__ method of your model to decode the results. Good luck if you attempt this (and if you do get it working, post what you did back here as an answer to your own question, because I'd like to know! )

3 Comments

Wow, this looks like it may actually work if only I was smart enough to understand the whole thing. For example, where did _rdict come from, why is it not self._rdict. Also, I don't know how and why to "turn results back into an OrderedDict" when my goal is to have a Results table in the django admin that shows all the pretty newly created fields with their values. And I'm not sure what to do with the save method here. I'm in over my head here with this one, it seems. I'll keep at it, maybe it'll come to me eventually. Thank you.
Typos in my code. It's not tested. Should be hasattr('_rdict'). Fixing it.
If you aren't ever going to be updating the data in results through the properties, you don't need setters at all. Then you don't need a subclassed save method either.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.