(
        cls,
        data,
        field_name="id",
        refetch=True,
        current_ids=None,
        pending_relations=None,
        save=True,
        stripe_account=None,
    )
:param data:
        :param field_name:
        :param refetch:
        :param current_ids: stripe ids of objects that are currently being processed
        :type current_ids: set
        :param pending_relations: list of tuples of relations to be attached post-save
        :type pending_relations: list
        :param save:
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        :return:
        :rtype: cls, bool
def _get_or_create_from_stripe_object(
        cls,
        data,
        field_name="id",
        refetch=True,
        current_ids=None,
        pending_relations=None,
        save=True,
        stripe_account=None,
    ):
        """

        :param data:
        :param field_name:
        :param refetch:
        :param current_ids: stripe ids of objects that are currently being processed
        :type current_ids: set
        :param pending_relations: list of tuples of relations to be attached post-save
        :type pending_relations: list
        :param save:
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        :return:
        :rtype: cls, bool
        """
        field = data.get(field_name)
        is_nested_data = field_name != "id"
        should_expand = False

        if pending_relations is None:
            pending_relations = []

        id_ = get_id_from_stripe_data(field)

        if not field:
            # An empty field - We need to return nothing here because there is
            # no way of knowing what needs to be fetched!
            logger.warning(
                "empty field %s.%s = %r - this is a bug, "
                "please report it to dj-stripe!",
                cls.__name__,
                field_name,
                field,
            )
            return None, False
        elif id_ == field:
            # A field like {"subscription": "sub_6lsC8pt7IcFpjA", ...}
            # We'll have to expand if the field is not "id" (= is nested)
            should_expand = is_nested_data
        else:
            # A field like {"subscription": {"id": sub_6lsC8pt7IcFpjA", ...}}
            data = field

        try:
            return cls.stripe_objects.get(id=id_), False
        except cls.DoesNotExist:
            if is_nested_data and refetch:
                # This is what `data` usually looks like:
                # {"id": "cus_XXXX", "default_source": "card_XXXX"}
                # Leaving the default field_name ("id") will get_or_create the customer.
                # If field_name="default_source", we get_or_create the card instead.
                cls_instance = cls(id=id_)
                try:
                    data = cls_instance.api_retrieve(stripe_account=stripe_account)
                except InvalidRequestError as e:
                    if "a similar object exists in" in str(e):
                        # HACK around a Stripe bug.
                        # When a File is retrieved from the Account object,
                        # a mismatch between live and test mode is possible depending
                        # on whether the file (usually the logo) was uploaded in live
                        # or test. Reported to Stripe in August 2020.
                        # Context: https://github.com/dj-stripe/dj-stripe/issues/830
                        pass
                    elif "No such PaymentMethod:" in str(e):
                        # payment methods (card_… etc) can be irretrievably deleted,
                        # but still present during sync. For example, if a refund is
                        # issued on a charge whose payment method has been deleted.
                        return None, False
                    else:
                        raise
                should_expand = False

        # The next thing to happen will be the "create from stripe object" call.
        # At this point, if we don't have data to start with (field is a str),
        # *and* we didn't refetch by id, then `should_expand` is True and we
        # don't have the data to actually create the object.
        # If this happens when syncing Stripe data, it's a djstripe bug. Report it!
        assert not should_expand, "No data to create {} from {}".format(
            cls.__name__, field_name
        )

        try:
            # We wrap the `_create_from_stripe_object` in a transaction to
            # avoid TransactionManagementError on subsequent queries in case
            # of the IntegrityError catch below. See PR #903
            with transaction.atomic():
                return (
                    cls._create_from_stripe_object(
                        data,
                        current_ids=current_ids,
                        pending_relations=pending_relations,
                        save=save,
                        stripe_account=stripe_account,
                    ),
                    True,
                )
        except IntegrityError:
            # Handle the race condition that something else created the object
            # after the `get` and before `_create_from_stripe_object`.
            # This is common during webhook handling, since Stripe sends
            # multiple webhook events simultaneously,
            # each of which will cause recursive syncs. See issue #429
            return cls.stripe_objects.get(id=id_), False
def _get_or_create_from_stripe_object ( cls , data , field_name = "id" , refetch = True , current_ids = None , pending_relations = None , save = True , stripe_account = None , ) : field = data . get ( field_name ) is_nested_data = field_name != "id" should_expand = False if pending_relations is None : pending_relations = [ ] id_ = get_id_from_stripe_data ( field ) if not field : # An empty field - We need to return nothing here because there is # no way of knowing what needs to be fetched! logger . warning ( "empty field %s.%s = %r - this is a bug, " "please report it to dj-stripe!" , cls . __name__ , field_name , field , ) return None , False elif id_ == field : # A field like {"subscription": "sub_6lsC8pt7IcFpjA", ...} # We'll have to expand if the field is not "id" (= is nested) should_expand = is_nested_data else : # A field like {"subscription": {"id": sub_6lsC8pt7IcFpjA", ...}} data = field try : return cls . stripe_objects . get ( id = id_ ) , False except cls . DoesNotExist : if is_nested_data and refetch : # This is what `data` usually looks like: # {"id": "cus_XXXX", "default_source": "card_XXXX"} # Leaving the default field_name ("id") will get_or_create the customer. # If field_name="default_source", we get_or_create the card instead. cls_instance = cls ( id = id_ ) try : data = cls_instance . api_retrieve ( stripe_account = stripe_account ) except InvalidRequestError as e : if "a similar object exists in" in str ( e ) : # HACK around a Stripe bug. # When a File is retrieved from the Account object, # a mismatch between live and test mode is possible depending # on whether the file (usually the logo) was uploaded in live # or test. Reported to Stripe in August 2020. # Context: https://github.com/dj-stripe/dj-stripe/issues/830 pass elif "No such PaymentMethod:" in str ( e ) : # payment methods (card_… etc) can be irretrievably deleted, # but still present during sync. For example, if a refund is # issued on a charge whose payment method has been deleted. return None , False else : raise should_expand = False # The next thing to happen will be the "create from stripe object" call. # At this point, if we don't have data to start with (field is a str), # *and* we didn't refetch by id, then `should_expand` is True and we # don't have the data to actually create the object. # If this happens when syncing Stripe data, it's a djstripe bug. Report it! assert not should_expand , "No data to create {} from {}" . format ( cls . __name__ , field_name ) try : # We wrap the `_create_from_stripe_object` in a transaction to # avoid TransactionManagementError on subsequent queries in case # of the IntegrityError catch below. See PR #903 with transaction . atomic ( ) : return ( cls . _create_from_stripe_object ( data , current_ids = current_ids , pending_relations = pending_relations , save = save , stripe_account = stripe_account , ) , True , ) except IntegrityError : # Handle the race condition that something else created the object # after the `get` and before `_create_from_stripe_object`. # This is common during webhook handling, since Stripe sends # multiple webhook events simultaneously, # each of which will cause recursive syncs. See issue #429 return cls . stripe_objects . get ( id = id_ ) , False