Send push notifications to mobile devices through GCM or APNS in Django.

Overview

django-push-notifications

https://travis-ci.org/jazzband/django-push-notifications.svg?branch=master Jazzband

A minimal Django app that implements Device models that can send messages through APNS, FCM/GCM and WNS.

The app implements three models: GCMDevice, APNSDevice and WNSDevice. Those models share the same attributes:
  • name (optional): A name for the device.
  • active (default True): A boolean that determines whether the device will be sent notifications.
  • user (optional): A foreign key to auth.User, if you wish to link the device to a specific user.
  • device_id (optional): A UUID for the device obtained from Android/iOS/Windows APIs, if you wish to uniquely identify it.
  • registration_id (required): The FCM/GCM registration id or the APNS token for the device.

The app also implements an admin panel, through which you can test single and bulk notifications. Select one or more FCM/GCM, APNS or WNS devices and in the action dropdown, select "Send test message" or "Send test message in bulk", accordingly. Note that sending a non-bulk test message to more than one device will just iterate over the devices and send multiple single messages. UPDATE_ON_DUPLICATE_REG_ID: Transform create of an existing Device (based on registration id) into a update. See below Update of device with duplicate registration ID for more details.

Dependencies

  • Python 3.5+
  • Django 1.11+
  • For the API module, Django REST Framework 3.7+ is required.
  • For WebPush (WP), pywebpush 1.3.0+ is required. py-vapid 1.3.0+ is required for generating the WebPush private key; however this step does not need to occur on the application server.

Setup

You can install the library directly from pypi using pip:

$ pip install django-push-notifications

Edit your settings.py file:

INSTALLED_APPS = (
        ...
        "push_notifications"
)

PUSH_NOTIFICATIONS_SETTINGS = {
        "FCM_API_KEY": "[your api key]",
        "GCM_API_KEY": "[your api key]",
        "APNS_CERTIFICATE": "/path/to/your/certificate.pem",
        "APNS_TOPIC": "com.example.push_test",
        "WNS_PACKAGE_SECURITY_ID": "[your package security id, e.g: 'ms-app://e-3-4-6234...']",
        "WNS_SECRET_KEY": "[your app secret key, e.g.: 'KDiejnLKDUWodsjmewuSZkk']",
        "WP_PRIVATE_KEY": "/path/to/your/private.pem",
        "WP_CLAIMS": {'sub': "mailto: [email protected]"}
}

Note

If you need to support multiple mobile applications from a single Django application, see Multiple Application Support for details.

Note

If you are planning on running your project with APNS_USE_SANDBOX=True, then make sure you have set the development certificate as your APNS_CERTIFICATE. Otherwise the app will not be able to connect to the correct host. See settings for details.

For more information about how to generate certificates, see docs/APNS.

You can learn more about APNS certificates here.

Native Django migrations are in use. manage.py migrate will install and migrate all models.

Settings list

All settings are contained in a PUSH_NOTIFICATIONS_SETTINGS dict.

In order to use FCM/GCM, you are required to include FCM_API_KEY or GCM_API_KEY. For APNS, you are required to include APNS_CERTIFICATE. For WNS, you need both the WNS_PACKAGE_SECURITY_KEY and the WNS_SECRET_KEY.

General settings

  • USER_MODEL: Your user model of choice. Eg. myapp.User. Defaults to settings.AUTH_USER_MODEL.
  • UPDATE_ON_DUPLICATE_REG_ID: Transform create of an existing Device (based on registration id) into a update. See below Update of device with duplicate registration ID for more details.
  • UNIQUE_REG_ID: Forces the registration_id field on all device models to be unique.

APNS settings

  • APNS_CERTIFICATE: Absolute path to your APNS certificate file. Certificates with passphrases are not supported. If iOS application was build with "Release" flag, you need to use production certificate, otherwise debug. Read more about Generation of an APNS PEM file.
  • APNS_AUTH_KEY_PATH: Absolute path to your APNS signing key file for Token-Based Authentication . Use this instead of APNS_CERTIFICATE if you are using .p8 signing key certificate.
  • APNS_AUTH_KEY_ID: The 10-character Key ID you obtained from your Apple developer account
  • APNS_TEAM_ID: 10-character Team ID you use for developing your company’s apps for iOS.
  • APNS_TOPIC: The topic of the remote notification, which is typically the bundle ID for your app. If you omit this header and your APNs certificate does not specify multiple topics, the APNs server uses the certificate’s Subject as the default topic.
  • APNS_USE_ALTERNATIVE_PORT: Use port 2197 for APNS, instead of default port 443.
  • APNS_USE_SANDBOX: Use 'api.development.push.apple.com', instead of default host 'api.push.apple.com'. Default value depends on DEBUG setting of your environment: if DEBUG is True and you use production certificate, you should explicitly set APNS_USE_SANDBOX to False.

FCM/GCM settings

  • FCM_API_KEY: Your API key for Firebase Cloud Messaging.
  • FCM_POST_URL: The full url that FCM notifications will be POSTed to. Defaults to https://fcm.googleapis.com/fcm/send.
  • FCM_MAX_RECIPIENTS: The maximum amount of recipients that can be contained per bulk message. If the registration_ids list is larger than that number, multiple bulk messages will be sent. Defaults to 1000 (the maximum amount supported by FCM).
  • FCM_ERROR_TIMEOUT: The timeout on FCM POSTs.
  • GCM_API_KEY, GCM_POST_URL, GCM_MAX_RECIPIENTS, GCM_ERROR_TIMEOUT: Same parameters for GCM

