Can we get the request object data in signal - python

I am registering the data in user model and also want to save the profile data same time like first_name and last_name in profile model.
So I have used django signals to store the profile information and send the mail to user.
But we are unable to get the first_name and last_name in signal file:
#---------------------------- Create profile at the time of registration --------------------------#
def register_profile(sender, **kwargs):
if kwargs.get('created'):
user = kwargs.get('instance')
request = kwargs.get("request")
if user.id is not None and user._disable_signals is not True:
profile = Profile(user=user)
if user.status is not 1:
#------------------- Send the registration mail to user and it have confirmation link ----------#
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
activation_key = hashlib.sha1(salt+user.email).hexdigest()
key_expires = datetime.datetime.today() + datetime.timedelta(2)
#print user
profile.activation_key = activation_key
profile.key_expires = key_expires
#--------------------- End -------------------------------------------------------------#
profile.save()
if user.status is not 1:
user = model_to_dict(user)
BaseSendMail.delay(user,type='account_confirmation',key = activation_key)
return
post_save.connect(register_profile, sender=User, dispatch_uid='register_profile')
#-------------------------- End ---------------------------------------------------------------------#
In above code I am unable to get first_name and last_name data which is sent at the time of registration.Also I would like to mention that first_name and last_name fields belong to profile model.

No, and you shouldn't try. Signals could be executed from anywhere: a management script, a Celery task, all sorts of places that might not have a request.
You could store the data on the User instance temporarily, as you do with the _disable_signals attribute. However I suspect that this is not really best done in a signal; since you're saving the result of a form submission, and it depends on the data in that form, you should really do that in the view or the form itself.

I did this and it worked.
Not sure about it's impact on performance etc.
some_file.py:
data = {}
middleware.py:
class MyMiddleware(object):
def process_request(self):
from path.to.some_file import data
data['request'] = self.request
signals / model_method / manager / template tag / any where else:
from path.to.some_file import data
request = data.get('request')

Related

How to queue requests in Django?

I manage a physical locker with Django (DRF). Users fill out a form, authenticate via link sent to their e-mail, authorize via a pin displayed on the locker.
My view should handle three cases:
If user authenticates and authorizes successfully, pin displayed on
the locker is replaced with a generic message and the locker opens.
(Already implemented)
If the user fails to authorize within 3 minutes, locker pin is replaced with a generic message.
If a new authorization request is made by user Foo, while authorization for user Bar is still incomplete, stack the request in
a queue and wait for case 1. or case 2. to complete.
How can I:
Implement a request queue, such that the pin displayed on the locker does not get overridden/replaced when a new request comes in?
How can I wait 3 minutes for authorization to be completed before processing the next request?
View as is, in case it is useful:
if request.method == 'POST':
form = ConfirmationForm(request.POST)
if form.is_valid():
if pin == form.cleaned_data['pin']:
open_bay(jwt_token=jwt[1], pin=pin)
display_generic_message(jwt_token=jwt[1])
lock_bay(jwt_token=jwt[1], pin=pin)
return render(request, 'static/pages/request-success.html')
else:
pass
else:
form = ConfirmationForm()
return render(request, 'static/pages/confirmation.html', {'form': form})
in your model which u save the authorization u need to add a field of date_created or date_requested and must be a datetime field . then on each request you can check if 3 minutes has passed from date_requested which you saved .
you also need an is_authorized field to check if your user is authorized or not.
we assume that you are getting the user from email you sent .
user = get_object_or_404(User,email=kwargs['email'])
if user.date_request + timedelta(minutes=3) > datetime.datetime.now():
"do your authorzing stuff ..."
else:
return HttpResponse("you need to wait 3 minutes to request again")
You would need to store when the last authorization started, clear that timestamp if the user puts in the pin.
# models.py
class LockerUserQueue(models.Model):
user = models.ForeignKey(get_user_model())
locker = models.ForeignKey("yourapp.Locker")
created_at = models.DateTimeField(index=True, auto_now_add=True)
class Locker(models.Model):
last_authorization = models.DateTimeField(null=True, blank=True)
user_queue = models.ManyToManyField(through=LockerUserQueue)
class BadPin(Exception):
pass
def enqueue_user(self, user):
self.user_queue.add(user)
def process_authorization(self, user):
# do authorization for user
def process_pin(self, user, pin):
self.last_authorization = None
if validate_pin(user, pin):
# pin OK logic
else:
raise self.BadPin
# views.py
def authorize_user(request, locker_id):
locker = get_object_or_404(pk=locker_id)
locker.enqueue_user(request.user)
locker.save()
return render(request, "authorization_started.html")
def open_bay_with_pin(request, locker_id):
locker = get_object_or_404(pk=locker_id)
pin = get_pin_from_request(request)
try:
# No matter if the pin is correct, the locker.last_authorization is cleared
locker.process_pin(user, pin)
except locker.BadPin:
return render(request, "bad_pin.html")
finally:
locker.save()
return render(request, "good_pin.html")
# management/commands/process_queue.py
# you would run this by
# $ python manage.py process_queue
class Command(BaseCommand):
#transaction.atomic
def process_queue(self):
# you probably want to put the 3 min delay in your settings.py
# so you don't end up with a magic value here
for locker in LockerUserQueue.objects.filter(
Q(locker__last_authorization__isnull=True)|Q(locker__last_authorization__gt=now() - timedelta(minutes=3))
).order_by("created_at").values_list("locker", flat=True).distinct():
locker.process_authorization()
def handle(self):
while True:
self.process_queue()
# you don't want to keep querying the DB when there's nothing in the queue
sleep(5)
The code above shows how you would do it using the database as the queue, in a low volume use case this should be just fine. If the volume is high, I would store the locker last_authorization in faster storage, like redis, the queue can be maintained in redis as well. But the logic behind would be the same.

