Skip to content

Commit 707496d

Browse files
Zaimwa9pre-commit-ci[bot]khvn26
authored
fix: metadata-incorrectly-linked-to-entity (#57)
* fix: reviewed-metadata-update-or-create-parameter-and-moved-in-shared-serializer * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: replaced-ids-with-pks * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: removed-redundant-checks * feat: bumped-version * fix: reviewed-validation-to-delete-metadata-without-value * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: reverted-to-delete-before-update-behavior * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update src/common/metadata/serializers.py Co-authored-by: Kim Gustyr <[email protected]> * fix: typed-dict-metadata-item * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: fix-incorrect-comparison --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Kim Gustyr <[email protected]>
1 parent ecce903 commit 707496d

File tree

4 files changed

+54
-31
lines changed

4 files changed

+54
-31
lines changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "flagsmith-common"
3-
version = "1.12.0"
3+
version = "1.12.1"
44
description = "Flagsmith's common library"
55
requires-python = ">=3.11,<4.0"
66
dependencies = [

src/common/metadata/serializers.py

+39
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from django.db import models
66
from rest_framework import serializers
77

8+
from common.metadata.types import MetadataItem
9+
810
if typing.TYPE_CHECKING:
911
from common.types import (
1012
Metadata, # noqa: F401
@@ -56,6 +58,43 @@ def get_required_for_object(
5658
)
5759
return instance
5860

61+
def update_metadata(
62+
self,
63+
instance: models.Model,
64+
metadata_data: list[MetadataItem],
65+
) -> None:
66+
Metadata = apps.get_model("metadata", "Metadata")
67+
ContentType = apps.get_model("contenttypes", "ContentType")
68+
69+
content_type = ContentType.objects.get_for_model(instance.__class__)
70+
71+
if not metadata_data:
72+
Metadata.objects.filter(
73+
object_id=instance.pk, content_type=content_type
74+
).delete()
75+
return
76+
77+
incoming_updated_fields = {
78+
item["model_field"].id for item in metadata_data if not item.get("delete")
79+
}
80+
81+
Metadata.objects.filter(
82+
object_id=instance.pk,
83+
content_type=content_type,
84+
).exclude(model_field__id__in=incoming_updated_fields).delete()
85+
86+
for metadata_item in metadata_data:
87+
metadata_model_field = metadata_item.pop("model_field", None)
88+
if metadata_model_field is not None and metadata_model_field.id is not None:
89+
Metadata.objects.update_or_create(
90+
model_field=metadata_model_field,
91+
content_type=content_type,
92+
object_id=instance.pk,
93+
defaults={
94+
**metadata_item,
95+
},
96+
)
97+
5998
def validate_required_metadata(
6099
self,
61100
data: dict[str, typing.Any],

src/common/metadata/types.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from typing import Any, NotRequired, Protocol, TypedDict
2+
3+
4+
class HasId(Protocol):
5+
id: int
6+
7+
8+
class MetadataItem(TypedDict, total=False):
9+
model_field: HasId
10+
field_value: Any
11+
delete: NotRequired[bool]

src/common/segments/serializers.py

+3-30
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
from django.apps import apps
55
from django.conf import settings
6-
from django.contrib.contenttypes.models import ContentType
76
from django.db import models
87
from flag_engine.segments.constants import PERCENTAGE_SPLIT
98
from rest_framework import serializers
@@ -118,7 +117,8 @@ def create(self, validated_data: dict[str, Any]) -> "Segment":
118117
self._update_or_create_segment_rules(
119118
rules_data, segment=segment, is_create=True
120119
)
121-
self._update_or_create_metadata(metadata_data, segment=segment)
120+
self.update_metadata(segment, metadata_data)
121+
segment.refresh_from_db()
122122
return segment
123123

124124
def update(
@@ -139,7 +139,7 @@ def update(
139139

140140
try:
141141
self._update_segment_rules(rules_data, segment=instance)
142-
self._update_or_create_metadata(metadata_data, segment=instance)
142+
self.update_metadata(instance, metadata_data)
143143

144144
# remove rules from validated data to prevent error trying to create segment with nested rules
145145
del validated_data["rules"]
@@ -250,33 +250,6 @@ def _update_or_create_segment_rules(
250250
child_rules, rule=child_rule, is_create=is_create
251251
)
252252

253-
def _update_or_create_metadata(
254-
self,
255-
metadata_data: list[dict[str, Any]],
256-
segment: "Segment | None" = None,
257-
) -> None:
258-
Metadata = apps.get_model("metadata", "Metadata")
259-
Segment = apps.get_model("segments", "Segment")
260-
assert segment
261-
if len(metadata_data) == 0:
262-
Metadata.objects.filter(object_id=segment.id).delete()
263-
return
264-
if metadata_data is not None:
265-
for metadata_item in metadata_data:
266-
metadata_model_field = metadata_item.pop("model_field", None)
267-
if metadata_item.get("delete"):
268-
Metadata.objects.filter(model_field=metadata_model_field).delete()
269-
continue
270-
271-
Metadata.objects.update_or_create(
272-
model_field=metadata_model_field,
273-
defaults={
274-
**metadata_item,
275-
"content_type": ContentType.objects.get_for_model(Segment),
276-
"object_id": segment.id,
277-
},
278-
)
279-
280253
@staticmethod
281254
def _update_or_create_segment_rule(
282255
rule_data: dict[str, Any],

0 commit comments

Comments
 (0)