WNS settings

  • WNS_PACKAGE_SECURITY_KEY: TODO
  • WNS_SECRET_KEY: TODO

WP settings

  • Install:
pip install pywebpush
pip install py-vapid  (Only for generating key)
  • Getting keys:

    • Create file (claim.json) like this:
{
        "sub": "mailto: [email protected]",
        "aud": "https://android.googleapis.com"
}

- Generate public and private keys:
vapid --sign claim.json

No private_key.pem file found.
Do you want me to create one for you? (Y/n)Y
Do you want me to create one for you? (Y/n)Y
Generating private_key.pem
Generating public_key.pem
Include the following headers in your request:

Crypto-Key: p256ecdsa=BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70

Authorization: WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20iLCJleHAiOiIxNTA4NDkwODM2Iiwic3ViIjoibWFpbHRvOiBkZXZlbG9wbWVudEBleGFtcGxlLmNvbSJ9.r5CYMs86X3JZ4AEs76pXY5PxsnEhIFJ-0ckbibmFHZuyzfIpf1ZGIJbSI7knA4ufu7Hm8RFfEg5wWN1Yf-dR2A

- Generate client public key (applicationServerKey)
vapid --applicationServerKey

Application Server Key = BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70
  • Configure settings:
  • WP_PRIVATE_KEY: Absolute path to your private certificate file: os.path.join(BASE_DIR, "private_key.pem")
  • WP_CLAIMS: Dictionary with the same sub info like claims file: {'sub': "mailto: [email protected]"}
  • WP_ERROR_TIMEOUT: The timeout on WebPush POSTs. (Optional)
  • WP_POST_URL: A dictionary (key per browser supported) with the full url that webpush notifications will be POSTed to. (Optional)
  • Configure client (javascript):
// Utils functions:

function urlBase64ToUint8Array (base64String) {
        var padding = '='.repeat((4 - base64String.length % 4) % 4)
        var base64 = (base64String + padding)
                .replace(/\-/g, '+')
                .replace(/_/g, '/')

        var rawData = window.atob(base64)
        var outputArray = new Uint8Array(rawData.length)

        for (var i = 0; i < rawData.length; ++i) {
                outputArray[i] = rawData.charCodeAt(i)
        }
        return outputArray;
}
function loadVersionBrowser (userAgent) {
        var ua = userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
        if (/trident/i.test(M[1])) {
                tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
                return {name: 'IE', version: (tem[1] || '')};
        }
        if (M[1] === 'Chrome') {
                tem = ua.match(/\bOPR\/(\d+)/);
                if (tem != null) {
                        return {name: 'Opera', version: tem[1]};
                }
        }
        M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
        if ((tem = ua.match(/version\/(\d+)/i)) != null) {
                M.splice(1, 1, tem[1]);
        }
        return {
                name: M[0],
                version: M[1]
        };
};
var applicationServerKey = "BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70";
....

// In your ready listener
if ('serviceWorker' in navigator) {
        // The service worker has to store in the root of the app
        // http://stackoverflow.com/questions/29874068/navigator-serviceworker-is-never-ready
        var browser = loadVersionBrowser();
        navigator.serviceWorker.register('navigatorPush.service.js?version=1.0.0').then(function (reg) {
                reg.pushManager.subscribe({
                        userVisibleOnly: true,
                        applicationServerKey: urlBase64ToUint8Array(applicationServerKey)
                }).then(function (sub) {
                        var endpointParts = sub.endpoint.split('/');
                        var registration_id = endpointParts[endpointParts.length - 1];
                        var data = {
                                'browser': browser.name.toUpperCase(),
                                'p256dh': btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('p256dh')))),
                                'auth': btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('auth')))),
                                'name': 'XXXXX',
                                'registration_id': registration_id
                        };
                        requestPOSTToServer(data);
                })
        }).catch(function (err) {
                console.log(':^(', err);
        });




// Example navigatorPush.service.js file

var getTitle = function (title) {
        if (title === "") {
                title = "TITLE DEFAULT";
        }
        return title;
};
var getNotificationOptions = function (message, message_tag) {
        var options = {
                body: message,
                icon: '/img/icon_120.png',
                tag: message_tag,
                vibrate: [200, 100, 200, 100, 200, 100, 200]
        };
        return options;
};

self.addEventListener('install', function (event) {
        self.skipWaiting();
});

self.addEventListener('push', function(event) {
        try {
                // Push is a JSON
                var response_json = event.data.json();
                var title = response_json.title;
                var message = response_json.message;
                var message_tag = response_json.tag;
        } catch (err) {
                // Push is a simple text
                var title = "";
                var message = event.data.text();
                var message_tag = "";
        }
        self.registration.showNotification(getTitle(title), getNotificationOptions(message, message_tag));
        // Optional: Comunicating with our js application. Send a signal
        self.clients.matchAll({includeUncontrolled: true, type: 'window'}).then(function (clients) {
                clients.forEach(function (client) {
                        client.postMessage({
                                "data": message_tag,
                                "data_title": title,
                                "data_body": message});
                        });
        });
});

