11import typing
2+ from datetime import datetime
23
34from marshmallow import fields , post_dump , pre_dump
45
5- from flag_engine .django_transform .fields import DjangoRelatedManagerField
6+ from flag_engine .django_transform .fields import (
7+ DjangoFeatureStatesRelatedManagerField ,
8+ DjangoRelatedManagerField ,
9+ )
610from flag_engine .django_transform .filters import sort_and_filter_feature_segments
711from flag_engine .environments .schemas import (
812 BaseEnvironmentAPIKeySchema ,
@@ -59,26 +63,34 @@ def __init__(self, *args, **kwargs):
5963 self .feature_state_schema = DjangoFeatureStateSchema ()
6064
6165 def serialize_feature_states (self , instance : typing .Any ) -> typing .List [dict ]:
66+ # TODO: move this logic to Django so we can optimise queries
6267 # api key is set in the context using a pre_dump method on EnvironmentSchema.
6368 environment_api_key = self .context .get ("environment_api_key" )
6469 feature_segments = sort_and_filter_feature_segments (
6570 instance .feature_segments .all (), environment_api_key
6671 )
6772
68- # Django datamodel incorrectly uses a foreign key for the
69- # FeatureState -> FeatureSegment relationship so we have to recursively
70- # build the list like this
71- feature_states = []
73+ # iterate over the feature segments and related feature states to end up with
74+ # a list consisting of the latest version feature state for each feature
75+ feature_states = {}
76+ now = datetime . now ()
7277 for feature_segment in feature_segments :
73- feature_states .extend (feature_segment .feature_states .all ())
74- return self .feature_state_schema .dump (feature_states , many = True )
78+ for feature_state in feature_segment .feature_states .all ():
79+ existing_feature_state = feature_states .get (feature_state .feature_id )
80+ if not existing_feature_state or (
81+ feature_state .version > existing_feature_state .version
82+ and feature_state .live_from < now
83+ ):
84+ feature_states [feature_state .feature_id ] = feature_state
85+
86+ return self .feature_state_schema .dump (list (feature_states .values ()), many = True )
7587
7688
7789class DjangoIdentitySchema (BaseIdentitySchema ):
7890 identity_traits = DjangoRelatedManagerField (
7991 fields .Nested (TraitSchema ), required = False
8092 )
81- identity_features = DjangoRelatedManagerField (
93+ identity_features = DjangoFeatureStatesRelatedManagerField (
8294 fields .Nested (DjangoFeatureStateSchema ), required = False
8395 )
8496 django_id = fields .Int (attribute = "id" )
@@ -105,7 +117,7 @@ class DjangoProjectSchema(BaseProjectSchema):
105117
106118
107119class DjangoEnvironmentSchema (BaseEnvironmentSchema ):
108- feature_states = DjangoRelatedManagerField (
120+ feature_states = DjangoFeatureStatesRelatedManagerField (
109121 fields .Nested (DjangoFeatureStateSchema ),
110122 filter_func = lambda e : e .feature_segment_id is None and e .identity_id is None ,
111123 dump_only = True ,
0 commit comments