Почему возникает ошибка ‘TypeError: ‘FirebaseResponseDict’ object is not callable’ при использовании UUID в качестве primary_key для пользователя в Django с FCM-Django? Как правильно настроить отправку push-уведомлений через FCM-Django, когда id пользователя является UUID вместо стандартного integer?
Ошибка ‘TypeError: ‘FirebaseResponseDict’ object is not callable’ в FCM-Django с UUID primary key для пользователя возникает из-за несоответствия типов данных: библиотека ожидает integer ID, но получает UUID, что ломает вызовы методов на ответе от Firebase. Чтобы отправлять push-уведомления корректно, создайте кастомную модель FCMDevice с UUIDField для связи с User и укажите её в настройках через FCM_DJANGO_FCMDEVICE_MODEL. Это решает проблему сериализации и позволяет FCM-Django работать с UUID без ошибок.
Содержание
- Понимание ошибки FirebaseResponseDict
- Конфигурация UUID primary key в Django
- Кастомная модель FCMDevice для UUID
- Настройка отправки push-уведомлений
- Устранение неполадок и лучшие практики
- Источники
- Заключение
Понимание ошибки FirebaseResponseDict
Представьте: вы пытаетесь отправить push через FCM-Django, код вроде result = device.send_message(...), и бац — TypeError: ‘FirebaseResponseDict’ object is not callable. Что это за зверь такой, FirebaseResponseDict?
Это объект из библиотеки fcm-django, который возвращает Firebase после отправки сообщения. По сути, это словарь с результатами (success, failure, message_id), но с дополнительными методами. Проблема не в нём самом, а в том, как Django/FCM-Django обрабатывает связи моделей при UUID pk.
Когда ваш User имеет uuid = models.UUIDField(primary_key=True), FCMDevice (стандартная модель) пытается сохранить user_id как integer. UUID не влезает — Django кидает ошибку при создании/поисках устройств. А когда доходит до отправки, response от FCM не вызывается правильно, потому что registration_id или user_id искажены. В issues библиотеки, например здесь, народ жалуется на похожие беды с нестандартными pk.
Коротко: стандартная FCMDevice ожидает AutoField (int), UUID её пугает. Но фикс простой — своя модель.
Конфигурация UUID primary key в Django
Сначала убедитесь, что UUID в User настроен верно. В models.py вашего app:
import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# другие поля...
Запустите миграции: python manage.py makemigrations && python manage.py migrate. Теперь все пользователи имеют UUID pk. Но FCM-Django сломается на FCMDevice.objects.get(user=request.user) — типы не совпадут.
Почему UUID лучше integer? Масштабируемость, распределённые системы, нет последовательных ID для атаки. Об этом подробно в гайде. Только учтите: generic relations (ContentType) с UUID требуют осторожности, как описано на StackOverflow.
А теперь к FCM: без кастомизации уведомления не полетят.
Кастомная модель FCMDevice для UUID
Вот сердце решения. Создайте в models.py:
from fcm_django.models import AbstractFCMDevice
from django.db import models
import uuid
class MyFCMDevice(AbstractFCMDevice):
user = models.ForeignKey(
'auth.User', # или ваш кастомный User
models.CASCADE,
null=True,
blank=True,
related_name='fcm_devices'
)
# UUID уже наследуется, но убедитесь в типах
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# Остальные поля как в AbstractFCMDevice
Ключ: наследуйтесь от AbstractFCMDevice, переопределите user на ForeignKey с правильным типом (UUID под капотом сработает). В документации fcm-django прямо советуют это для кастомных моделей.
В settings.py добавьте:
FCM_DJANGO_SETTINGS = {
"FCM_DEVICE_MODEL": "myapp.MyFCMDevice", # путь к вашей модели
}
Или короче: FCM_DJANGO_FCMDEVICE_MODEL = 'myapp.MyFCMDevice'.
Миграции снова: makemigrations fcm_django myapp. Теперь Django знает вашу модель вместо стандартной.
Регистрация устройства в views.py станет:
from fcm_django.models import FCMDevice # Импорт сработает на вашу модель
device = FCMDevice(
registration_id=token,
type='android', # или ios
user=request.user # UUID user!
)
device.save()
Тестируйте: создайте устройство для UUID-юзера. Без ошибок!
Настройка отправки push-уведомлений
Отправка простая, но с UUID всё равно требует аккуратности. Пример в view:
from fcm_django.models import FCMDevice
def send_notification(request):
user = request.user # UUID pk
devices = FCMDevice.objects.filter(user=user, active=True)
sent = devices.send_message(
title="Привет!",
body="Тест с UUID",
icon="ic_launcher"
)
# sent — это FirebaseResponseDict
if not sent['success']:
print("Ошибки:", sent['failure'])
return JsonResponse({'sent': sent['success']})
Важно: send_message возвращает FirebaseResponseDict. Не вызывайте его как функцию — sent() сломает! Просто читайте ключи: sent.success, sent.failure.
Для массовой рассылки: FCMDevice.objects.filter(user__in=users_qs).send_message(...). UUID в queryset работает на ура.
Ещё фишка: в README fcm-django есть опции для data payload, sound, badge. Добавляйте:
devices.send_message(
message,
data={"custom_key": "value"},
sound="default"
)
Серверные ключи Firebase в settings: FCM_DJANGO_SERVER_KEY = 'your_server_key'.
Устранение неполадок и лучшие практики
Ошибки остаются? Проверьте:
- Типы pk:
print(type(user.id))— должен быть UUID. - Миграции: Удалили старую FCMDevice?
python manage.py migrate --fake-initial. - Registration token: Валидный? Firebase его ревоцирует часто.
- Логи: Вкрутите
LOGGINGдля fcm_django.DEBUG.
Частая засада — старые устройства в БД с int user_id. Почистите: FCMDevice.objects.filter(user__isnull=True).delete().
Лучшие практики:
- Используйте Celery для асинхронной отправки: большие рассылки не блочат views.
- Валидация токенов:
device.is_valid_token(). - Тестируйте на эмуляторе: Android/iOS.
- Мониторинг: логируйте
sent['message_id']для отладки.
В статье о UUID pk подчёркивают: индексы на UUIDField обязательны для скорости.
А если production — DRF integration: сериалайзеры для FCMDevice с read-only token.
Источники
- fcm-django GitHub — Основная документация по кастомным моделям FCMDevice и настройкам: https://github.com/xtrinch/fcm-django
- fcm-django README — Инструкции по FCM_DJANGO_FCMDEVICE_MODEL и конфигурации: https://github.com/xtrinch/fcm-django/blob/master/README.rst
- fcm_django/models.py — Исходный код FirebaseResponseDict и AbstractFCMDevice: https://github.com/xtrinch/fcm-django/blob/master/fcm_django/models.py
- fcm-django Issue 213 — Обсуждение реальных проблем с response объектами: https://github.com/xtrinch/fcm-django/issues/213
- UUID as Primary Key in Django — Влияние UUID на generic relations и модели: https://stackoverflow.com/questions/3936182/using-a-uuid-as-a-primary-key-in-django-models-generic-relations-impact
- Django ORM Cookbook UUID — Базовая настройка UUIDField как primary key: https://books.agiliq.com/projects/django-orm-cookbook/en/latest/uuid.html
Заключение
TypeError с FirebaseResponseDict в FCM-Django при UUID primary key решается кастомной моделью FCMDevice и настройкой FCM_DJANGO_FCMDEVICE_MODEL — это стандартный подход из документации. После фикса push-уведомления летают стабильно, даже для тысяч пользователей. Главное — миграции, типы данных и не вызывать response как функцию. Попробуйте на dev-сервере, и уведомления заработают как часы.