My Django project lets users log in with a Google or Facebook profile, using python-social-auth's django integration.
Once they've logged in, is there a way to get a link to their profile on Google or Facebook? I'm trying to do something like this:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.core.mail import mail_admins
#receiver(post_save, sender=User)
def alert_new_user(sender, **kwargs):
instance = kwargs['instance']
social = instance.social_auth.last() # Assuming .last() gives me the one just added
if social is not None:
mail_admins('New user resistration',
'A new user registered with name {}'.format(instance.get_full_name()),
'<html><head/><body>A new user, {}, registered.</body></html>'.format(
???What goes here???, instance.get_full_name()
)
)
You can get a link to their Facebook profile before they are logged in.
In the pipeline, there is a function called social_details which get information about the user from specified social network.
If we take a look at that method, it is pretty simple:
def social_details(backend, details, response, *args, **kwargs):
return {'details': dict(backend.get_user_details(response),
**details)}
and if you print the response:
def social_details(backend, details, response, *args, **kwargs):
print(response)
return {'details': dict(backend.get_user_details(response),
**details)}
you will see that there is a parameter link, provided login is through Facbook. Now you can get that link and save/use it however you want.
Related
I am currently using python social auth to login users into my Django app and show a tutorial upon first user creation. I've tried this so far but request does not work in pre_save signals. Is there another way to do this?
#receiver(pre_save, sender=User)
def show_tutorial(sender, instance=None, **kwargs):
# For first time creation of user display tutorial
if instance._state.adding:
print ('USER CREATED')
request.session['first_login'] = True
EDIT: I tried the following code in my views but once logging in, the print statement never logged in the terminal.
def after_user_created(request, user, **kwargs):
user, created = User.objects.get_or_create(user=user)
if created:
request.session['first_login'] = True
print('FIRST LOGIN')
You cannot do it. You should not do it even if you could do it. Let's do the operation on "request" as much as possible on "view". like this.
def create_view(request):
user, created = User.objects.get_or_create(id=xxx)
if created:
request.session['first_login'] = True
UPDATE
Add a solution that assumes using "social-app-django".
① set settings.SOCIAL_AUTH_NEW_USER_REDIRECT_URL
NEW_USER_REDIRECT_URL=/new/user/view
http://python-social-auth.readthedocs.io/en/latest/configuration/settings.html?highlight=NEW_USER_REDIRECT_URL#urls-options
② Update session with new user view。
def new_user_view(request):
request.session['first_login'] = True
As per title, in Django, when and where i should store information about the user access? I'm already logging the user login/logout using Django built-in signals, but what if the user is already logged on that machine? How can i detect this?
I wanted to do the same thing on my Django project. The problem is that the last_login variable doesn't helps since it is updated at the user login, and If the user cache his session the variable will never be updated.
One solution is to create a decorator, and use it on your view's. In that way if the user uses your site, the variable will get updated.
This is an example of how to do it:
from functools import wraps
from django.contrib.auth.models import User
from django.utils import timezone
def W_update_user_login(func):
#wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
if request.user.is_authenticated():
user=User.objects.get(username=request.user)
user.last_login=timezone.now()
user.save()
return func(*args, **kwargs)
return wrapper
If you add the previous code in to your views.py file, you will be able to decorate your functions. ex:
#W_update_user_login
def your_view_function(request):
#
# your normal code..
#
return HttpResponse....
I'm trying to crate Django web project , and there are some User backend application where every URL ( view ) only for authenticated users.
I can write a condition in every view for example:
User/urls.py
urlpatterns = patterns('',
url(r'^settings/', 'User.views.user_settings'),
url(r'^profile/', 'User.views.profile'),
#Other User Admin panel Urls
)
User/views.py
def user_settings(request):
if request.user is None:
return redirect('/login')
#Some other code
def profile(request):
if request.user is None:
return redirect('/login')
#Some other code
As you can see it's not well coded but it works fine.
The only thing that I want to know , is it possible to add some condition for urls.py file all urls for not adding same code lines in every view function.
For example Symfony , Laravel and Yii frameworks have something like that what I want to do.
Is it possible do this in Django ? :)
Edited Here
With #login_required I need to add it for every view, I need something for all urls for example:
In Symfony framework I can write
{ path: ^/myPage, roles: AUTHENTICATED } and every url like this /myPage/someUrl will be asked for authentication.
I believe that Django have something like that :)
Thanks.
Well if you use a class based view, it makes easier for you to add #login_required. For example you can create a class based view here:
class BaseView(TemplateView):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(BaseView, self).dispatch(*args, **kwargs)
Now override it each time you want to create a new view. For example:
class SettingsView(BaseView):
def get(request):
return (...)
It will check each time at url dispatch if the user is logged in.
Also, if you use a class based view, you can override get() to check if the user is authenticated.
class BaseView(TemplateView):
template_name= None
role= None
def get(self, request, *args, **kwargs):
if request.user is not None and role is "AUTHENTICATE":
return super(BaseView, self).get(request, *args, **kwargs)
else:
raise Exception('user is not logged in')
urls.py:
url(r'^settings/', BaseView.as_view(template_name='/sometemplate', role="AUTHENTICATE")
You can use the #login_required decorator:
https://docs.djangoproject.com/en/1.5/topics/auth/default/#the-login-required-decorator
From the docs:
login_required() does the following:
If the user isn’t logged in, redirect to settings.LOGIN_URL, passing the current absolute path in the query string. Example: /accounts/login/?next=/polls/3/.
If the user is logged in, execute the view normally. The view code is free to assume the user is logged in.
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
You could use this : https://docs.djangoproject.com/en/1.6/topics/auth/default/#the-login-required-decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
First of all both methods below return True. I'd expect the second one to return False using the django standard admin authentication procedure or am I wrong?
def post_login(sender, **kwargs):
"""
Django 1.3 post login signal handler
"""
# do stuff
user = kwargs['user']
print user.is_authenticated()
user_logged_in.connect(post_login)
def post_logout(sender, **kwargs):
"""
Django 1.3 post logout signal handler
"""
# do stuff
user = kwargs['user']
print user.is_authenticated()
user_logged_out.connect(post_logout)
Anyway I'm trying to understand why django doesn't have a hook on authentication failure also.. I can use my own backend for users to login and out of their account, however I would like to hook onto the admin procedure as well to cover everything in one portion of code.. I found some topics but no real awnser how to fix this.
I came up with:
import settings
from django.dispatch import Signal
failed_login = Signal(providing_args=['user'])
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
class AuthSignalBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(username=username)
if user.check_password(password):
return user
else:
failed_login.send(sender=None, user=user)
except User.DoesNotExist:
return None
def login_handler(sender, **kwargs):
if settings.DEBUG:
print "failed login detected...!"
failed_login.connect(login_handler)
That works great, however there's no request in the ModelBackend, while the post_login and logout signals do have the request.. This is unfortunate because it would be great for IP logging
Any advise is welcome, I'm pretty sure people should have come across this one before..
If user is an instance of User model, user.is_authenticated() will always return True. Models instance can't know what's going on on request level. This method is for views.
If you want to deal with failed login attempts, take a look at django-axes. You can just use it, or look at the code and reimplement some ideas as you like.
In my Django app, I have a Newsletter model. Now I'd like to be able to send the newsletter (and even resend it) from Django Admin.
I could do this with a hook on the Model.save() method but is there another way that is not tied to the Model?
Thanks.
Admin actions allow you to easily hook up custom actions which can be performed on selected items from the admin's list pages.
If you are doing it from the admin then you'll need to override the save() method, but it can be the AdminModel save... doesn't need to be the full Model save.
However, if you are emailing a lot of emails, a better approach would be to install django-mailer which puts emails into a queue for later processing and then provides you with a new management command: send_mail.
So once you're ready to send the newsletter out you can manually run python manage.py send_mail. Any emails with errors will be moved to a deferred queue where you can retry sending them later.
You can automate this by running manage.py send_mail from cron.
If you really want to get fancy and do it from admin site, install django-chronograph and set up your send_mail schedule from there.
2022 Update:
django.__version__ == 4.1 and maybe ealier.
You can add a custom action at /admin/.../change by overriding django.contrib.admin.options.ModelAdmin._changeform_view:
from django.contrib import admin
class MyModelAdmin(admin.ModelAdmin):
def _changeform_view(self, request, object_id, form_url, extra_context):
if '<your-action>' in request:
# 1. check permissions
# 2. do your thing
print(request)
return super()._changeform_view(request, object_id, form_url, extra_context)
You can now add simple sumbit button to your form with your custom action:
<input type="submit" name="<your-action>" value="<button-title>">
Advantage
Zero javascript.
Authentication (CSRF) protected.
All stock admin actions will work with your form (add, save and continue, ...)
You can try this YouTube tutorial. Just change:
def available(modeladmin, request, queryset):
queryset.update(status='ava')
def not_available(modeladmin, request, queryset):
queryset.update(status='not')
to something like this:
def send(modeladmin, request, queryset):
for data in queryset:
subject=data.title
message=data.mesage
for d in Users.objects.filter(newsletter=True):
email=d.email
sendemail = EmailMessage(
subject, message + unsubscribe,
'emailo#mdjangotutsme.com',
[email], [],
headers = {'Reply-To': 'emailo#mdjangotutsme.com'}
)
sendemail.content_subtype = "html"
sendemail.send()
You can create custom django admin actions.
For example, I have Person model below:
# "store/models.py"
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
Now, I created uppercase() and lowercase() admin actions in Person admin as shown below. *actions = ("uppercase", "lowercase") is necessary to display uppercase() and lowercase() admin actions:
# "store/admin.py"
from django.contrib import admin, messages
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
actions = ("uppercase", "lowercase") # Necessary
#admin.action(description='Make selected persons uppercase')
def uppercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.upper()
obj.save()
messages.success(request, "Successfully made uppercase!")
#admin.action(description='Make selected persons lowercase')
def lowercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.lower()
obj.save()
messages.success(request, "Successfully made lowercase!")
Then, if I select 2 persons and Make selected persons uppercase then click on Go as shown below:
I can make 2 persons uppercase as shown below:
Then, if I select 2 persons and Make selected persons lowercase then click on Go as shown below:
I can make 2 persons lowercase as shown below:
In addition, if I remove #admin.action() as shown below
# "store/admin.py"
# ...
# #admin.action(description='Make selected persons uppercase')
def uppercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.upper()
obj.save()
messages.success(request, "Successfully made uppercase!")
# #admin.action(description='Make selected persons lowercase')
def lowercase(modeladmin, request, queryset):
for obj in queryset:
obj.name = obj.name.lower()
obj.save()
messages.success(request, "Successfully made lowercase!")
Actual function names are displayed as shown below: