рдПрдХ рдФрд░ рдЧрд╛рдЗрдб рдХреНрдпреЛрдВ?
рдЬрдм рдореБрдЭреЗ рдбреНрд░рд╛рдлреНрдЯ рдкреБрд╢ рдиреЛрдЯрд┐рдлрд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдХрд╛ рдХрд╛рдо рд╕реМрдВрдкрд╛ рдЧрдпрд╛, рддреЛ рдПрдХ рддреНрд╡рд░рд┐рдд рдЦреЛрдЬ рд╕реЗ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ рд╣рдм рдкрд░ рдкреБрд╢ рдиреЛрдЯрд┐рдлрд┐рдХреЗрд╢рди рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдкрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдмрд╣реБрдд рд╕рд╛рд░реЗ рд▓реЗрдЦ рдереЗред рдпрд╣рд╛рдБ рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рд╕рдмрд╕реЗ рдЙрдкрдпреБрдХреНрдд рд╣реИрдВ:
рдЬреЗрдПрд╕ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ: рд╡реЗрдм рдкреБрд╢ рд╕реВрдЪрдирд╛рдПрдВрд╡реЗрдм PUSH рдЕрдзрд┐рд╕реВрдЪрдирд╛рдПрдВ рддреЗрдЬ рдФрд░ рдЖрд╕рд╛рди 924 /рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ред рд╡реЗрдм рдкреБрд╢ рдФрд░ рд╡реЗ рдХрд╣рд╛рдБ рд░рд╣рддреЗ рд╣реИрдВрдпрд╣ рд╕рдм рдареАрдХ рд╣реИ, рд▓реЗрдХрд┐рди рд╡реНрдпрдХреНрддрд┐рдЧрдд рд░реВрдк рд╕реЗ рдореЗрд░реЗ рдкрд╛рд╕ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдПрдХ рд╕рд░рд▓ рдФрд░ рд╕рдордЭрдиреЗ рдпреЛрдЧреНрдп рдорд╛рд░реНрдЧрджрд░реНрд╢рд┐рдХрд╛ рдХрд╛ рдЕрднрд╛рд╡ рдерд╛ рдЬреЛ рдореБрдЭреЗ рддреБрд░рдВрдд рдХреЙрдкреА-рдкреЗрд╕реНрдЯ рджреНрд╡рд╛рд░рд╛ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рдЕрдиреБрдорддрд┐ рджреЗрдЧрд╛, рд╕рдм рдХреБрдЫ рддреБрд░рдВрдд рдХрд╛рдо рдХрд░ рджреЗрдЧрд╛ред рдареАрдХ рд╣реИ, рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореИрдиреБрдЕрд▓ рдХреЗ рдмреАрдЪ рдореЗрдВ рдмреИрдХрд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдЕрдиреБрдХреВрд▓ рдХреЛрдИ рдЕрдЬрдЧрд░ рдирд╣реАрдВ рд╣реИред
рдЕрдВрдд рдореЗрдВ рд╕реВрдЪрдирд╛рдУрдВ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдореЗрдВ рддреАрди рджрд┐рди рд▓рдЧреЗ рдФрд░ рдореБрдЭреЗ рдРрд╕рд╛ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╣реИред рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдореЗрд░рд╛ рд▓реЗрдЦ рдХрд┐рд╕реА рдХреЛ рддреАрди рджрд┐рдиреЛрдВ рдХреЗ рдмрдЬрд╛рдп рддреАрди рдШрдВрдЯреЗ рдореЗрдВ рдкреБрд╢ рдиреЛрдЯрд┐рдлрд┐рдХреЗрд╢рди рд╕реЗрдЯ рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдЧрд╛ред
рдореИрдВ рдЬрд┐рд╕ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдкрд░ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реВрдВ, рд╡рд╣ Django рдкрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХреА рдЧрдИ рд╣реИ рдФрд░ рдореИрдВ рдЗрд╕ рд░реВрдкрд░реЗрдЦрд╛ рдХреЗ рд╕рдВрдмрдВрдз рдореЗрдВ рдХрд╛рдо рдХреА рдкреНрд░рдЧрддрд┐ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реВрдВрдЧрд╛, рд▓реЗрдХрд┐рди рдЬреЛ рд▓реЛрдЧ рдЖрд╕рд╛рдиреА рд╕реЗ рдЗрд╕реЗ рдлреНрд▓рд╛рд╕реНрдХ рдпрд╛ рдХреБрдЫ рдФрд░ рдХреЗ рд▓рд┐рдП рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рддреЛ, рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВред
рд╣рдореЗрдВ рдЪрд╛рдмреА рдорд┐рд▓ рдЬрд╛рддреА рд╣реИ
рдмрд┐рдирд╛ рдЪрд╛рдмреА рдХреЗ, рд╕реНрд╡рд╛рднрд╛рд╡рд┐рдХ рд░реВрдк рд╕реЗ, рдЙрдиреНрд╣реЛрдВрдиреЗ рд╣рдореЗрдВ рдХрд╣реАрдВ рдирд╣реАрдВ рдЬрд╛рдиреЗ рджрд┐рдпрд╛, рддреЛ рдЪрд▓рд┐рдП рдЙрдирдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдХреБрдВрдЬрд┐рдпреЛрдВ рдХреЛ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ
py_vapid рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ред рдпрд╣ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ
pywebpush рдХреЗ рд╕рд╛рде рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рд╣рдореЗрдВ рдЕрднреА рднреА рдмрд╛рдж рдореЗрдВ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА, рдЗрд╕рд▓рд┐рдП, рджреЛ рдмрд╛рд░ рдирд╣реАрдВ рдЙрдардиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо pywebpush рд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВрдЧреЗ:
> bin/pip install pywebpush < > > bin/vapid --applicationServerKey No private_key.pem file found. Do you want me to create one for you? (Y/n)Y Generating private_key.pem Generating public_key.pem Application Server Key = < Server Key>
рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╕рд░реНрд╡рд░ рдХреБрдВрдЬреА рдорд╛рди рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИред
NOTIFICATION_KEY = < Server Key>
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╣рдореЗрдВ рд╕рдВрджрд░реНрдн рдХреЗ рд▓рд┐рдП NOTIFICATION_KEY рдкрд╛рд╕ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХрд╛ рд╕рдмрд╕реЗ рдЖрд╕рд╛рди рддрд░реАрдХрд╛ рд╣реИ рдХрд┐ рдЖрдк
рдЕрдкрдирд╛ рд╕рдВрджрд░реНрдн_рдкреНрд░реЛрд╕реЗрд╕рд░ рд▓рд┐рдЦреЗрдВред
рд╕реЗрд╡рд╛ рдХрд░реНрдореА рдмрдирд╛рдирд╛
рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛, рдЬреЛ рдирд╣реАрдВ рдЬрд╛рдирддрд╛ рд╣реИ - рдпрд╣ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдлрд╝рд╛рдЗрд▓ рд╣реИ рдЬреЛ рдкреГрд╖реНрдарднреВрдорд┐ рдореЗрдВ рдЪрд▓рддреА рд╣реИред рд╣рдореЗрдВ рдЕрдкрдиреА рд╕реВрдЪрдирд╛рдПрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
рд╕рдмрд╕реЗ рд╕рд░рд▓ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдХреЛрдб рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
self.addEventListener('push', function(event) { var message = JSON.parse(event.data.text());
рдФрд░ рдЕрдм рд╣рдореЗрдВ рдЕрдкрдиреЗ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдкреНрд░рдХреНрд░рд┐рдпрд╛, рд╕рд┐рджреНрдзрд╛рдВрдд рд░реВрдк рдореЗрдВ,
рдпрд╣рд╛рдБ рд╡рд░реНрдгрд┐рдд
рд╣реИ ред рдЗрд╕рд▓рд┐рдП, рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ:
function checkWorkerAndPushManager () { if (!('serviceWorker' in navigator)) { console.log('Workers are not supported.'); return; } if (!('PushManager' in window)) { console.log('Push notifications are not supported.'); return; } } function registerWorker () { window.addEventListener('load', function () { navigator.serviceWorker.register('/static/js/sw.js').then(function (registration) { console.log('ServiceWorker registration successful'); }, function (err) { console.log('ServiceWorker registration failed: ', err); return; }); }); return true; } var supported = checkWorkerAndPushManager(); if (supported){ var worker = registerWorker (); }
рдорд╣рд╛рди, рдЖрдк рд╣рдорд╛рд░реЗ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдХреЗ рдХрд╛рдо рдХреА рдЬрд╛рдВрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдПрдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдбреЗрд╡рд▓рдкрд░ рдЯреВрд▓ рдкрд░ рдЬрд╛рдПрдВ, рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдПрдХ рд╕рдВрджреЗрд╢ рдХрдВрд╕реЛрд▓ рдкрд░ рд╡реЛрдХрд░ рдХреЗ рд╕рдлрд▓ рдкрдВрдЬреАрдХрд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИ рдФрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЯреИрдм рдкрд░ рдЬрд╛рдПрдВ рдФрд░ рдмрд╛рдИрдВ рдУрд░ рд╕рд░реНрд╡рд┐рд╕ рд╡рд░реНрдХрд░ рдХрд╛ рдЪрдпрди рдХрд░реЗрдВред

рдпрджрд┐ рдЕрдзрд┐рд╕реВрдЪрдирд╛ рджрд┐рдЦрд╛рдИ рдирд╣реАрдВ рджреЗрддреА рд╣реИ, рддреЛ рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рд╕реВрдЪрдирд╛рдПрдВ рдЖрдкрдХреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИрдВред
рдареАрдХ рд╣реИ, рд╣рдо рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рдЦреБрдж рдХреЛ рд╕реВрдЪрдирд╛рдПрдВ рдХреИрд╕реЗ рднреЗрдЬреЗрдВред рдХреВрд▓, рдЪрд▓реЛ рдЖрдЧреЗ рдмрдврд╝рддреЗ рд╣реИрдВред
рд╕реВрдЪрдирд╛рдПрдВ рджрд┐рдЦрд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЕрдиреБрдорддрд┐ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ
рд╡реЛрдХрд░ рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рд╕реВрдЪрдирд╛рдПрдВ рджрд┐рдЦрд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдХреЗ рд▓рд┐рдП рдХрд╣реЗрдВред
function requestPermission() { return new Promise(function(resolve, reject) { const permissionResult = Notification.requestPermission(function(result) {
рдЗрд╕ рдХреЛрдб рдХреЗ рд▓рд┐рдП рдХрд┐рд╕реА рдЯрд┐рдкреНрдкрдгреА рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдпрд╣ рд╕рд┐рд░реНрдл рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред
рд╕рджрд╕реНрдпрддрд╛ рд▓реЗрдВ рдФрд░ рдЕрдкрдиреА рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рдмрдЪрд╛рдПрдВ
рд╕рджрд╕реНрдпрддрд╛ рднреА рд╕рдмрд╕реЗ рдЖрдЧреЗ рд╣реИред
рдЖрдк рдпрд╣рд╛рдВ рд╕рджрд╕реНрдпрддрд╛ рдХреЛрдб рдкрд╛ рд╕рдХрддреЗ рд╣реИрдВ , рд▓реЗрдХрд┐рди рдХреЛрдИ urlBase64ToUint8Array рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреЛрдб рдХрд░рддрд╛ рд╣реВрдВред
NOTIFICATION_KEY = '{{ NOTIFICATION_KEY }}; function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding) .replace(/\-/g, '+') .replace(/_/g, '/') ; const rawData = window.atob(base64); return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0))); } function subscribeUserToPush(key) { return navigator.serviceWorker.register('/static/path/to/js/sw.js') .then(function(registration) { var subscribeOptions = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(key), }; return registration.pushManager.subscribe(subscribeOptions) }) .then(function(pushSubscription) { sendSubscriptionToBackEnd(pushSubscription); }); }
(рдпрд╣рд╛рдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ urlBase64ToUint8Array рд╢рд╛рдпрдж рдмреИрд╕рд╛рдЦреА рдФрд░ рд╕рд╛рдЗрдХрд┐рд▓ рдХреА рд╢реНрд░реЗрдгреА рд╕реЗ рд╣реИ, рд▓реЗрдХрд┐рди рдбреЗрдЯрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП btoa рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рд╕рдлрд▓ рдирд╣реАрдВ рд╣реБрдЖ, рдореБрдЭреЗ рдкрддрд╛ рдирд╣реАрдВ рд╣реИред рд╢рд╛рдпрдж рдХреЛрдИ рдЖрдкрдХреЛ рдмрддрд╛рдПрдЧрд╛)ред
рдлрд┐рд░ рд╣рдо рдкреНрд░рд╛рдкреНрдд рдбреЗрдЯрд╛ рдХреЛ рд╕рд░реНрд╡рд░ рдкрд░ рднреЗрдЬрддреЗ рд╣реИрдВред рдореИрдВрдиреЗ рдЗрд╕реЗ рдЗрд╕ рддрд░рд╣ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рд╣реИ:
function sendSubscriptionToBackEnd(subscription) { $.post( SAVE_REGISTRATION_URL, { 'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
рдЦреИрд░, рдлрд┐рд░ рд╣рдо рдЗрд╕реЗ рд╕рд░реНрд╡рд░ рдкрд░ рд╕рд╣реЗрдЬрддреЗ рд╣реИрдВред рдЖрдк рд▓рд╛рдЗрди рджреНрд╡рд╛рд░рд╛ рд╕рд╣реА рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╣рд╛рдВ, рдПрдХ-рд╕реЗ-рдПрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛-рд╕рджрд╕реНрдпрддрд╛ рд╕рдВрдмрдВрдз рдмрдирд╛рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рди рдХрд░реЗрдВред рдпрд╣ рд╕реНрдкрд╖реНрдЯ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЕрдЪрд╛рдирдХ рдХрд┐рд╕реА рдХреЛ рднреА рдкрд╕рдВрдж рдЖрдПрдЧрд╛ред
рдореИрдВрдиреЗ рдмрдЪрдд рдХреЗ рд▓рд┐рдП рдЗрд╕ рддрд░рд╣ рдХреЗ рдПрдХ рд╕рд░рд▓ рдореЙрдбрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рд╣реИ, рдЗрд╕реЗ рдмрд╛рдж рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдЗрд╕реЗ рджреВрдВрдЧрд╛:
class UserSubscription(models.Model): subscription = models.CharField(max_length=500) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='subscriptions')
рдЕрдВрддрд┐рдо рдЪрд░рдгред Pywebpush рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕рдВрджреЗрд╢ рднреЗрдЬрдирд╛
рд╕рдм рдХреБрдЫ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдореЗрдВ рд╣реИ, рдХреЛрдИ рд╡рд┐рд╢реЗрд╖ рд╕реВрдХреНрд╖реНрдорддрд╛ рдирд╣реАрдВред рдареАрдХ рд╣реИ, рд╕рд┐рд╡рд╛рдп рдЗрд╕рдХреЗ рдХрд┐ рд╕рдВрджреЗрд╢ рдРрд╕реА рд╕рдВрд░рдЪрдирд╛ рдореЗрдВ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдХрд┐ рд╣рдорд╛рд░реЗ рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдЗрд╕реЗ рдкрд╛рд░реНрд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
import json from pywebpush import webpush, WebPushException from django.conf import settings from .models import UserSubscription def push_notification(user_id): user_subscriptions = UserSubscription.objects.filter(user_id=notification.user_id) for subscription in user_subscriptions: data = json.dumps({ 'title': 'Hello', 'body': 'there', }) try: webpush( subscription_info=json.loads(subscription.subscription), data=data, vapid_private_key='./private_key.pem', vapid_claims={ 'sub': 'mailto:{}'.format(settings.ADMIN_EMAIL), } ) notification.sent = True notification.save() except WebPushException as ex: print('I\'m sorry, Dave, but I can\'t do that: {}'.format(repr(ex))) print(ex)
рджрд░рдЕрд╕рд▓, рдЖрдк рдкрд╣рд▓реЗ рд╕реЗ рд╣реА django рд╢реЗрд▓ рд╕реЗ push_notification рдХрд╣ рд╕рдХрддреЗ рд╣реИрдВ рдФрд░ рд╢реБрд░реВ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдЗрд╕ рдХреЛрдб рдореЗрдВ, 410 рдХреА рд╕реНрдерд┐рддрд┐ рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рд░реЛрдХрдирд╛ рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдЙрддреНрддрд░ рдХрд╣рддрд╛ рд╣реИ рдХрд┐ рд╕рджрд╕реНрдпрддрд╛ рд░рджреНрдж рдХрд░ рджреА рдЧрдИ рд╣реИ, рдФрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рдРрд╕реА рд╕рджрд╕реНрдпрддрд╛ рдХреЛ рд╣рдЯрд╛рдирд╛ рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ред
рдирд┐рд╖реНрдХрд░реНрд╖ рдореЗрдВ
рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдПрдХ рдФрд░ рдорд╣рд╛рди
django-webpush рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╣реИ ред рд╢рд╛рдпрдж рдЬреЛ рд▓реЛрдЧ Django рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, рдЙрдиреНрд╣реЗрдВ рдЗрд╕рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред
PS рд╣реИрдкреНрдкреА рдкреНрд░реЛрдЧреНрд░рд╛рдорд░ рдбреЗ!