Merge pull request #29196 from ankush/fix/meta_mutation

fix: Don't assume homogenous data in meta tables
This commit is contained in:
Ankush Menat 2025-01-16 20:41:02 +05:30 committed by GitHub
commit c8ec528aa7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 32 deletions

View file

@ -796,6 +796,16 @@ class TestDocType(IntegrationTestCase):
)
self.assertRaises(frappe.ValidationError, recursive_dt.insert)
def test_meta_serialization(self):
doctype = new_doctype(
fields=[{"fieldname": "some_fieldname", "fieldtype": "Data", "set_only_once": 1}],
is_submittable=1,
).insert()
doc = frappe.new_doc(doctype.name, some_fieldname="something").insert()
doc.save()
doc.submit()
frappe.get_meta(doctype.name).as_dict()
def new_doctype(
name: str | None = None,

View file

@ -19,7 +19,7 @@ import json
import os
import typing
from datetime import datetime
from functools import singledispatchmethod
from functools import cached_property, singledispatchmethod
from types import NoneType
import click
@ -188,6 +188,8 @@ class Meta(Document):
def as_dict(self, no_nulls=False):
def serialize(doc):
if isinstance(doc, dict):
return doc.copy()
out = {}
for key, value in doc.__dict__.items():
if isinstance(value, list | tuple):
@ -236,16 +238,19 @@ class Meta(Document):
def get_set_only_once_fields(self):
"""Return fields with `set_only_once` set"""
if not hasattr(self, "_set_only_once_fields"):
self._set_only_once_fields = self.get("fields", {"set_only_once": 1})
fieldnames = [d.fieldname for d in self._set_only_once_fields]
for df in self.standard_set_once_fields:
if df.fieldname not in fieldnames:
self._set_only_once_fields.append(df)
return self._set_only_once_fields
@cached_property
def _set_only_once_fields(self):
set_only_once_fields = self.get("fields", {"set_only_once": 1})
fieldnames = [d.fieldname for d in set_only_once_fields]
for df in self.standard_set_once_fields:
if df.fieldname not in fieldnames:
set_only_once_fields.append(df)
return set_only_once_fields
def get_table_fields(self):
return self._table_fields
@ -258,34 +263,40 @@ class Meta(Document):
return fields
def get_valid_columns(self) -> list[str]:
if not hasattr(self, "_valid_columns"):
table_exists = frappe.db.table_exists(self.name)
if self.name in self.special_doctypes and table_exists:
self._valid_columns = get_table_columns(self.name)
else:
self._valid_columns = self.default_fields + [
df.fieldname
for df in self.get("fields")
if not df.get("is_virtual") and df.fieldtype in data_fieldtypes
]
if self.istable:
self._valid_columns += list(child_table_fields)
return self._valid_columns
def get_valid_fields(self) -> list[str]:
if not hasattr(self, "_valid_fields"):
if (frappe.flags.in_install or frappe.flags.in_migrate) and self.name in self.special_doctypes:
self._valid_fields = get_table_columns(self.name)
else:
self._valid_fields = self.default_fields + [
df.fieldname for df in self.get("fields") if df.fieldtype in data_fieldtypes
]
if self.istable:
self._valid_fields += list(child_table_fields)
@cached_property
def _valid_columns(self):
table_exists = frappe.db.table_exists(self.name)
if self.name in self.special_doctypes and table_exists:
valid_columns = get_table_columns(self.name)
else:
valid_columns = self.default_fields + [
df.fieldname
for df in self.get("fields")
if not df.get("is_virtual") and df.fieldtype in data_fieldtypes
]
if self.istable:
valid_columns += list(child_table_fields)
return valid_columns
def get_valid_fields(self) -> list[str]:
return self._valid_fields
@cached_property
def _valid_fields(self):
if (frappe.flags.in_install or frappe.flags.in_migrate) and self.name in self.special_doctypes:
valid_fields = get_table_columns(self.name)
else:
valid_fields = self.default_fields + [
df.fieldname for df in self.get("fields") if df.fieldtype in data_fieldtypes
]
if self.istable:
valid_fields += list(child_table_fields)
return valid_fields
def get_table_field_doctype(self, fieldname):
return TABLE_DOCTYPES_FOR_DOCTYPE.get(fieldname)