Creating a custom signal for when a user activates his account - django

I am trying to create a custom signal for when the field auth_user.is_active becomes 1. I looked at Django's docs on signals, but was having trouble understanding how to implement custom signals.
When a user account becomes active, I want to execute the following function:
def new_user(sender, **kwargs)
profile = User.objects.get(id=user_id).get_profile()
return RecentActivity(content_object=profile, event_type=1, timestamp=datetime.datetime.now())
How would I do this. And also, what is the advantage of using signals over just doing the database insert directly? Thank you.

Here is what I did:
# in models.py
#receiver(pre_save, sender=User, dispatch_uid='get_active_user_once')
def new_user_activation_handler(sender, instance, **kwargs):
if instance.is_active and User.objects.filter(pk=instance.pk, is_active=False).exists():
profile = User.objects.get(pk=instance.pk).get_profile()
RecentActivity.objects.create(content_object=profile, event_type=1, timestamp=datetime.datetime.now())

If you want to do something when the field is changing, you can use the approach suggested by Josh, which is essentially to override the __init__ method.
Signals are generally used to communicate between apps. For example auth app sends user_logged_in signal. So if you want to do something when user is logging in, you just handle this signal, no need to patch the app.

Related

How to trigger allauth.account.signals.user_signed_up signal using pytest?

I have a OneToOneField between my UserTrust model and django.contrib.auth.models.User that I would like to create whenever a new User registers. I thought on creating the UserTrust instance using the user_signed_up signal.
I have the following code in my AppConfig
def ready(self):
# importing model classes
from .models import UserTrust
#receiver(user_signed_up)
def create_usertrust(sender, **kwargs):
u = kwargs['user']
UserTrust.objects.create(user=u)
... and this is in my pytest test
#pytest.fixture
def test_password():
return 'strong-test-pass'
#pytest.fixture
def create_user(db, django_user_model, test_password):
def make_user(**kwargs):
kwargs['password'] = test_password
if 'username' not in kwargs:
kwargs['username'] = str(uuid.uuid4())
return django_user_model.objects.create_user(**kwargs)
return make_user
#pytest.mark.django_db
def test_my_user(create_user):
user = create_user()
assert user.usertrust.available == 1000
Still, the test fails with
django.contrib.auth.models.User.usertrust.RelatedObjectDoesNotExist: User has no usertrust.
What did I miss?
The problem with you creating the user via the django_user_model is that it doesn't actually pass through the allauth code that actually sends that signal. You've got two options:
Use a client (since I'm assuming you're using pytest-django) and fill out a registration via the allauth provided link for registering new users. That way, a signal is sent, and you can assert attributes and model instances like that.
You can simply ignore the signal and unittest the function itself. That's it. You put your trust in that single registration view not changing at all and put your trust in the package that the API will not change. I can still recommend this option, but you've been warned.
You can send the signal yourself. Not recommended in case allauth's API changes, but you could just import the signal from allauth and send it like this: user_signed_up.send(sender=self.__class__, toppings=toppings, size=size) where user_signed_up is the signal. Ref the docs: https://docs.djangoproject.com/en/dev/topics/signals/#sending-signals
Again, definitely recommend the first one in case of API changes. I can also recommend the second option just because allauth is pretty reputable and you know what going to happen without too huge of an package change, but you never know.

Django signal to recognise which view called

