Django: Get IP Address using a signal - python

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

Related

How to clean up Django login message from framework

There is a project setup with Django 1.6 and Django allauth. when user logged in django saves a login message to users session and its stack in there. While user reached to any page included messages framework, login message shows up with other message.
Because of this reason, I want to remove login message from message queue after user logged in.
I tried remove login message in django's and allauth's user_logged_in signal, but I discovered the message is not created there.
The example of the message removal code is below:
# from allauth.account.signals import user_logged_in
# First I tried allauth signal above.
from django.contrib.auth.signals import user_logged_in
#receiver(user_logged_in)
def user_logged_in_(request, **kwargs):
storage = messages.get_messages(request)
storage.used = True
Edit: The workaround below is working. But I feel it is not right way to do.
After this, I decided to make a workaround. After user logged in, user redirected to index view. I removed signal and append storage.used = True method in index view. Also It is not worked too.
def clear_messages(request):
storage = messages.get_messages(request)
storage.used = True
def index(request):
clear_messages(request)
return render_to_response('website/index.html', {}, context_instance=RequestContext(request, {}))
From django-allauth's documentation:
All messages (as in django.contrib.messages) are configurable by
overriding their respective template. If you want to disable a message
simply override the message template with a blank one.

Django Test : SESSION_KEY failing assertIn

I am using the Django Test client (django.test.Client) to run view tests. Upon attempting to use the Test Client on my index function that handles post requests for logins, it continually fails the test even though the authentication successfully occurs.
Heres my test:
def test_login(self):
response = self.client.post('/login/', {'username':'user', 'password':'pass'})
print response.content
self.assertIn(SESSION_KEY, self.client.session)
So the reason i know the login process successfully works is because response.content yields HTML data from another view that can only be access if request.user.is_authenticated() is true. In other words, they must be logged in for response.content to yield the "logged in page". So given this, i can tell that the function obviously works for its practical purpose of logging the user in, however, i've been scouring the docs for hours trying to figure out why i can't access SESSION_KEY from the client session. All my reading suggests that the django test client is in fact stateful in nature and should store the session.
Can someone shed some light on this?
Ok after much searching and asking around on #django, i made a working solution for Django 1.6.x
from django.contrib.auth import SESSION_KEY, get_user_model
from django.test import Client
def setUp(self):
self.client = Client()
def test_login_view(self):
user_pk = get_user_model()._default_manager.get(username__exact='test_username_here').pk
response = self.client.post('/login/', {'username':'test_username_here', 'password':'test_password_here'})
self.assertEqual(self.client.session[SESSION_KEY], user_pk)
The test_login_view function will be the one evaluating the view in my app that handles user logins from the template form. First, i grab user_pk which is the real primary key of the given user in the database. I used get_user_model() instead of User.objects.get() because the former allows you to reference regardless of whether the User model is modified or not. Of course you can use the latter as well. Second, i go ahead and send the post request using the test client just like a standard user's browser would. Finally, i discovered that self.client.session[SESSION_KEY] contains the primary key of the logged in user. (If the login was successful, otherwise, it will simply yield a KeyError)

Is Django post_save triggered before/after saving instance to database?