// Optional: Added to that the browser opens when you click on the notification push web.
self.addEventListener('notificationclick', function(event) {
        // Android doesn't close the notification when you click it
        // See http://crbug.com/463146
        event.notification.close();
        // Check if there's already a tab open with this URL.
        // If yes: focus on the tab.
        // If no: open a tab with the URL.
        event.waitUntil(clients.matchAll({type: 'window', includeUncontrolled: true}).then(function(windowClients) {
                        for (var i = 0; i < windowClients.length; i++) {
                                var client = windowClients[i];
                                if ('focus' in client) {
                                        return client.focus();
                                }
                        }
                })
        );
});

Sending messages

FCM/GCM and APNS services have slightly different semantics. The app tries to offer a common interface for both when using the models.

from push_notifications.models import APNSDevice, GCMDevice

device = GCMDevice.objects.get(registration_id=gcm_reg_id)
# The first argument will be sent as "message" to the intent extras Bundle
# Retrieve it with intent.getExtras().getString("message")
device.send_message("You've got mail")
# If you want to customize, send an extra dict and a None message.
# the extras dict will be mapped into the intent extras Bundle.
# For dicts where all values are keys this will be sent as url parameters,
# but for more complex nested collections the extras dict will be sent via
# the bulk message api.
device.send_message(None, extra={"foo": "bar"})

device = APNSDevice.objects.get(registration_id=apns_token)
device.send_message("You've got mail") # Alert message may only be sent as text.
device.send_message(None, badge=5) # No alerts but with badge.
device.send_message(None, content_available=1, extra={"foo": "bar"}) # Silent message with custom data.
# alert with title and body.
device.send_message(message={"title" : "Game Request", "body" : "Bob wants to play poker"}, extra={"foo": "bar"})
device.send_message("Hello again", thread_id="123", extra={"foo": "bar"}) # set thread-id to allow iOS to merge notifications

Note

APNS does not support sending payloads that exceed 2048 bytes (increased from 256 in 2014). The message is only one part of the payload, if once constructed the payload exceeds the maximum size, an APNSDataOverflow exception will be raised before anything is sent. Reference: Apple Payload Documentation

Sending messages in bulk

from push_notifications.models import APNSDevice, GCMDevice

devices = GCMDevice.objects.filter(user__first_name="James")
devices.send_message("Happy name day!")

Sending messages in bulk makes use of the bulk mechanics offered by GCM and APNS. It is almost always preferable to send bulk notifications instead of single ones.

It's also possible to pass badge parameter as a function which accepts token parameter in order to set different badge value per user. Assuming User model has a method get_badge returning badge count for a user:

devices.send_message(
        "Happy name day!",
        badge=lambda token: APNSDevice.objects.get(registration_id=token).user.get_badge()
)

Firebase vs Google Cloud Messaging

django-push-notifications supports both Google Cloud Messaging and Firebase Cloud Messaging (which is now the officially supported messaging platform from Google). When registering a device, you must pass the cloud_message_type parameter to set the cloud type that matches the device needs. This is currently defaulting to 'GCM', but may change to 'FCM' at some point. You are encouraged to use the officially supported library.

When using FCM, django-push-notifications will automatically use the notification and data messages format to be conveniently handled by Firebase devices. You may want to check the payload to see if it matches your needs, and review your notification statuses in FCM Diagnostic console.

# Create a FCM device
fcm_device = GCMDevice.objects.create(registration_id="token", cloud_message_type="FCM", user=the_user)

# Send a notification message
fcm_device.send_message("This is a message")

# Send a notification message with additionnal payload
fcm_device.send_message("This is a enriched message", extra={"title": "Notification title", "icon": "icon_ressource"})

# Send a notification message with additionnal payload (alternative syntax)
fcm_device.send_message("This is a enriched message", title="Notification title", badge=6)

# Send a notification message with extra data
fcm_device.send_message("This is a message with data", extra={"other": "content", "misc": "data"})

# Send a notification message with options
fcm_device.send_message("This is a message", time_to_live=3600)

# Send a data message only
fcm_device.send_message(None, extra={"other": "content", "misc": "data"})

You can disable this default behaviour by setting use_fcm_notifications to False.

fcm_device = GCMDevice.objects.create(registration_id="token", cloud_message_type="FCM", user=the_user)

# Send a data message with classic format
fcm_device.send_message("This is a message", use_fcm_notifications=False)

Sending FCM/GCM messages to topic members

FCM/GCM topic messaging allows your app server to send a message to multiple devices that have opted in to a particular topic. Based on the publish/subscribe model, topic messaging supports unlimited subscriptions per app. Developers can choose any topic name that matches the regular expression, "/topics/[a-zA-Z0-9-_.~%]+". Note: gcm_send_bulk_message must be used when sending messages to topic subscribers, and setting the first param to any value other than None will result in a 400 Http error.

from push_notifications.gcm import send_message

# First param is "None" because no Registration_id is needed, the message will be sent to all devices subscribed to the topic.
send_message(None, {"body": "Hello members of my_topic!"}, to="/topics/my_topic")

