Where art they, Django-Paypal signals - django

Im using Django and django-paypal to try and setup a payment system.
Using the developer IPN simulator I can send posts to my server to test with.
[13/Sep/2015 18:54:08] "POST /paypal/ HTTP/1.1" 200 4
There is the post coming through from the simulator, no problems.
Now I want to get the signal fireing. I put the following in one of my aps models.py as in the documentation:
from paypal.standard.models import ST_PP_COMPLETED
from paypal.standard.ipn.signals import valid_ipn_received
def show_me_the_money(sender, **kwargs):
print "Hello"
ipn_obj = sender
if ipn_obj.payment_status == ST_PP_COMPLETED:
# Undertake some action depending upon `ipn_obj`.
if ipn_obj.custom == "Upgrade all users!":
Users.objects.update(paid=True)
else:
#...
valid_ipn_received.connect(show_me_the_money)
But I never get any repsonse from the signal.
I have no idea why? And I don't know how else to test this

I'm not familiar with this library, but are you defining your receiver and sender?
For example, on a post_save signal, I would structure it like this:
post_save.connect(receiver=my_function, sender=myModel)
It looks like show_me_the_money the money is the receiver of the signal, but it's not clear here what is firing the signal. Perhaps it should be:
post_save.connect(receiver=my_function, sender=ST_PP_COMPLETED)

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.

How to use Django channels to push newly created data to client side without reloading and not completely changing the existing normal view code

Hey guys I am quite new to django and django channels. I have a small doubt related to channels. For example, if I have a post model and I created a post, for the user to view the post, they need to either reload the page or we need to redirect them to the list page to view all the posts.
What if I want to push the newly created post to the front end or to the client side without having them to reload the page? Can we use channels for that? And if we use channels for that purpose, do we need to rewrite all the codes we wrote in normal views or adding a snippet code like sending a signal when created and running an async function will do the trick? Is it difficult to implement?
Thanks
What if I want to push the newly created post to the front end or to the client side without having them to reload the page?
YES, but with a caveat, that being you would have to cache all the posts written before that if the user(on the client) is on the <post_list> page. This won't be an issue for a small project but if their are too many posts it would take too much time to load.
Can we use channels for that? YES
And if we use channels for that purpose, do we need to rewrite all the codes we wrote in normal views or adding a snippet code like sending a signal when created and running an async function will do the trick?
YES and no, since you have used [django-rest-framework(DRF)], the rest-framework will only work on a HTTP protocol; when you use websockets you are using WS protocol thereby to handle the events django-channels has consumers just like views in django & DRF. But you can(you should for maintaining the robustness of the code) use serializers that you wrote for [django-rest-framework].
To demonstrate that, a scenario in which a user wrote a post and you received it on your django-channel AsyncConsumer you can use something like this:-
from channels.db import database_sync_to_async
#database_sync_to_async
async def save_post(self, data):
serializer = PostSerializer(data=data)
serializer.is_valid(raise_exception=True)
x = serializer.create(serializer.validated_data)#this will create the post
return PostSerializer(x).data #this will return the serialized post data
Since django-channels AsyncConsumer has all the events written in an async function
and saving a new post is a synchronous function we need to use a "#database_sync_to_async"; (make sure to use await-keyword when calling the save_post function).
To answer the later half of the question can you use django-signals?
Yes, modify the above code to call that django-signal instead of using the serializer in the "save_post" function above you can serialize the data inside the django-signals.
But I believe the above method would do the trick.
From my understanding I believe that you want to notify the user regarding a new post for that every user should be connected to the same channel-group and once the save_post function is completed dispatch an event in that group with the notification.
#inside the signals.py file
from .models.py import <Post-model>
from django.db.models.signals import post_save
from django.dispatch import receiver
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync #since everything in django
#is running synchronously.
"""
when the post gets created; the below receiver function would run.
"""
#receiver(post_save,sender=<Post-model>)
def notify_new_post(sender,instance,created,**kwargs)
if created:
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
group=<group_name>,#the group/room that will receive the broadcast
message= {
'type':"<name of the event>",
'payload':{<whatever notification you want to send>}
#before writing that payload make sure what type of
#channels you are using if you are using a
#"AsyncWebsocketConsumer" the payload should be in the
#appropriate type thereby use json.dumps to convert it to JSON.
#if you are using "AsyncJsonWebsocketConsumer" then you don't
#have to do it [this is just for precautionary].
}
)
Hope this helps, if not keep asking on this thread.

Can't hear the Django user_logged_out signal