Django and one-time links with data

I think I have a simple case here but I'm not finding good examples of the implementation ( or probably failing to understand).
After the user (not logged in) types his username to a form, Django would generate a unique URL based of this data (encoded in URL?) for the user that can be accessed once and within 5 minutes. Based on that URL (after clicking it) the data (username) would be decoded and ready for use in this one-time view.
Simple scenario if needed: user nimda fills the form and then is redirected (for example) to a view that shows the generated URL. Then nimda clicks the generated URL and a view is shown with the data he or she typed into the form
If you don't need that url you could save data to the session and send the user to a specific url.
The view connected to the url generates content depending on the (anonymous) users session. The user can see the content as long as you sessions last or you implement a time stamp an check this before delivering content.
If you need the url:
Build a model connected with the sessions with url and a time stamp.
Configure the urls.py for the url-model like
url(r'^dataUrl/(?P[0-9]+)/$', PostDelete.as_view()),
Assign the user session and the entered data (saved to the session) with
the url-model.
When delivering the content check for the random-url-part, and the timestamp and deliver the date (or not ;) )
You can access the session in a cvb's like this:
class YourClassName(TemplateView):
template_name = ""
def get_context_data(self, **kwargs):
context = super(YourClassName , self).get_context_data(**kwargs)
DataYouNeed = self.request.session["SessionVariableOfTheUser"]
userDAta = self.request.user #if this is usefull `
or in a createView:
class URLCreate(CreateView):
model = randomUrl
template_name = "entryCreate.html"
success_url = "../xyz/"
form_class = UrlCreateForm
# if you like to change the success-url
def get_success_url(self):
#print dir(self.object.instance)
#print self.object.instance.id
url = "../bringMeTo/%s" % self.object.instance.id
return url
def form_valid(self,form):
form.instance.user = self.request.user
self.request.session["formData"]= form.instance
return super(URLCreate, self).form_valid(form)
pass
This is not a ready solution! Just an inspiration for a start.

django-allauth: Check whether user signed up using a social account

