def update_traits(
        self,
        trait_data_items: list[SDKTraitData],
    ) -> list[Trait]:
        """
        Given a list of traits, update any that already exist and create any new ones.
        Return the full list of traits for the given identity after these changes.

        :param trait_data_items: list of dictionaries validated by TraitSerializerFull
        :return: queryset of updated trait models
        """
        current_traits = {t.trait_key: t for t in self.identity_traits.all()}

        keys_to_delete = set()
        new_traits = []
        updated_traits = []
        transient_traits = []

        for trait_data_item in trait_data_items:
            trait_key = trait_data_item["trait_key"]
            trait_value = trait_data_item["trait_value"]
            transient = trait_data_item.get("transient")

            if transient:
                trait = Trait(
                    **Trait.generate_trait_value_data(trait_value),
                    trait_key=trait_key,
                    identity=self,
                )
                trait.transient = True
                transient_traits.append(trait)
                continue

            if trait_value is None:
                # build a list of trait keys to delete having been nulled by the
                # input data
                keys_to_delete.add(trait_key)
                continue

            if trait_key in current_traits:
                current_trait = current_traits[trait_key]
                # Don't update the trait if the value hasn't changed
                if current_trait.trait_value == trait_value:
                    continue

                for attr, value in Trait.generate_trait_value_data(trait_value).items():
                    setattr(current_trait, attr, value)
                updated_traits.append(current_trait)
                continue

            new_traits.append(
                Trait(
                    **Trait.generate_trait_value_data(trait_value),
                    trait_key=trait_key,
                    identity=self,
                )
            )

        # delete the traits that had their keys set to None
        # (except the transient ones)
        if keys_to_delete:
            current_traits = {
                trait_key: trait
                for trait_key, trait in current_traits.items()
                if trait_key not in keys_to_delete
            }
            self.identity_traits.filter(trait_key__in=keys_to_delete).delete()

        Trait.objects.bulk_update(updated_traits, fields=Trait.BULK_UPDATE_FIELDS)

        # use ignore_conflicts to handle race conditions which result in IntegrityError if another request
        # has added a particular trait_key for the identity while this method has been determining what to
        # update or create.
        # See: https://github.com/Flagsmith/flagsmith/issues/370
        Trait.objects.bulk_create(new_traits, ignore_conflicts=True)

        # return the full list of traits for this identity
        # override persisted traits by transient traits in case of key collisions
        return [
            *{
                trait.trait_key: trait
                for trait in chain(
                    current_traits.values(),
                    updated_traits,
                    new_traits,
                    transient_traits,
                )
            }.values()
        ]

        Given a list of traits, update any that already exist and create any new ones.
        Return the full list of traits for the given identity after these changes.

        :param trait_data_items: list of dictionaries validated by TraitSerializerFull
        :return: queryset of updated trait models
        
api/environments/identities/models.py