OK, using the new-ish 'user_logged_in' signal in Django 1.3, and it works great. But, the corresponding 'user_logged_out' signal appears to be getting ignored. Let's assume I'm just trying to log the logins & logouts using a module called transaction. Again, the login bit works great, but the logout looks like it's being ignored... No error messages anywhere, and nothing in my resulting logs. OK, here's the code:
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.dispatch import receiver
from myapp.utils import transaction
#receiver(user_logged_out)
def log_logout(sender, **kwargs):
u = kwargs['user'].username
data={ 'Successful Logout': u }
transaction.add(data)
#receiver(user_logged_in)
def log_login(sender, **kwargs):
u = kwargs['user'].username
data={ 'Successful Login': u }
transaction.add(data)
...I've tested this a couple different ways, and I can't seem to get the user_logged_out signal to fire (or, be heard). Any ideas? Thanks in advance.

Django notification on comment submission

I am making use of Django's contrib.comments and want to know the following.
Are there any utils or app out there that can be plugged into an app that sends you a notification when a comment is posted on an item?
I haven't really worked with signals that much, so please be a little bit descriptive.
This is what I came up with.
from django.contrib.comments.signals import comment_was_posted
from django.core.mail import send_mail
if "notification" in settings.INSTALLED_APPS:
from notification import models as notification
def comment_notification(request):
user = request.user
message = "123"
notification.send([user], "new comment", {'message': message,})
comment_was_posted.connect(comment_notification)
Connect django.contrib.comments.signals.comment_was_posted to notification.models.send() as appropriate.
You have to register your comment_notification function with comment_was_posted signal.
from django.contrib.comments.signals import comment_was_posted
if "notification" in settings.INSTALLED_APPS:
from notification import models as notification
def comment_notification(sender, comment, request):
user = request.user
message = "123"
notification.send([user], "new comment", {'message': message,})
comment_was_posted.connect(comment_notification)
I don't know of an app (pretty sure there'll be something out there) but it is fairly straightforward to roll your own. You can tap the Comment model's comment_was_posted signal to call a function that will send you an email.

How to debug Django PayPal IPN?

I'm using this django app to implement PayPal IPN. I'm testing it using PayPal's IPN simulator, but it's telling me
IPN delivery failed. HTTP error code 500: Internal Server Error
So how can I debug this and see what's really going on? I've dug into code:
#require_POST
def ipn(request, item_check_callable=None):
"""
PayPal IPN endpoint (notify_url).
Used by both PayPal Payments Pro and Payments Standard to confirm transactions.
http://tinyurl.com/d9vu9d
PayPal IPN Simulator:
https://developer.paypal.com/cgi-bin/devscr?cmd=_ipn-link-session
"""
flag = None
ipn_obj = None
form = PayPalIPNForm(request.POST)
if form.is_valid():
try:
ipn_obj = form.save(commit=False)
except Exception, e:
flag = "Exception while processing. (%s)" % e
else:
flag = "Invalid form. (%s)" % form.errors
if ipn_obj is None:
ipn_obj = PayPalIPN()
ipn_obj.initialize(request)
if flag is not None:
ipn_obj.set_flag(flag)
else:
# Secrets should only be used over SSL.
if request.is_secure() and 'secret' in request.GET:
ipn_obj.verify_secret(form, request.GET['secret'])
else:
ipn_obj.verify(item_check_callable)
ipn_obj.save()
return HttpResponse("OKAY")
All looks fine and dandy there, but since it's not sending a response to my browser, it's kinda tricky to debug. What should I do? I'm trying to look at my apache logs, but it really isn't telling me much.
216.113.191.33 - - [06/Mar/2010:14:10:30 -0600] "POST /paypal/ipn HTTP/1.0" 500 16282 "-" "-"
I tried to send emails and log messages when this view was called, but neither wanted to work. It's possible that I entered the wrong URL into the IPN simulator :) I disabled the "post required" decorator and went to the page directly to see what was going on. My system started to logging "invalid transactions" as expected (since there was no post-data) and then I took a random stab in the dark and figured that Django's CSRF protection was kicking in and preventing PayPal from sending me the data. Adding the #csrf_exempt decorator seems to have fixed it. Yay for guessing errors.
In your django settings.py file, set DEBUG = False
Then for any HTTP 500s (incl. for those being returned to PayPal), you'll be sent a debugging email with all the python stack information.
You'll need to have Django email already set up for this to work, see http://docs.djangoproject.com/en/dev/howto/error-reporting/ for more info on that
You can install a Poster Add-on and make a POST to IPN notify_url from the browser. You will get a Response with all errors. Pretty helpful for debugging.
I just ran into the same problem and this was what I did wrong. Just in case anyone else is as silly as me...
Do not change the method signature from the wiki's
def show_me_the_money(sender, **kwargs):
to something like
def show_me_the_money(ipn, **kwargs):
Reason: in paypal.standard.ipn.models.PayPalIPN.send_signals the listeners are being called with a named argument: payment_was_successful.send(sender=self)
Therefore the method has to have an argument called sender.
I recall having hit (something like) this when using django-paypal too. I can't remember for sure what my cause was, but have you created/migrated the appropriate IPN tables in your database after including the ipn app in your setttings.py?