I have a website using Django. Each post is an object called Article. I want to retrieve the post's HTML after saving it so I wrote the following post_save hook:
#receiver(models.signals.post_save, sender=Article)
def _send_article_mentions(sender, instance, **kwargs):
import requests
from django.contrib.sites.models import Site
from urlparse import urljoin
from ParallelTransport.settings import ARTICLES_URL
SITE_URL = 'http://'+Site.objects.get_current().domain
article_url = urljoin( SITE_URL, instance.get_absolute_url() )
import time
time.sleep(20)
r = requests.get(article_url)
error_file = open(ARTICLES_URL+'/'+'error.txt','w')
error_file.write('file started1\n')
m = r.status_code
error_file.write(str(m))
error_file.close()
It basically waits for 20s (added as a test) then tries to retrieve the HTML of the post using its URL, and writes the request status code to a file for debugging.
The problem is I always get status = 404 on the first save, it does work on 2nd and subsequent saves. I thought the way Django works would be, in order:
save instance to database using save(). At this point the post would get a URL
send post_save signal
But then I should be able to retrieve the HTML in post_save. Am I understanding post_save incorrectly?
Added notes:
Putting this code in save() method does not work. Nor should it. The post is added to database at the end of the save() method and so should not have any URL until save() ends.
This is on a production site, not on the development server.
I want to use the links in the HTML to send 'pingbacks' or actually webmention. But all my pingbacks are being rejected because the post does not have a URL yet. This is the bare minimum code that does not work.
Although this is a completely wrong approach(*), the problem is probably in database transactions. Current thread saves the article but within this uncommited transaction you are trying to get these data through another thread (through web server). In that case, this behaviour is fully correct. Either you need to commit before retrieving through another thread or get the HTML by another way.
(*) should be done asynchronously on the background (Celery or other more lightweight async queue app) or you can call the view directly if you want to get the HTML (depending on your view, you may have to forge the request; if too complicated, you can create a helper function that cherrypicks minimal code to render the template). If you only need to call a 3rd party API after you save something, you want to do it asynchronously. If you don't do it, the success of your "save() code" will depend on the availability of your connection or the 3rd party service and you will need to deal with transactions on place where you won't to deal with transactions ;)
Have you tried overriding the object's save method, calling super, waiting and then trying to retrieve the HTML?
Also are you using the development server? It may have issues handling the second request while the first one is still going. Maybe try it on a proper server?
I had similar problem caused probably by the same issue (Asked differently, https://plus.google.com/u/0/106729891586898564412/posts/Aoq3X1g4MvX).
I did not solve it in a proper way, but you can try playing with the database cache, or (seen it in another django database problem) close all of the database connections and requery.
Edit 2:
I have created a simple example (using Django 1.5.5) to test whether this works as intended. As far as I can tell, it does. pre_save fires before a database commit and post_save fires after.
Example detailed:
Two example models.
Article is used for triggering signals.
ArticleUrl is used to log responses from Article.get_absolute_url().
# models.py
from django.db import models
from django.core.urlresolvers import reverse
class Article(models.Model):
name = models.CharField(max_length=150)
def get_absolute_url(self):
"""
Either return the actual url or a string containing '404' if reverse
fails (Article is not saved).
"""
try:
return reverse('article:article-detail', kwargs={'pk': self.pk})
except:
return '404'
class ArticleUrl(models.Model):
article_name = models.CharField(max_length=150)
url = models.CharField(max_length=300)
def __unicode__(self):
return self.article_name + ': ' + self.url
Example views.py and urls.py were omitted as they are simple. I can add them if needed.
# views.py, url.py
Creating pre_save and post_save signals for Article:
# signals.py
from django.db.models import signals
from django.dispatch import receiver
from article.models import Article, ArticleUrl
#receiver(signals.pre_save, sender=Article)
def _log_url_pre(sender, instance, **kwargs):
article = instance
article_url = ArticleUrl(
article_name = 'pre ' + article.name,
url = article.get_absolute_url()
)
article_url.save()
#receiver(signals.post_save, sender=Article)
def _log_url_post(sender, instance, **kwargs):
article = instance
article_url = ArticleUrl(
article_name = 'post ' + article.name,
url = article.get_absolute_url()
)
article_url.save()
Importing my signals.py so Django can use it:
# __init__.py
import signals
After defining the above I went ahead and created a new Article in Django shell (python.exe manage.py shell).
>>> from article.models import *
>>> a = Article(name='abcdd')
>>> a.save()
>>> ArticleUrl.objects.all()
[<ArticleUrl: pre abcdd: 404>, <ArticleUrl: post abcdd: /article/article/8>]
The above example does seem to show that pre_save did indeed not return a url, but post_save did. Both appear to be behaving as intended.
You should check whether your code either deviates from the above example or interferes with program execution in some way.
Edit 1:
Having said that (below), according to What Happens When You Save, the post_save signal should run after a database save.
Could there be other parts of your app/site somehow interfering with this?
Original post:
According to the Django documentation, the post_save signal is sent at the end of save(), not after it.
As far as I understand, Django Signals are synchronous (in-process) so they would stall the actual save(). It won't fully complete until the signals are done.
This isn't always applicable, but have you considered a custom signal you could call after save()?

How to confirm user created by create_user in flask security mongoengine app?

I have a python flask app using mongoengine and flask-security built up from the examples to expose all of the confirmation, registration, tracking, and recovery functionality.
Everything works except that a user created imperatively in the code using:
MongoEngineUserDatastore.create_user(...)
cannot login. That is, when you try to login with this user, you get an error message:
"Email requires confirmation"
Since the email with a hashed URL has not been sent, there is no way to confirm. Is there a parameter I can pass somewhere to confirm this user on creation or set the confirmed flag somewhere?
Here's my code:
I figured it out by confirming a newly registered user and examining mongodb to see what fields were added. Turns out that the required field for confirmation is confirmed_at, which must have a datetime so:
import datetime
# Create a user to test with
#app.before_first_request
def create_user():
user_datastore.create_user(
email='me#mydomain.com',
password=utils.encrypt_password('password'),
confirmed_at=datetime.datetime.now())
I've updated the gist here:
https://gist.github.com/davidthewatson/327776905ef30815c138
When you create your test user you need to make them active eg:
#app.before_first_request
def create_user():
user_datastore.create_user(
email='me#mydomain.com',
password=utils.encrypt_password('password'),
active=True)

Sending emails when the email address is changed

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

Categories