I have a model with two different views.
The problem is that I have a signal registered to the model, and I want to change the behaviour of the signal based on what view called the object save method.
Is there a way doing so?
Simple answer: no.
Longer answer: well, technically, you could inspect the stack, but you really DONT want to go into such kind of dirty hacks. If you need specific "per view" behaviour, do it in the views (or in functions explicitely called by the views if you need to factor out this behaviour).
Also and FWIW, the point of signals is to allow for decoupling between apps - more specifically, to allow your app to hook into other apps (3rd part mostly) without having to touch those other apps code. Having signal handlers for models of your own app is an antipattern.
Built-in Signals are handled by django itself, so you can only achieve that by passing extra flag to the signal (if you don't want to inspect the caller):
def first_view(request):
#...
model_instance._through_view = 'first'
model_instance.save()
#...
def second_view(request):
#...
model_instance._through_view = 'second'
model_instance.save()
#...
and for your signal:
#receiver(pre_save, sender=MyModel)
def pre_save_handler(sender, instance, created, **kwargs):
through_view = getattr(instance, '_through_view' None)
if through_view == 'first':
...
elif through_view == 'second':
...

Creating user profile after they sign up

I'm using Django Allauth. Users can either sign up using Google, Twitter, Facebook or they can sign up using their email address. Once signed up, their details will be stored in the User table. There's also another model I have called Profile that contains user information like bio, avatar, etc. I'd like to create a Profile for the user when they sign up. I looked at Allauth signals and found the user_signed_up signal to be appropriate. Here's how I wrote the code in my handlers.py file:
#receiver(user_signed_up)
def create_profile(request, user):
profile = Profile(avatar='img/blah/blah.jpg', bio='Example text', gender='M', dob='2018-01-01',
country='US', user=user)
profile.save()
I added random stuff just so I can see if it's being created or not, but for some reason when the user signs up their profile is not being created. What am I doing wrong?
You have to be sure you're importing the handlers.py module somehow in order to signal handler gets registered, you can write
import handlers
anywhere in your code, but the recommended place is in the ready method of your app config class.
References:
https://chriskief.com/2014/02/28/django-1-7-signals-appconfig/
https://docs.djangoproject.com/en/2.1/ref/applications/
You need to write signal when user model instance save signal will work there you need to write one condition this instance is new created or old modify base on this condition you can create profile
#receiver(post_save, sender=User)
def user_updated(sender, created=False, **kwargs):
user = kwargs.get('instance', None)
if user and created:
##create profile object here

Get Django username to log signals

I am experimenting with logging in Django. I would like to log each occasion of users saving or modifying data. In my signal handler code, I have something like this:
#receiver(post_save)
def post_save_callback(sender, instance, created, raw, using, update_fields, **kwargs):
logger.info(
'Post-save called for %s. Created? %r. update_fields: %s',
sender, created, json.dumps(update_fields)
)
I would also like to log the username of the user performing the data manipulation, basically—the currently logged in user. How can I get this username?
Can't be done. The current user is only available via the request which is not available when using post_save.
To achieve what you are looking for you can do one of these:
you can override the save() method on that model.
You can use a middleware to store the current user(see this snippet wich can add user created_by and modified_by foreign key refs to any model automatically)
As described in documentation here signals are useful when many pieces of code may be interested in the same events.
Is it that case?
If the data manipulation is performed in admin panel you can consider to override log_change method of ModelAdmin object and you can use request.user.username to get the username of current logged in user.
def log_change(self, request, object, message):
logger.info('User %s changed user data %s. %s' %
(request.user.username, object, message)
super(MyClass, self).log_change(request, object, message)
Keep in mind that ModelAdmin already log changes in admin log, documentation here

Initializing after first superuser is created

I want to know if there are any specific signals sent when the first superuser is created. Am talking about when I run syncdb and am asked to create a super user. At that point I want to know if any signals are sent so that I can Use the signal to do some initialization. Or is there a way I can have a piece of code run only at that moment when the first super user is created. I want that piece of code to run only once at the beginning. help pls. I hope this question makes sense... of late I'v been getting criticism that my questions don't make sense. I hope this one does
def superuser_creation(sender, instance, created, **kwargs):
user = kwargs['instance']
if user.is_superuser:
//do something here
post_save.connect(superuser_creation, sender=User)
The creating of superuser is invoked by post_syncdb signal, you could hook to the post_syncdb signal as well and run the extra code afterwards.
Put the following code to the management/__init__.py of one of your app and make sure the path of that app is under the django.contrib.auth, in settings.INSTALLED_APPS.
from django.contrib.auth import models as auth_app
from django.db.models import signals
def operate_upon_first_superuser_after_syncdb(app, created_models, verbosity, db, **kwargs):
if auth_app.User in created_models and kwargs.get('interactive', True):
if auth_app.User.objects.filter(is_superuser=True).exists():
# ... extra code here
signals.post_syncdb.connect(operate_upon_first_superuser_after_syncdb,
sender=auth_app, dispatch_uid='operate_upon_first_superuser_after_syncdb')