Reference: FCM Documentation

Exceptions

  • NotificationError(Exception): Base exception for all notification-related errors.
  • gcm.GCMError(NotificationError): An error was returned by GCM. This is never raised when using bulk notifications.
  • apns.APNSError(NotificationError): Something went wrong upon sending APNS notifications.
  • apns.APNSDataOverflow(APNSError): The APNS payload exceeds its maximum size and cannot be sent.

Django REST Framework (DRF) support

ViewSets are available for both APNS and GCM devices in two permission flavors:

  • APNSDeviceViewSet and GCMDeviceViewSet

    • Permissions as specified in settings (AllowAny by default, which is not recommended)
    • A device may be registered without associating it with a user
  • APNSDeviceAuthorizedViewSet and GCMDeviceAuthorizedViewSet

    • Permissions are IsAuthenticated and custom permission IsOwner, which will only allow the request.user to get and update devices that belong to that user
    • Requires a user to be authenticated, so all devices will be associated with a user

When creating an APNSDevice, the registration_id is validated to be a 64-character or 200-character hexadecimal string. Since 2016, device tokens are to be increased from 32 bytes to 100 bytes.

Routes can be added one of two ways:

from push_notifications.api.rest_framework import APNSDeviceAuthorizedViewSet, GCMDeviceAuthorizedViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'device/apns', APNSDeviceAuthorizedViewSet)
router.register(r'device/gcm', GCMDeviceAuthorizedViewSet)

urlpatterns = patterns('',
        # URLs will show up at <api_root>/device/apns
        url(r'^', include(router.urls)),
        # ...
)
  • Using as_view (specify which views to include)
from push_notifications.api.rest_framework import APNSDeviceAuthorizedViewSet

urlpatterns = patterns('',
        # Only allow creation of devices by authenticated users
        url(r'^device/apns/?$', APNSDeviceAuthorizedViewSet.as_view({'post': 'create'}), name='create_apns_device'),
        # ...
)

Update of device with duplicate registration ID

The DRF viewset enforce the uniqueness of the registration ID. In same use case it may cause issue: If an already registered mobile change its user and it will fail to register because the registration ID already exist.

When option UPDATE_ON_DUPLICATE_REG_ID is set to True, then any creation of device with an already existing registration ID will be transformed into an update.

The UPDATE_ON_DUPLICATE_REG_ID only works with DRF.

