I have gone through many blogs and apps, mostly all the apps are integrating notifications feature using Django auth user, So if there is any way or blog for it that would be helpful.
Problem That i am facing is i have defined my user model as:
class user_signup(models.Model):
full_name = models.CharField('Full Name',max_length=200)
user_email = models.EmailField('User Email', max_length=255)
user_password = models.CharField('Password', max_length=30)
date_time=models.DateTimeField(auto_now=True)
def __unicode__(self):
return smart_unicode(self.user_email)
And i want to add notification center to the existing model, so what could be the best way for it.
Donot know why anybody didn't answered
You can use one that i recently used , its django-notification module and below is the snippet
Handler:-
from django.db.models.signals import post_save
from notifications.signals import notify
from myapp.models import MyModel
def my_handler(sender, instance, created, **kwargs):
notify.send(instance, verb='was saved')
post_save.connect(my_handler, sender=MyModel)
generate an notification
from notifications.signals import notify
notify.send(user, recipient=user, verb='you reached level 10')
// "recipient" can also be a Group, the notification will be sent to all the Users in the Group
notify.send(comment.user, recipient=group, verb=u'replied', action_object=comment,
description=comment.comment, target=comment.content_object)
notify.send(follow_instance.user, recipient=follow_instance.follow_object, verb=u'has followed you',
action_object=instance, description=u'', target=follow_instance.follow_object, level='success')
Read refs:-
Ref1 and Ref2
Related
I'm struggling with the following.
I'm trying to create a custom signal that will trigger when the current time will be equal to the value of my model's notify_on DateTimeField.
Something like this:
class Notification(models.Model):
...
notify_on = models.DateTimeField()
def send_email(*args, **kwargs):
# send email
signals.when_its_time.connect(send_email, sender=User)
After I've read through all docs and I found no information on how to implement such a signal.
Any ideas?
UPDATE:
Less naive approach with ability to discard irrelevant tasks: https://stackoverflow.com/a/55337663/9631956
Ok, thanks to comments by #SergeyPugach I've done the following:
Added a post_save signal that calls a function that adds a task to the celery. apply_async let's you pass eta - estimated time of arrival which can accept DateTimeField directly, that's very convenient.
# models.py
from django.db.models import signals
from django.db import models
from .tasks import send_notification
class Notification(models.Model):
...
notify_on = models.DateTimeField()
def notification_post_save(instance, *args, **kwargs):
send_notification.apply_async((instance,), eta=instance.notify_on)
signals.post_save.connect(notification_post_save, sender=Notification)
And the actual task in the tasks.py
import logging
from user_api.celery import app
from django.core.mail import send_mail
from django.template.loader import render_to_string
#app.task
def send_notification(self, instance):
try:
mail_subject = 'Your notification.'
message = render_to_string('notify.html', {
'title': instance.title,
'content': instance.content
})
send_mail(mail_subject, message, recipient_list=[instance.user.email], from_email=None)
except instance.DoesNotExist:
logging.warning("Notification does not exist anymore")
I will not get into details of setting up celery, there's plenty of information out there.
Now I will try to figure out how to update the task after it's notification instance was updated, but that's a completely different story.
In django's documentation there is two interesting signals that may help you on this task: pre_save and post_save.
It depends on your needs, but let's say you want to check if your model's notify_on is equal to the current date after saving your model (actually after calling the save() or create() method). If it's your case you can do:
from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
class Notification(models.Model):
...
# Every notification is related to a user
# It depends on your model, but i guess you're doing something similar
user = models.ForeignKey(User, related_name='notify', on_delete=models.DO_NOTHING)
notify_on = models.DateTimeField()
...
def send_email(self, *args, **kwargs):
"""A model method to send email notification"""
...
#receiver(post_save, sender=User)
def create_notification(sender, instance, created, **kwargs):
# check if the user instance is created
if created:
obj = Notification.objects.create(user=instance, date=datetime.now().date())
if obj.notify_on == datetime.now().date():
obj.send_email()
And you should know, that django signals won't work by they own only if there is an action that triggers them. What this mean is that Django signals won't loop over your model's instances and perform an operation, but django signals will trigger when your application is performing an action on the model connected to a signal.
Bonus: To perform a loop over your instances and process an action regulary you may need an asyncworker with a Queue database (mostly, Celery with Redis or RabbitMQ).
Whenever a new User instance is created, I want to create a Profile instance linked to it.
To do this, I'm trying to use signals.
here's code from models.py:
#receiver(post_save, sender=User)
def create_user_profile(sender,**kwargs):
print(sender)
And here's from view.py:
#api_view(["POST"])
def register(request):
username = request.data.get("username")
first_name = request.data.get("first_name")
last_name = request.data.get("last_name")
email = request.data.get("email")
password1 = request.data.get("password1")
password2 = request.data.get("password2")
user = User.objects.create_user(username,email,password1)
if user:
user = authenticate(username=username, password=password1)
if not user:
return Response({"error": "Login failed"}, status=HTTP_401_UNAUTHORIZED)
token, _ = Token.objects.get_or_create(user=user)
return Response({"token": token.key})
However, nothing gets printed to my Terminal when a new User is created.
EDIT: I moved the function to signals.py and edited apps.py but it's still not working:
from django.apps import AppConfig
class AuthConfig(AppConfig):
name = 'auth'
verbose_name = 'Auth Application'
def ready(self):
from . import signals
and here's __init__.py:
default_app_config = 'auth.apps.AuthConfig'
Maybe a bit outdated answer, but is this what you were looking for?
From Django Docs:
Where should this code live?
Strictly speaking, signal handling and registration code can live
anywhere you like, although it’s recommended to avoid the
application’s root module and its models module to minimize
side-effects of importing code.
In practice, signal handlers are usually defined in a signals
submodule of the application they relate to. Signal receivers are
connected in the ready() method of your application configuration
class. If you’re using the receiver() decorator, import the signals
submodule inside ready().
Generally the issue is signals.py hasn't been imported, you need to add it to your AppConfig as mentioned in the comments:
# apps.py
# or whatever the class is called inside the app containing signals.py
class RegistrationConfig(AppConfig):
name = 'registration'
# This is the piece you will need to add
def ready(self):
from . import signals
we are running multiple django sites (let's call them site1, site2, site3) against the same database, and we'd like to permit duplicated usernames accross them.
Site and auth framework do not seem to achieve this, by default, username is a unique field in auth.User.
So what I've done so far (monkey patch, messing up with the user object...):
User._meta.get_field('username')._unique = False
User.add_to_class('site', models.ForeignKey(Site, default=Site.objects.get_current().id, blank=True, null=True))
User._meta.unique_together = (('username', 'site'),)
This piece removes the uniqueness of username, add a site field, make the couple (username, site) unique.
Then come problems which could occure when requesting a User.objects.get(username=xx) (e.g., authentication backends), if some users have the same username on different site.
So, I decided to patch the User.objects manager:
def get_query_set(filter=True):
q = QuerySet(User.objects.model, using=User.objects._db)
if filter:
return q.filter(site = Site.objects.get_current())
return q
User.objects.get_query_set = get_query_set
Seems to work so far. But... the sites use pretty much the same objects, and it's all likely we change user field of these objects using the admin interface, which is common to all sites... hence, if I want to attribute an object (which has a foreignkey to auh.User) to a user of site2 while being logged in as admin on site1, that won't work, as the user manager will filter on site=site1.
I digged up a little, found that this seems to work:
class UserDefaultManager(UserManager):
def get_query_set(self, filter=None):
return QuerySet(User.objects.model)
User._default_manager = UserDefaultManager()
As far as I understand, _default_manager is used by the related objects manager.
Then, User.objects.get(username=xx) filter on sites, and an_object.user won't.
Well, question is: yes, this is messy, and I'm pretty sure there will be flaws, but which are they ?
Next question is: if it's valid, then where is the best place to put this piece of code ? It's currently in a models.py file, just ran as the module is loaded...
Instead of this I propose to use a profile :
models.py:
from django.contrib.auth.models import User
class UserProfile(models.Model):
""" Modèle ajoutant des propriété au modèle User """
user = models.OneToOneField(User, editable=False)
site1 = models.BooleanField()
site2 = models.BooleanField()
site3 = models.BooleanField()
def create_user_profile(sender, instance, created, **kwargs):
""" Crée la jonction entre le modèle User, et le modèle UserProfile """
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
and on each site you create a decorator :
decorators.py:
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps
from django.http import HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from distrib.views.error import error403
def site1_required(function):
#wraps(function)
#login_required
def decorateur(request, *k, **a):
if request.user.get_profile().site1 or request.user.is_superuser:
return function(request, *k, **a)
else:
result = error403(request)
return HttpResponseForbidden(result)
return decorateur
return function
then on each view you add the decorator, if the user is not allowed to connect on this site, he will get a http403 error.
I am working on a recipe website based on django and have run into a snag on the custom backends.
I am receiving a Validation error -no exception supplied when I try to save my cookbook instance in the backend.
here is my backend:
from registration.backends.default import DefaultBackend
from cookbook.models import Cookbook
from django.contrib.auth.models import User
from registration.models import RegistrationProfile
class RecipeekActivationBackend(DefaultBackend):
def register(self, request, **kwargs):
new_user = super(RecipeekActivationBackend, self).register(request, **kwargs)
new_user.save()
cookbook = Cookbook(name=new_user.first_name, pub_date="12/12/2012", user=new_user)
print"cookbook"
cookbook.save()
return new_user
the error occurs at cookbook.save()
here is my Cookbook model:
class Cookbook(models.Model):
def __unicode__(self):
return self.name
name = models.CharField(max_length=50)
pub_date = models.DateTimeField('date published')
user = models.ForeignKey(User, related_name='cookbooks')
recipes = models.ManyToManyField('Recipe', related_name = 'cookbooks')
I believe that is all i need to supply in order to get a little help.
thank you in advance,
A. Cooper
update: the error was caused by pub_date being passed a string instead of a datetime
update2: the way I am going about this is not the best way and i am now going to attempt to use signals to achieve the same outcome
You're going about this all wrong. Authentication backends are for one thing: authentication. The only reason you should be customizing a backend is if you're trying to tie authentication in from another system or need to make some other change like using email for username. Otherwise, use the defaults
Django provides signals for this exact purpose, so that's what you should use.
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=User)
def create_cookbook_for_user(sender, instance, created, *args, **kwargs):
if created and not instance.cookbooks.exists():
Cookbook.objects.create(name=instance.first_name, pub_date=date.today(), user=instance)
Put that in your models.py, and you're done.
See: https://docs.djangoproject.com/en/dev/topics/signals/
I think the error is with pub_date="12/12/2012": that's not a valid value for DateTimeField. Instead, you want to give it a datetime object: datetime.datetime(2012, 12, 12) (after import datetime).
Or maybe datetime.datetime.now(), or something else based on the actual user.
(Also, maybe this should be a DateField, in which case you want datetime.date(2012, 12, 12) or datetime.date.today(). The datetime object above means midnight on December 12th.)
I don't think you can pass a string to pub_date. Try:
import datetime
pub_date=datetime.datetime.now()
I'm currently using django-registration, and it is working well (with some tricks). When the user registers, he has to check his/her mail and click on the activation link. That's fine, but...
What if the user changes the email? I would like to send him/her an email in order to confirm that he is the owner of the email address...
Is there an application, snippet, or something that would save me the time of writing it by myself?
I've faced the same issue recently. And I didn't like the idea of having another app/plugin for just that.
You can achieve that by, listening to User model's singles(pre_save, post_save) and using RegistrationProfile:
signals.py:
from django.contrib.sites.models import Site, RequestSite
from django.contrib.auth.models import User
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from registration.models import RegistrationProfile
# Check if email change
#receiver(pre_save,sender=User)
def pre_check_email(sender, instance, **kw):
if instance.id:
_old_email = instance._old_email = sender.objects.get(id=instance.id).email
if _old_email != instance.email:
instance.is_active = False
#receiver(post_save,sender=User)
def post_check_email(sender, instance, created, **kw):
if not created:
_old_email = getattr(instance, '_old_email', None)
if instance.email != _old_email:
# remove registration profile
try:
old_profile = RegistrationProfile.objects.get(user=instance)
old_profile.delete()
except:
pass
# create registration profile
new_profile = RegistrationProfile.objects.create_profile(instance)
# send activation email
if Site._meta.installed:
site = Site.objects.get_current()
else:
site = RequestSite(request)
new_profile.send_activation_email(site)
So whenever a User's email is changed, the user will be deactivated and an activation email will be send to the user.