Sending emails when the email address is changed - python

In my DJango application I'd like to implement a system so that when the email field in my jmodel, MyModel is changed, an email is sent to that email address. I have a ModelForm on this Model of mine. How and how should I implement this?
Should I override the save() method of my ModelForm and send the email there?
Should I try and try an trap some kind of model updated signal and send the email there — if so, how? What signal shoudl I be trapping?
What is a good way to check that the email was changed. This itself is a trivial thing to implement but I'd like the code to reside it it's rightful place.
Thanks
Checking if a field was changed: Django: When saving, how can you check if a field has changed?

You can use django-model-changes to do this without an additional database lookup:
from django.db import models
from django.dispatch import receiver
from django_model_changes import ChangesMixin
class MyModel(ChangesMixin, models.Model):
# your model
#receiver(pre_save, sender=MyModel)
def send_email_if_changed(sender, instance, **kwargs):
if 'email' in instance.changes():
# send email

I wouldn't follow the advice of the referenced SO question. It got 18 upvotes but it would appear upvotes aren't everything ;). Actually, it's probably just dated info (2009).
It's far better to use a pre_save signal to do this, which requires absolutely zero changes to your model and therefore doesn't have any negative consequences such as the answer your referenced has.
Essentially, in your receiver method, you look up the instance object from the database. Since this is pre-save, the database hasn't been changed yet. As a result, you can then compare instance.some_field with obj.some_field and see if they're different.
#receiver(pre_save, sender=MyModel)
def send_email_if_changed(sender, instance, **kwargs):
try:
obj = MyModel.objects.get(pk=instance.pk)
except MyModel.DoesNotExist:
pass # It's new, so email hasn't technically changed, but you might want to do something else here.
else:
if not obj.email == instance.email: # Email has changed
# send email

Related

Triggering action on adding object from admin panel

I have a model that holds emails of users who signed up for emails that notify about new articles on the site. Articles are added from the admin panel. I want to figure out how to trigger some function on object addition by admin.
MailingList model
class MailingList(models.Model):
email = models.CharField(max_length=50)
def __str__(self):
return 'email: ' + str(self.email)
Thank you!
Depending on your needs you can use or Django signals to do something after new article is added (post_save signal, for example). Or, if you'd like to do something exactly when new article is saved only from admin, you can override ModelAdmin.save_model.

Django - How to differentiate between users in the database?

I'm building a Django server for my company and I'm still unfamiliar with some processes. I'm sure this is super simple, I'm just completely unaware of how this works.
How do I differentiate between user's data so it doesn't get mixed up?
If Jill is a user and she requests a page of her profile data, how do I not send her Jack's profile data, especially if there are multiple models invovled?
For example, the code in the view would look like this:
def display_profile(request)
profile = Profile.objects.get(???) # What do I put in here?
I understand that I can do:
def display_profile(request, user)
profile = Profile.objects.get(user_id=user)
But that's not my design intention.
Thank you in advance.
As documented
Django uses sessions and middleware to hook the authentication system into request objects.
These provide a request.user attribute on every request which
represents the current user. If the current user has not logged in,
this attribute will be set to an instance of AnonymousUser, otherwise
it will be an instance of User.
So in your case (notice field not being called user_id )
profile = Profile.objects.get(user=user)
In your Django view, you can access the current user with request.user.
So if you want to get a Profile instance matching your current logged in user, just do a query as follow:
profile = Profile.objects.get(user=request.user)
This assumes you have a user foreign key field (or OneToOne) in your Profile model.

Django not REST - API user registration

I have my first project as junior in my work. It is old (Django 1.8) and it is normal django framework... not REST.
It supports web, and mobile.
I have to create endpoint for mobile to create user.
I think it is not a problem (to create) but I want to make sure it will be save.
First of all I thought that I will create normal ModelForm (RegisterAPIForm based on model=User with full validation (I mean init all fields that are in "backend" not visible for user | cleaned_data for all fields | special overwriten method save() that in addition hashes password, and send email) and in Views I'll add something like this:
class RegistrationAPITestView(View):
def post(self, request):
form = RegistrationAPIForm(
request.POST
)
if form.is_valid():
form.save()
return JsonResponse({})
else:
#check errors and send error code back
Or I should do it other way, by using User object?
class RegistrationAPITestView(View):
def post(self, request):
#check if user does not exist
#password1 and password2 validation
user = User.objects.create()
user.name = request.POST['username']
user.set_password(request.POST['password'])
#init all fields that user can't choose like groups etc
user.save()
What do you think? Do I need ModelForm that I won't even render? It seems to be safer, but maybe I should check it other way? with User object?
Btw. Registration form already exists for web but there is a lot of "web" stuff that I don't need and I don't have to check and there is another method of saving password, so I believe I should create new one.
My code will be revieved in 2 weeks (senior vacations) but now I'm alone and want to do my best.
There is nothing wrong with the second option, but here is the problem that you as a junior should avoid. This line will make the server return a 500 error request.POST['username'] because python will throw a key error if the user doesn't provide the username, to fix just change to request.POST.get('username', 'value if doesn\'t exit') also make sure that everything is ready before create the user or you will have records in the database that wont be useful. Call validators to the password too and try to cover all possible scenario. Remember never trust the user