So I have integrated django-allauth in my app, and now users have the ability to log in via instagram. Let's say I have a model called UserProfile and it has a field
user_avatar = models.ImageField(upload_to='profile_images', blank=True, default=None)
And with that I have a signal that creates a user profile as soon as a new user registers:
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
So usually when the user registers the user_avatar is blank since the default is set as None, now I want to add in the signal(if that's the correct way of doing it), to check if the user created his account via signing in using instagram, to go and fetch his profile picture and use it in the user_avatar. I think it's possible https://instagram.com/developer/endpoints/users/, but since I am a complete noob in python and django I don't know how to exactly do it.
So I found this signal from the django-allauth docs allauth.socialaccount.signals.pre_social_login(request, social_login) so this states that I can check that the user has signed up using a social account, but how would I use it with my create_user_profile function? The steps that I thought of is to first create the profile which I did and then to check whether the user signed up using a social account or not, if they did then the user_avatar which use their instagram profile picture and if not it would stay as none.
And as a plus I know that I can fetch the users social account profile picture in a template using {{user.socialaccount_set.all.0.get_avatar_url}}, but I don't want to do it via templates rather than doing it via Models which is the best way.
This might look really stupid but I gave it a go and tried to come up with something (this is what a newbie thinks would work, I thought this on top of my head, as I have no idea if this how signals work)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
def pre_social_login(request, social_login):
user_logged_social = social_login.account.user
if user_logged_social:
UserProfile.objects.get(user_avatar=user_logged_social.profile_picture)
else:
pass
post_save.connect(create_user_profile, sender=User)
UPDATE
Got it working with the help of #bellum! Thank you!
Here is the code that I used:
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name="profile")
user_avatar = models.ImageField(upload_to='profile_images'
blank=True,
default=None)
def __unicode__(self):
return self.user.username
class Meta:
verbose_name_plural = "User Profiles"
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
3utils.py
def download_file_from_url(url):
# Stream the image from the url
try:
request = requests.get(url, stream=True)
except requests.exceptions.RequestException as e:
# TODO: log error here
return None
if request.status_code != requests.codes.ok:
# TODO: log error here
return None
# Create a temporary file
lf = tempfile.NamedTemporaryFile()
# Read the streamed image in sections
for block in request.iter_content(1024 * 8):
# If no more file then stop
if not block:
break
# Write image block to temporary file
lf.write(block)
return files.File(lf)
class SocialAccountAdapter(DefaultSocialAccountAdapter):
def save_user(self, request, sociallogin, form=None):
user = super(SocialAccountAdapter, self).save_user(request, sociallogin, form)
url = sociallogin.account.get_avatar_url()
avatar = download_file_from_url(url)
if avatar:
profile = user.profile # access your profile from user by correct name
profile.user_avatar.save('avatar%d.jpg' % user.pk, avatar)
return user
settings.py
SOCIALACCOUNT_ADAPTER = 'main.s3utils.SocialAccountAdapter'
The signal for creating the profile on sign up in my models was left the same, just added an SocialAccountAdapter!
I have done the same task for Facebook provider. allauth gives possibility to achive this in another way. I think you don't need to get avatar every time user logins in your system. If yes then you can override class like this:
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
class SocialAccountAdapter(DefaultSocialAccountAdapter):
def save_user(self, request, sociallogin, form=None):
user = super(SocialAccountAdapter, self).save_user(request, sociallogin, form)
url = sociallogin.account.get_avatar_url()
avatar = download_file_from_url(url) # here you should download file from provided url, the code is below
if avatar:
profile = user.user_profile # access your profile from user by correct name
profile.user_avatar.save('avatar%d.jpg' % user.pk, avatar)
return user
You should add this line to your config: SOCIALACCOUNT_ADAPTER = 'path-to-your-adapter.SocialAccountAdapter'.
As result this code will be called only during new socialaccount registration process, fetch avatar url, download it and save in your User model.
import requests
import tempfile
from django.core import files
def download_file_from_url(url):
# Stream the image from the url
try:
request = requests.get(url, stream=True)
except requests.exceptions.RequestException as e:
# TODO: log error here
return None
if request.status_code != requests.codes.ok:
# TODO: log error here
return None
# Create a temporary file
lf = tempfile.NamedTemporaryFile()
# Read the streamed image in sections
for block in request.iter_content(1024 * 8):
# If no more file then stop
if not block:
break
# Write image block to temporary file
lf.write(block)
return files.File(lf)

Google App Engine with Python: Unable to update the data entity

I have read, read, and read again the documentation and do many search on the Web but I don't understand yet why my app doesn't work correctly.
When a user connects, he have to complete a form. The code for this works. When the user logs out, and then, logs in, the form fields are filled with his information. So, the data is correctly saved. But, when the user changes the form fields values and submits the form, data are not updated.
My model:
class Members(db.Model):
account = db.UserProperty()
hashtags = db.StringProperty()
Here the class to submit the form:
class Submit(webapp.RequestHandler):
def post(self):
user = users.get_current_user()
if user:
url = users.create_logout_url('/')
url_linktext = 'Logout'
member = db.GqlQuery("SELECT * FROM Members WHERE account = :1", user)
if member.count(1) > 0:
m = Members(account=user)
m.hashtags = ','.join([
self.request.get('hashtag1'),
self.request.get('hashtag2'),
self.request.get('hashtag3')])
m.put()
else:
member = Members()
member.account = user
member.hashtags = ','.join([
self.request.get('hashtag1'),
self.request.get('hashtag2'),
self.request.get('hashtag3')])
member.put()
self.redirect('/')
else:
self.redirect('/')
The problem is you are adding a new record instead of updating the existing record. For your code, the simplest fix would be to change this:
if member.count(1) > 0:
m = Members(account=user)
to:
if member.count(1) > 0:
m = member[0]
The reason your code is updating the existing record is because you have not assigned a key. To learn more about keys, you can read about them in Kinds and Identifiers.

Get first name from Facebook with django-socialregistration

