I'd like to convert a string into dict. The input string is taken from the database. Below is the log.before (this is the given string from the databse)given. {'id': '25c16650-070c-4e17-8996-f8d79d1747d7', 'tariff': OrderedDict([('id', 1), ('title', 'dTtvdvemDLYDHZiPNfaDwgAsTopnQBvYiHVPJvlREQaqLtlJFkQVFKZhpmWWphaxedqzWZBcpXxglKZvgpzPzPVEkSnJfEHnVfHy'), ('price', '0.00'), ('label', '(None) dTtvdvemDLYDHZiPNfaDwgAsTopnQBvYiHVPJvlREQaqLtlJFkQVFKZhpmWWphaxedqzWZBcpXxglKZvgpzPzPVEkSnJfEHnVfHy')]), 'title': 'Test Product', 'price': None, 'establishment': 0.0, 'billing_period': 'M', 'additional_points': [], 'speed_unit': 'Gb', 'service_type': 'P', 'type': 'S', 'show_with_vat': True, 'price_with_vat': 0, 'establishment_with_vat': 0, 'has_express_delivery': False, 'conversion_reporting_configs': [], 'index': 1, 'speed_up': 'None', 'speed_down': 'None'} In order to convert this string to dict, I've written like this. try: before = ast.literal_eval(log.before) if log.before else {} except Exception as e: print(e) And there's an error like this. malformed node or string: <ast.Call object at 0x7fb3b60d94f0> I think the problem is with the OrderedDict in the string given. Just for the record, below is the full code snippet. import csv import ast # import json # import re from collections import OrderedDict from io import StringIO from core.celery import app as celery_app from django.utils.timezone import now, timedelta from django.core.mail import EmailMessage from django.conf import settings from intern.apps.geo.models import GeoLog, GeoUpload, GeoUploadProcess from intern.apps.geo.processors import GeoFileProcessor @celery_app.task def generate_geo_manager_report(start_date=None, end_date=None): if start_date is None and end_date is None: end_date = now() start_date = end_date - timedelta(days=7) logs = GeoLog.objects.filter(when__range=[start_date, end_date]) if not logs.exists(): print("No logs found, exiting task.") return stats = {model: {action: 0 for action in GeoLog.Action.values} for model in GeoLog.ModelType.values} csv_files = {model: [] for model in GeoLog.ModelType.values} for log in logs: if log.model_type is None: print(f"Skipping log due to None model_type: {log}") continue print("GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG") stats[log.model_type][log.action] += 1 print(f"Raw before: {log.before}") print(f"Raw after: {log.after}") try: before = ast.literal_eval(log.before) if log.before else {} except Exception as e: print(e) try: after = ast.literal_eval(log.after) if log.after else {} except Exception as e: print(e) print(f"Serialized after: {before}") print(f"Serialized after: {after}") if log.action == 'update': changes = { key: (before[key], after[key]) if before.get(key) != after.get(key) else before[key] for key in set(before) | set(after) } else: changes = {**before, **after} # print(f"Serialized before: {before}") # print(f"Serialized after: {after}") csv_files[log.model_type].append({ 'changes': changes, 'action': log.action, 'when': log.when, 'employee': log.employee.username if log.employee else 'System' }) print("eeeeeeeeeeeeeeeeeeeeeeeeeeeee") print(f"Serialized csv_flies: {type(csv_files)}") attachments = [] for model, rows in csv_files.items(): if rows: output = StringIO() writer = csv.DictWriter(output, fieldnames=rows[0].keys()) writer.writeheader() writer.writerows(rows) attachments.append((f"{model}.csv", output.getvalue())) subject = settings.GEO_MANAGER_REPORT_SUBJECT.format( start_date=start_date.strftime("%d/%m/%Y"), end_date=end_date.strftime("%d/%m/%Y"), ) body = ( f"Geo Manager Report for period {start_date.strftime('%d/%m/%Y')} to {end_date.strftime('%d/%m/%Y')}\n\n" + "\n".join( f"{sum(stats[model].values())} changes in {model} ({', '.join(f'{count} {action}' for action, count in actions.items() if count > 0)})" for model, actions in stats.items() ) ) print("Sending email...") email = EmailMessage(subject, body, to=settings.GEO_MANAGER_REPORT_RECIPIENTS) for filename, content in attachments: email.attach(filename, content, 'text/csv') email.send() Continue reading...