What's the cleanest way to add arbitrary data to ModelForm?

Imagine we're developing a message system, and each Message has a foreign key for sender.
We're using ModelForms, and there is a MessageForm that infers its fields from Message.
Of course, we don't want the user to be able to spoof sender by posting a different sender ID.
Therefore, we must exclude sender from ModelForm and fill it from session on post.
Where and how should I assign arbitrary data to ModelForm fields?
In my example, I probably want to access session so we need to access to request as well.
Does this mean the code has to be in the view, right after the form has been created?
How do we assign a form field from code and make sure it overrides POST data?
(Of course, the example is pretty fictional and is here just to illustrate the problem.)
You can just exclude it as you have and then when processing the form in the view do:
obj = form.save(commit=False)
obj.sender = request.user
obj.save()
Alternatively, you can do it in the save method of the form; for this you need to save the request object momentarily in the form. Something like the following:
class MyForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
self._request = request
super(MyForm, self).__init__(*args, **kwargs)
def save(self, commit=False):
obj = super(MyForm, self).save(commit=False)
obj.sender = self._request.user
if commit:
obj.save()
return obj
I prefer the second way myself as it helps encapsulate the logic regarding that model and it's data in one neat place.
Just exclude the sender field from the ModelForm and, when you instantiate the object in the view on the POST endpoint (just before saving), make sure you populate the sender field with the appropriate session or user ID.
When you exclude the field, there is no way to add the field to the post data[1], so a user can't spoof it.
[1] With JavaScript, a &sender=someSenderId could be added in theory, but in your view, you don't need to look for a sender field. It won't be serialized into the ModelForm object.

Django: Get IP Address using a signal

I am trying to log the IP address of the user that is trying to login using signals. How do I do this?
I have already captured the datetime for the login.
#models.py
class UserLogin(models.Model):
"""user details when logging in"""
user = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now=True)
This is for the signal:
#models.py
def user_login_save(sender, instance, **kwargs):
if instance.last_login:
old = instance.__class__.objects.get(pk=instance.pk)
if instance.last_login != old.last_login:
instance.userlogin_set.create(timestamp=instance.last_login)
models.signals.post_save.connect(user_login_save, sender=User)
Although I know how to get the IP Address using: request.META[REMOTE_ADDR] my problem is that I cannot use the request instance in my model. I am not also sure if getting something from the request is good practice.
What is the recommended way of doing this?
Any reply will be greatly appreciated.
Regards,
Wenbert
Although I know how to get the IP Address using: request.META[REMOTE_ADDR] my problem is that I cannot use the request instance in my model.
That's why you have view functions.
I am not also sure if getting something from the request is good practice.
It is perfectly fine.
That's why the request is provided to every view function.
That's why you have view functions.
Just do it in the view function. Don't mess with signals unless you're writing a new kind of database interface.
As the instance is passed into the signal and the instance is really the same object that is saved, you can attach a request object or IP to the instance upon saving it.
user_login.request=request
user_login.save()
And retrieve it in the signal like
instance.request
Django has a signal that fires whenever a user logs in that is called user_logged_in. By attaching a signal handler to this signal you can be notified of login events. The handler is passed the user and request objects. As you note, you can get the IP address from the request object.
This is a very old question, but I had the same problem today and this was the first hit at Google. So maybe this helps other people as well. This is how it works for me.
from django.contrib.auth.signals import (
user_logged_in,
user_logged_out,
user_login_failed,
)
from django.dispatch import receiver
import logging
user_logger = logging.getLogger("user")
#receiver(user_logged_in)
def log_user_login(sender, user, **kwargs):
"""log user "login" to log-file setup in django settings.py"""
# if the IP-ADRESS is 127.0.0.1 from external users then check this
# https://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
request = kwargs["request"]
ip_address = request.META.get("REMOTE_ADDR")
user_logger.info(f"{user} login successful. IP-Address: {ip_address}")
# other functions deleted for brevity

Categories