With django-socialregistration what should I do to get the first name from Facebook at the moment of the Facebook Connect?
I tried to put these lines in django-socialregistration/views.py:
graph = request.facebook.graph
fb_profile = graph.get_object("me")
user.first_name = fb_profile['first_name']
user.save()
in the method post(self, request) after user = profile.authenticate() but I get this error when I try to connect:
int() argument must be a string or a number, not 'AnonymousUser'
Why? The error occurs at the first line graph = request.facebook.graph
The code of the django-socialregistration view:
class Setup(SocialRegistration, View):
"""
Setup view to create new Django users from third party APIs.
"""
template_name = 'socialregistration/setup.html'
def get_form(self):
"""
Return the form to be used. The return form is controlled
with ``SOCIALREGISTRATION_SETUP_FORM``.
"""
return self.import_attribute(FORM_CLASS)
def get_username_function(self):
"""
Return a function that can generate a username. The function
is controlled with ``SOCIALREGISTRATION_GENERATE_USERNAME_FUNCTION``.
"""
return self.import_attribute(USERNAME_FUNCTION)
def get_initial_data(self, request, user, profile, client):
"""
Return initial data for the setup form. The function can be
controlled with ``SOCIALREGISTRATION_INITIAL_DATA_FUNCTION``.
:param request: The current request object
:param user: The unsaved user object
:param profile: The unsaved profile object
:param client: The API client
"""
if INITAL_DATA_FUNCTION:
func = self.import_attribute(INITAL_DATA_FUNCTION)
return func(request, user, profile, client)
return {}
def generate_username_and_redirect(self, request, user, profile, client):
"""
Generate a username and then redirect the user to the correct place.
This method is called when ``SOCIALREGISTRATION_GENERATE_USERNAME``
is set.
:param request: The current request object
:param user: The unsaved user object
:param profile: The unsaved profile object
:param client: The API client
"""
func = self.get_username_function()
user.username = func(user, profile, client)
user.save()
profile.user = user
profile.save()
user = profile.authenticate()
self.send_connect_signal(request, user, profile, client)
self.login(request, user)
self.send_login_signal(request, user, profile, client)
self.delete_session_data(request)
return HttpResponseRedirect(self.get_next(request))
def get(self, request):
"""
When signing a new user up - either display a setup form, or
generate the username automatically.
"""
# I want some validation here, hacked up in the generic callback
try:
urlfrom = request.session['urlfrom']
match = resolve(urlfrom)
username, code = match.args
checkcode, referrer, ticket = utils.register_validate(username, code)
except:
return http.HttpResponseServerError()
# validation end
try:
user, profile, client = self.get_session_data(request)
except KeyError:
return self.render_to_response(dict(
error=_("Social profile is missing from your session.")))
if GENERATE_USERNAME:
return self.generate_username_and_redirect(request, user, profile, client)
form = self.get_form()(initial=self.get_initial_data(request, user, profile, client))
return self.render_to_response(dict(form=form))
def post(self, request):
"""
Save the user and profile, login and send the right signals.
"""
try:
user, profile, client = self.get_session_data(request)
except KeyError:
return self.render_to_response(dict(
error=_("A social profile is missing from your session.")))
form = self.get_form()(request.POST, request.FILES,
initial=self.get_initial_data(request, user, profile, client))
if not form.is_valid():
return self.render_to_response(dict(form=form))
user, profile = form.save(request, user, profile, client)
# validation count up referrals, tickets, etc.
try:
urlfrom = request.session['urlfrom']
match = resolve(urlfrom)
username, code = match.args
checkcode, referrer, ticket = utils.register_validate(username, code)
except:
return http.HttpResponseServerError()
utils.register_accounting(checkcode, referrer, ticket, user)
user = profile.authenticate()
self.send_connect_signal(request, user, profile, client)
self.login(request, user)
self.send_login_signal(request, user, profile, client)
self.delete_session_data(request)
# added by me
graph = request.facebook.graph
fb_profile = graph.get_object("me")
user.first_name = fb_profile['first_name']
user.save()
#
return HttpResponseRedirect(self.get_next(request))
let me take a stab at it. From where your code is it appears right above it you're killing the session self.delete_session_data(request). This might take away the session key or auth token. Try your code above that line.
It seems like you may have an AnonymousUser object in request.user when you access request.facebook.graph. Check to see if your user is_authenticated (more docs on AnonymousUser):
request.user.is_authenticated()
Another thing to play with is pdb from inside the dev server (manage.py runserver). The easiest way to use it is by using this line of code to put in a break point just before your code is bombing out:
import pdb; pdb.set_trace()
That will give you a prompt where you can look around at variables in context.

Categories