Объединение (из примера кода вопроса) не очень хорошая идея, его поиск в БД еще больше:
for group in groups:
username = users.models.User.objects.filter(groups__name=group)
for user in username: # <-- Hitting DB
form.instance.unread.add(user)
user_obj = users.models.User.objects.get(username=user) # <-- DB hit again
user_obj.send_sms('memo')
user_obj.send_email('memo')
Итак, минимальная оптимизация, которую вы можете сделать, это сначала оценить набор запросов,затем выполните итерации по циклу:
for group in groups:
username = list(users.models.User.objects.filter(groups__name=group))
for user in username:
form.instance.unread.add(user)
user.send_sms('memo')
user.send_email('memo')
Более подробную информацию можно найти в документации относительно того, как оцениваются наборы запросов.
И ИМХО, наиболее оптимальное решение выглядит следующим образом (Я избавился от цикла for):
for group in groups:
users = users.models.User.objects.filter(groups__name=group)
form.instance.unread.add(*users) # unpacking the queryset and passing it through add (m2m) method. Reference: https://stackoverflow.com/a/4959580/2696165
send_sms('memo', users.values_list('phone_number')) # you need to convert this method so that it is not attached to user object and can accept a list of phone numbers
send_email('memo', users.values_list('email')) # you need to convert this method so that it is not attached to user object and can accept a list of emails