Django: Database efficiency - currently accessed twice
Your step 2 can be done entirely in DB by filtering and limiting your order_items. And then updating it to contain the desired value. Note that the F objects documentation already mention that use of a queryset update method. So we could imagine something like:

order.order_items.filter(social_discount__isnull=False)[:1].update(social_discount__redeemed_amount=F('social_discount__redeemed_amount') + 1)


Except this doesn't work:


we cannot use update after slicing a queryset;
we cannot update fields of related models anyway.


Instead we must use the relationship backwards and filter/update on the SocialDiscount model, the filter being the subset of OrderItems we are interested in:

order_item = order.order_items.filter(social_discount__isnull=False)[:1]
# Note that order_item is still a queryset and has not hit the database yet
SocialDiscount.objects.filter(order_items__in=order_item).update(redeemed_amount=F('redeemed_amount') + 1)


Even though this only hit the database once, this will update all SocialDiscount objects that are related to the one order_item specified by the queryset above. If it ever can mean that more than one object is updated and you don't want that, you can still remove the for loop using this two-queries code:

social_discount = order.order_items.filter(social_discount__isnull=False).select_related('social_discount').first().social_discount
social_discount.redeemed_amount = F('redeemed_amount') + 1
social_discount.save()