[1] Any devices which are not selected, but are not receiving notifications will not be deactivated on a subsequent call to "prune devices" unless another attempt to send a message to the device fails after the call to the feedback service.
Comments
  • compatibility issues with linux

    compatibility issues with linux

    i used django-push-notifications in windows , it was working fine , i used

    router.register(r'device/gcm', GCMDeviceViewSet)

    to add new devices and it was working fine but when i ported my app to a linux system the response i get is the device is sucessfully added but when i check device list nothing is added

    question 
    opened by ak-24 36
  • Django Rest Framework Support

    Django Rest Framework Support

    Almost 100% based on PR #102. One minor update to the DeviceMixin for DRF v3 support. Also restricts device access to only those uploaded by the user.

    opened by matthewh 32
  • Proposal: model refactoring

    Proposal: model refactoring

    Introduction

    Currently only 2 fields are actually required for GCMDevice and ANPSDevice models, they are:

    • active - which helps manage active devices
    • registration_id - required for identifying device which should receive push notification

    What about other fields:

    • date_created - required only in case if library user need track time, when specific device was added to internal database. For now, I have no use for this field.
    • user - optional field, which links together user instance with it's devices. Actually some devices can be linked with groups or behave in very different way.
    • name - very optional. Not every library user will use this option, because usually you only need to handle two distinct states: something have device or have not. Take care about naming devices is not main purpose of library for sending push notifications.

    Problems with extending current models

    Device model have not all necessary information. As I mentioned above they are: active and registration_id. It is only abstract model and extending existing concrete models (GCMDevice or ANPSDevice) using OneToOneField will brings additional database overhead at least.

    Why not make own concrete class with Device as base class?

    • There are lot of tips and tricks about registration_id and device_id in model and DRF serializers and reimplementing their behavior is not fun.

    Proposal

    Make two different base abstract models for each device type:

    • BaseGCMDevice(registration_id: token, active: bool)
    • BaseAPNSDevice(registration_id: token, active: boo)

    Base device model classed should be able to handle sending notifications, as they have all necessary information.

    All optional fields append as mixin for concrete device model classes:

    • DeviceModelMixin(created: datetime, user: User, name: str)
    • GCMDevice extends DeviceModelMixin, BaseGCMDevice
    • APNSDevice extends DeviceModelMixin, BaseAPNSDevice

    Also concrete device models can implement any custom logic, that didn't match responsabilities of this library.

    Serializers and views should behave in same way, e.g. BaseGCMSerializer handle model specific actions (validations, etc.) and concrete serializer (GCMSerializer) append logic specific for concrete device model.

    With such approach we will receive library which do one thing - sends push notifications using minimal amount of required data, as well as allows extending very fast and easy.

    question 
    opened by koutoftimer 28
  • Correcting rest framework validation of GCMDevice.device_id max value.

    Correcting rest framework validation of GCMDevice.device_id max value.

    Half of the valid GCM device_id range currently elicits a ValidationError due to incorrect validation added in e8d07766e2e89b6ff176e386e88039a3c8cc1554, whereby the submitted value is checked against the signed 64 bit integer maximum rather than the unsigned.

    opened by lukeburden 27
  • Full implementation for multiple mobile applications (solves #130, supercedes #226)

    Full implementation for multiple mobile applications (solves #130, supercedes #226)

    This is a replacement for the pull requests #247/#259 with all-in-one commit.

    This patch extends the packet to support multiple mobile applications receiving push notifications from one server.

    Code, documentation, and tests are provided.

    The implementation is backward-compatible, so old-style using of the packet is still available.

    Three types of settings are available:

    • old-style - one mobile application per server instance, compatibility mode
    • new-style static - multiple (several) mobile applications per server instance, settings file contains direct description of mobile applications credentials (key/certificate), a new mobile application requires settings patching or reloading
    • new-style dynamic - multiple (a lot) mobile applications per server instance, settings file contains reference to the custom model with mobile application credentials (key/certificate), a new mobile application can be created at runtime without server restart

    This patch supercedes and dismisses (makes irrelevant) patch #226

    This patch solves #130

    opened by nnseva 26
  • New release ?

    New release ?

    Would it be possible to enjoy a new release of django-push-notifications ? It has been one year and half since no release and I can see several commits the community could benefit from, given that Django 4.0 is out and that the current 2.0.0 release emits the following warnings :

    /venv/lib/python3.8/site-packages/push_notifications/models.py:235: RemovedInDjango40Warning: django.utils.translation.ugettext_lazy() is deprecated in favor of django.utils.translation.gettext_lazy().
        verbose_name=_("User public encryption key"),
    

    Thanks for this package, keep up the good work!

    opened by frallain 25
  • APNs enhancement to the feedback API.

    APNs enhancement to the feedback API.

    Wrapped the APNs socket calls to connect to either the push service or the feedback service. Wrapped the socket call with a function to parse the output.

    Added a prune_devices management command and admin API. The management command deactivates any device that's no longer receiving push messages. The admin API only deactivates the selected non-responding devices.

    opened by shigmas 25
  • Please read: Looking for volunteers to co-maintain this project

    Please read: Looking for volunteers to co-maintain this project

    If you want to get commit access to this project please express your interest here. I no longer use django-push-notifications myself but I don't want to see it rot away.

    I however ask that you maintain a very high standard for the code that goes in and not change the coding or commit style.

    I will only consider developers who have already contributed to the project, or can prove in some way that they are very familiar with the codebase.

    This won't be completely hands-off, I will remain available to help for releases and chime in for decisions.

    opened by jleclanche 24
  • Added support for multiple APNS certificates

    Added support for multiple APNS certificates

    Supersedes PR #182.

    This update introduces a new setting, APNS_APP_CERTIFICATES, and forcefully requires developers to replace APNS_CERTIFICATE. Settings are validated at startup using Django system checks. Based on the work of @cristiano2lopes with minor updates to address outstanding issues mentioned by the package maintainer.

    Fixes: #130

    opened by matthewh 20
  • make migrations shouldn't create migrate files for APNSDevice and GCMDevice

    make migrations shouldn't create migrate files for APNSDevice and GCMDevice

    It alter the fields UUID to use models.UUID in both of these models

    Shouldn't this done already in Initial migration?

    dependencies = [
            ('push_notifications', '0001_initial'),
        ]
    
        operations = [
            migrations.AlterField(
                model_name='apnsdevice',
                name='device_id',
                field=models.UUIDField(blank=True, help_text=b'UDID / UIDevice.identifierForVendor()', null=True, verbose_name='Device ID', db_index=True),
            ),
            migrations.AlterField(
                model_name='gcmdevice',
                name='device_id',
                field=push_notifications.fields.HexIntegerField(help_text='ANDROID_ID / TelephonyManager.getDeviceId() (always as hex)', null=True, verbose_name='Device ID', db_index=True, blank=True),
            ),
        ]
    

    PS. I install package via pip (1.2.1)

    bug migrations 
    opened by sainttail 20
  • Add configuration option to make APNSDevice.registration_id unique_together with user_id, fixes #41.

    Add configuration option to make APNSDevice.registration_id unique_together with user_id, fixes #41.

    I have added the configuration option, but unsure about what to test (and how). Tests pass (except for py34 which i don't have local) with original settings and when adding the flag.

    opened by eLod 18
  • Pruning inactive devices

    Pruning inactive devices

    What is best practice when it comes to removing inactive tokens from the database (APNS in my case, for now)? It seems like there was a way to do this at some point (#236 and #142), but I can't find it anywhere in the DPN library. Is there still a built in way to handle this or do people implement their own solutions?

    opened by DanielssonP 0
  • Add WebPush support for Safari

    Add WebPush support for Safari

    PR for #673 Stores the full endpoint url for web push given by the browser, in the registration_id of the model, instead of just the token, and use exactly what the browser gave us when we send the message. Still supports existing registration_id/browser combo's but rasies a warning.

    Also:

    • Adds tests for web push
    • Update docs for configuring web push
    • Handle 404/410 when regeistering and deactivate device
    • Remove pywebpush from APNS' options.extras_require in setup.cfg

    The new docs address issues/questions raised in #647, #658, #665 The PR was inspired/informed by #487, #558, #602, #603, #604, #605

    opened by blighj 0
  • [WebPush] Support for Safari

    [WebPush] Support for Safari

    Safari 16.1 on Mac OS Ventura supports WebPush, with the potential for it to come to iOS in 2023

    It will need a code change to make it work with django-push-notifications.

    As I understand it, when you register for a push notifaction on the client side, the browser gives you the full endpoint url to use. The way it is modeled in the code here, is that we only want the last part of the path from the endpoint url (some sort of token) and will determine the full endpoint url based on pre configured settings for each browser. These settings don't exist for Safari yet.

    Apple and Microsoft seem to have made the same implementation decision where the push notifaction endpoints can actually be one of multiple domains, (*.notify.windows.com, *.push.apple.com). So fixing a browser to just one endpoint may not be the best way to model the standard.

    Would it be perferable to store the full endpoint url given by the browser in the registration_id of the model, instead of just the token and use exactly what the browser gave us when we send the message. We could depercate the old way, but still support it for a while with a fallback. https://github.com/jazzband/django-push-notifications/blob/a20819258cc33ed7855db7231831bb9231dfe1ca/push_notifications/webpush.py#L7

    def get_subscription_info(application_id, uri, browser, auth, p256dh):
    -	url = get_manager().get_wp_post_url(application_id, browser)
    +	if uri.startswith("https://"):
    +		endpoint = uri
    +	else:
    +		url = get_manager().get_wp_post_url(application_id, browser)
    +		endpoint = "{}/{}".format(url, uri)	
    	return {
    -		"endpoint": "{}/{}".format(url, uri),
    +		"endpoint": endpoint,
    		"keys": {
    			"auth": auth,
    			"p256dh": p256dh,
    		}
    	}
    

    If people are happy with that general approach I can do up a PR with updated docs and tests.

    opened by blighj 0
  • HexadecimalField accepts non-hex values

    HexadecimalField accepts non-hex values

    The regular expression it uses is wrong, so HexadecimalField will happily accept for example "DJANGO" as input.

    This has been a bug for almost a decade... A PR with a fix is coming in a minute.

    opened by denizdogan 0
Releases(3.0.0)
  • 3.0.0(Feb 17, 2022)

    What's Changed

    • #567 Fixes crash on bulk_send test message operation from admin by @DataGreed in https://github.com/jazzband/django-push-notifications/pull/568
    • Updated README: added info about APNS_AUTH_KEY_PATH, APNS_AUTH_KEY_ID, APNS_TEAM_ID by @DataGreed in https://github.com/jazzband/django-push-notifications/pull/566
    • Add some details to APNS settings description by @scherbakovx in https://github.com/jazzband/django-push-notifications/pull/575
    • Django 4.0 warnings by @jheld in https://github.com/jazzband/django-push-notifications/pull/585
    • Run tests on python3.9 by @bertonha in https://github.com/jazzband/django-push-notifications/pull/589
    • add mutable_content to fcm by @paradizer in https://github.com/jazzband/django-push-notifications/pull/582
    • Improve efficiency of ORM lookups by @code-review-doctor in https://github.com/jazzband/django-push-notifications/pull/598
    • Drop python 2.x and django before 2.2 by @sevdog in https://github.com/jazzband/django-push-notifications/pull/599
    • Migrate to GitHub Actions. by @jezdez in https://github.com/jazzband/django-push-notifications/pull/607
    • Mention WebPushDevice in README by @Tyilo in https://github.com/jazzband/django-push-notifications/pull/608
    • Make push optional dependecies by @sevdog in https://github.com/jazzband/django-push-notifications/pull/600
    • fix: Remove python3.5 deps by @bertonha in https://github.com/jazzband/django-push-notifications/pull/621
    • Spelling error & convert string to be translatable by @Andrew-Chen-Wang in https://github.com/jazzband/django-push-notifications/pull/614
    • ++Enable sending image in GCM notifications by @ashishnitinpatil in https://github.com/jazzband/django-push-notifications/pull/624
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/jazzband/django-push-notifications/pull/625
    • Change feature detection logic for database field by @sevdog in https://github.com/jazzband/django-push-notifications/pull/612
    • Add Edge support to webpush by @simonkern in https://github.com/jazzband/django-push-notifications/pull/631
    • chore: test on Django 4.0 by @bertonha in https://github.com/jazzband/django-push-notifications/pull/638
    • chore: test only active Django versions by @bertonha in https://github.com/jazzband/django-push-notifications/pull/639
    • chore: dry tests, use newer DRF on tests by @bertonha in https://github.com/jazzband/django-push-notifications/pull/641
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/jazzband/django-push-notifications/pull/635
    • More robust handling of userAgentData, fix WebPushDeviceAdmin by @simonkern in https://github.com/jazzband/django-push-notifications/pull/643
    • [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in https://github.com/jazzband/django-push-notifications/pull/644
    • Add missing migration for https://github.com/jazzband/django-push-notifications/pull/614 by @simonkern in https://github.com/jazzband/django-push-notifications/pull/645
    • Jazzband: Synced file(s) with jazzband/.github by @jazzband-bot in https://github.com/jazzband/django-push-notifications/pull/626
    • Show coverage badge in README. by @shimakaze-git in https://github.com/jazzband/django-push-notifications/pull/651

    New Contributors

    • @scherbakovx made their first contribution in https://github.com/jazzband/django-push-notifications/pull/575
    • @jheld made their first contribution in https://github.com/jazzband/django-push-notifications/pull/585
    • @bertonha made their first contribution in https://github.com/jazzband/django-push-notifications/pull/589
    • @paradizer made their first contribution in https://github.com/jazzband/django-push-notifications/pull/582
    • @code-review-doctor made their first contribution in https://github.com/jazzband/django-push-notifications/pull/598
    • @sevdog made their first contribution in https://github.com/jazzband/django-push-notifications/pull/599
    • @jezdez made their first contribution in https://github.com/jazzband/django-push-notifications/pull/607
    • @Tyilo made their first contribution in https://github.com/jazzband/django-push-notifications/pull/608
    • @Andrew-Chen-Wang made their first contribution in https://github.com/jazzband/django-push-notifications/pull/614
    • @ashishnitinpatil made their first contribution in https://github.com/jazzband/django-push-notifications/pull/624
    • @pre-commit-ci made their first contribution in https://github.com/jazzband/django-push-notifications/pull/625
    • @simonkern made their first contribution in https://github.com/jazzband/django-push-notifications/pull/631
    • @jazzband-bot made their first contribution in https://github.com/jazzband/django-push-notifications/pull/626
    • @shimakaze-git made their first contribution in https://github.com/jazzband/django-push-notifications/pull/651

    Full Changelog: https://github.com/jazzband/django-push-notifications/compare/2.0.0...3.0.0

    Source code(tar.gz)
    Source code(zip)
    django-push-notifications-3.0.0.tar.gz(52.53 KB)
    django_push_notifications-3.0.0-py3-none-any.whl(32.60 KB)
  • 1.4.1(Jan 11, 2016)

  • 1.4.0(Jan 11, 2016)

    Changelog:

    • BACKWARDS-INCOMPATIBLE: Drop support for Python<3.4
    • DJANGO: Support Django 1.9
    • GCM: Handle canonical IDs
    • GCM: Allow full range of GCMDevice.device_id values
    • GCM: Do not allow duplicate registration_ids
    • DRF: Work around empty boolean defaults issue (django-rest-framework#1101)
    • BUGFIX: Do not throw GCMError in bulk messages from the admin
    • BUGFIX: Avoid generating an extra migration on Python 3
    • BUGFIX: Only send in bulk to active devices
    • BUGFIX: Display models correctly in the admin on both Python 2 and 3
    Source code(tar.gz)
    Source code(zip)
  • 1.3.1(Jun 30, 2015)

  • 1.3.0(Jun 30, 2015)

    This release drops support for Python<2.7 and Django<1.8. Upgrade from the 1.2 branch is highly recommended.

    Changelog:

    • BACKWARDS-INCOMPATIBLE: Drop support for Python<2.7
    • BACKWARDS-INCOMPATIBLE: Drop support for Django<1.8
    • NEW FEATURE: Added a Django Rest Framework API. Requires DRF>=3.0.
    • APNS: Add support for setting the ca_certs file with new APNS_CA_CERTIFICATES setting
    • GCM: Deactivate GCMDevices when their notifications cause NotRegistered or InvalidRegistration
    • GCM: Indiscriminately handle all keyword arguments in gcm_send_message and gcm_send_bulk_message
    • GCM: Never fall back to json in gcm_send_message
    • BUGFIX: Fixed migration issues from 1.2.0 upgrade.
    • BUGFIX: Better detection of SQLite/GIS MySQL in various checks
    • BUGFIX: Assorted Python 3 bugfixes
    • BUGFIX: Fix display of device_id in admin

    This release was made possible thanks to the contributions of the following people:

    Andrey Zevakin, David Pretty, Jamaal Scarlett, Jerome Leclanche, Matthew Hershberger, Mohamad Nour Chawich and Nicolas Delaby

    Source code(tar.gz)
    Source code(zip)
  • 1.2.1(Apr 11, 2015)

    This is a bugfix release and the last release to support Python versions older than 2.7, and Django versions older than 1.8.

    Changelog:

    • APNS, GCM: Add a db_index to the device_id field
    • APNS: Use the native UUIDField on Django 1.8
    • APNS: Fix timeout handling on Python 3
    • APNS: Restore error checking on apns_send_bulk_message
    • GCM: Expose the time_to_live argument in gcm_send_bulk_message
    • GCM: Fix return value when gcm bulk is split in batches
    • GCM: Improved error checking reliability
    • GCM: Properly pass kwargs in GCMDeviceQuerySet.send_message()
    • BUGFIX: Fix HexIntegerField for Django 1.3

    This release was made possible thanks to the contributions of the following people:

    Antonin Lenfant, Arthur Silva, Daniel Kronovet, Innocenty Enikeew, Jack Feng, Jerome Leclanche, Remigiusz Dymecki and @GaleDragon

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Oct 7, 2014)

    Changelog:

    • BACKWARDS-INCOMPATIBLE: Added support for Django 1.7 migrations. South users will have to upgrade to South 1.0 or Django 1.7.
    • APNS: APNS MAX_NOTIFICATION_SIZE is now a setting and its default has been increased to 2048
    • APNS: Always connect with TLSv1 instead of SSLv3
    • APNS: Implemented support for APNS Feedback Service
    • APNS: Support for optional "category" dict
    • GCM: Improved error handling in bulk mode
    • GCM: Added support for time_to_live parameter
    • BUGFIX: Fixed various issues relating HexIntegerField
    • BUGFIX: Fixed issues in the admin with custom user models

    This release was made possible thanks to the contributions of the following people:

    Alan Descoins, Alistair Broomhead, Arthur Silva, Francois Lebel, Jay Camp, Jerome Leclanche, Sergei Evdokimov, Sujit Nair, Thomas Iovine and @shigmas.

    Source code(tar.gz)
    Source code(zip)
Owner
Jazzband
Jazzband
Django-gmailapi-json-backend - Email backend for Django which sends email via the Gmail API through a JSON credential

django-gmailapi-json-backend Email backend for Django which sends email via the

Innove 1 Sep 9, 2022
Send logs to RabbitMQ from Python/Django.

python-logging-rabbitmq Logging handler to ships logs to RabbitMQ. Compatible with Django. Installation Install using pip. pip install python_logging_

Alberto Menendez Romero 38 Nov 17, 2022
Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project

Django URL Shortener Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project Install this package to your Dja

Rishav Sinha 4 Nov 18, 2021
Automatic caching and invalidation for Django models through the ORM.

Cache Machine Cache Machine provides automatic caching and invalidation for Django models through the ORM. For full docs, see https://cache-machine.re

null 846 Nov 26, 2022
Meta package to combine turbo-django and stimulus-django

Hotwire + Django This repository aims to help you integrate Hotwire with Django ?? Inspiration might be taken from @hotwired/hotwire-rails. We are sti

Hotwire for Django 31 Aug 9, 2022
django-reversion is an extension to the Django web framework that provides version control for model instances.

django-reversion django-reversion is an extension to the Django web framework that provides version control for model instances. Requirements Python 3

Dave Hall 2.8k Jan 2, 2023
Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application.

Django-environ django-environ allows you to use Twelve-factor methodology to configure your Django application with environment variables. import envi

Daniele Faraglia 2.7k Jan 7, 2023
Rosetta is a Django application that eases the translation process of your Django projects

Rosetta Rosetta is a Django application that facilitates the translation process of your Django projects. Because it doesn't export any models, Rosett

Marco Bonetti 909 Dec 26, 2022
Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.

Cookiecutter Django Powered by Cookiecutter, Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly. Documentati

Daniel Feldroy 10k Dec 31, 2022
Django project starter on steroids: quickly create a Django app AND generate source code for data models + REST/GraphQL APIs (the generated code is auto-linted and has 100% test coverage).

Create Django App ?? We're a Django project starter on steroids! One-line command to create a Django app with all the dependencies auto-installed AND

imagine.ai 68 Oct 19, 2022
django-quill-editor makes Quill.js easy to use on Django Forms and admin sites

django-quill-editor django-quill-editor makes Quill.js easy to use on Django Forms and admin sites No configuration required for static files! The ent

lhy 139 Dec 5, 2022
A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, celery and redis.

Django Channels Websocket Chatbot A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, c

Yunbo Shi 8 Oct 28, 2022
A handy tool for generating Django-based backend projects without coding. On the other hand, it is a code generator of the Django framework.

Django Sage Painless The django-sage-painless is a valuable package based on Django Web Framework & Django Rest Framework for high-level and rapid web

sageteam 51 Sep 15, 2022
A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a unique id.

Django-URL-Shortener A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a uni

Rohini Rao 3 Aug 8, 2021
Dockerizing Django with Postgres, Gunicorn, Nginx and Certbot. A fully Django starter project.

Dockerizing Django with Postgres, Gunicorn, Nginx and Certbot ?? Features A Django stater project with fully basic requirements for a production-ready

null 8 Jun 27, 2022
pytest-django allows you to test your Django project/applications with the pytest testing tool.

pytest-django allows you to test your Django project/applications with the pytest testing tool.

pytest-dev 1.1k Dec 14, 2022
APIs for a Chat app. Written with Django Rest framework and Django channels.

ChatAPI APIs for a Chat app. Written with Django Rest framework and Django channels. The documentation for the http end points can be found here This

Victor Aderibigbe 18 Sep 9, 2022
django-dashing is a customisable, modular dashboard application framework for Django to visualize interesting data about your project. Inspired in the dashboard framework Dashing

django-dashing django-dashing is a customisable, modular dashboard application framework for Django to visualize interesting data about your project.

talPor Solutions 703 Dec 22, 2022
Django-MySQL extends Django's built-in MySQL and MariaDB support their specific features not available on other databases.

Django-MySQL The dolphin-pony - proof that cute + cute = double cute. Django-MySQL extends Django's built-in MySQL and MariaDB support their specific

Adam Johnson 504 Jan 4, 2023