Программирование

Ошибка TypeError FirebaseResponseDict FCM Django UUID primary key

Решение ошибки TypeError с FirebaseResponseDict при использовании UUID в Django с FCM-Django. Настройка push-уведомлений с UUID primary key.

1 ответ 1 просмотр

Почему возникает ошибка ‘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

Представьте: вы пытаетесь отправить 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:

python
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:

python
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 добавьте:

python
FCM_DJANGO_SETTINGS = {
 "FCM_DEVICE_MODEL": "myapp.MyFCMDevice", # путь к вашей модели
}

Или короче: FCM_DJANGO_FCMDEVICE_MODEL = 'myapp.MyFCMDevice'.

Миграции снова: makemigrations fcm_django myapp. Теперь Django знает вашу модель вместо стандартной.

Регистрация устройства в views.py станет:

python
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:

python
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. Добавляйте:

python
devices.send_message(
 message,
 data={"custom_key": "value"},
 sound="default"
)

Серверные ключи Firebase в settings: FCM_DJANGO_SERVER_KEY = 'your_server_key'.


Устранение неполадок и лучшие практики

Ошибки остаются? Проверьте:

  1. Типы pk: print(type(user.id)) — должен быть UUID.
  2. Миграции: Удалили старую FCMDevice? python manage.py migrate --fake-initial.
  3. Registration token: Валидный? Firebase его ревоцирует часто.
  4. Логи: Вкрутите 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.


Источники

  1. fcm-django GitHub — Основная документация по кастомным моделям FCMDevice и настройкам: https://github.com/xtrinch/fcm-django
  2. fcm-django README — Инструкции по FCM_DJANGO_FCMDEVICE_MODEL и конфигурации: https://github.com/xtrinch/fcm-django/blob/master/README.rst
  3. fcm_django/models.py — Исходный код FirebaseResponseDict и AbstractFCMDevice: https://github.com/xtrinch/fcm-django/blob/master/fcm_django/models.py
  4. fcm-django Issue 213 — Обсуждение реальных проблем с response объектами: https://github.com/xtrinch/fcm-django/issues/213
  5. 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
  6. 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-сервере, и уведомления заработают как часы.

Авторы
Проверено модерацией
Модерация