Django message history - django

I wonder if you have any ideas on this
I present messages as below - it works great, when there is an error it shows the error.
But, how do I retain these messages - so the user can see all of the errors they had since their session started?
I.e. if I working for a while, I might want to review all my form validation failures at the end.
Thanks
messages.error(request, ('ERROR: Your milestone was not created. Milestone points must be numeric. Please try again.'))

Maybe Django's Sessions.
https://docs.djangoproject.com/en/3.2/topics/http/sessions/#using-sessions-in-views
def message_error(request, msg):
errors = request.session.get('errors', []):
errors.append(msg)
request.session['errors'] = errors
messages.error(request, msg)
def get_message_error_list(request):
return request.session.get('errors', [])

Related

Django REST Framework User Registration Privacy Issue: Looking for best practice to not show exception if user with given email exists already

I am suprised I have not found anything on the web regarding the following issue which I thought should be common? I may just have used the wrong search terms so I am happy to receive links with more info.
My problem is that when using ACCOUNT_EMAIL_VERIFICATION = 'mandatory' I want to not give a clue to any user except the owner of an email address whether that mail is registered on my website, i. e. not show a "A user is already registered with this e-mail address." if an existing email is entered.
My assumption is that that would require for the registration endpoint to return the same response independent of whether the email exists or not. I see several possible approaches, but none seems to be a good one:
Use a custom exception handler to remove the exception in question from the error messages sent. That means I have to somehow identify the abovementioned exception among all error messages sent so I can still keep the others in the response. I guess I have to identify the exception message by a string the actual error message (possibly dependent on language settings?). If there are multiple error messages I can simply remove the one in question. But if the exception is the only exception I'd have to fake the same response that would be given after successful creation of a user. That sounds fiddly and not robust to me.
Check uniqueness before is_valid() is called and fake a successful response. But then I won't be able to return exceptions from possible additions errors.
Remove the unique-contraint from the DB so that the is_valid() method does not raise an error and prevent the instance from saving in perform_create(). But I don't really want to remove that database-level protection layer.
There must be a better solution out there I hope?
Appreciate any help!
Mike
You should change the error/validation message to be more generic, for example:
Email address error.
It is very similar situation as in login. You don't write explicitly that the email address doesn't exist or the password is too short, you just send a message:
Invalid email or password.
Additionally, you can add information that if the problem repeats please contact to system/service administrator. Then if a person contacts the administrator from his/her email problem can be fixed manually.
Solution with generating unique email address might be too complex and can bring unexpected problems. What if the user just forgot about the old account and recreate the new account, with loss of previous account's data.
I came up with a possible solution today. Seems to be a little improvised but avoids the abovementioned problems. Would be happy to hear your thoughts or suggestions.
The approach was to override the create method and if it fails to the uniqueness constraint generate a pseudo serializer instance with a modified email address that would be unique and only return errors from that pseudo instance.
Additionally, the entire create request only returns an HTTP 200 OK status, independent of whether a user was created or if a user with that mail existed.
#custom create method to not return "email exists" error to user if email exists
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
try: #try if data is valid. If so, create user.
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer)
print('User created:')
print(user)
except ValidationError as err: #if Validation error was returned
email = request.data['email']
email = get_adapter().clean_email(email)
# check if one issue was due to existing email
if allauth_settings.UNIQUE_EMAIL and email and email_address_exists(email):
# generate unique version of the mail address by appending string to local part to revalidate with unique mail address
provisional_email = make_email_unique(email, uniqueness_validator=email_address_exists)
# create fake request.data dict with unique mail address
provisional_request_data = request.data.copy()
provisional_request_data['email'] = provisional_email
#get new serializer with new request.data
provisional_serializer = self.get_serializer(data=provisional_request_data)
#check if data was valid if email was unique
try:
provisional_serializer.is_valid(raise_exception=True)
# get user who owns original email from request
user = get_user_from_email(request.data['email'])
except ValidationError as err2:
# else validate again, this time return any errors that may occur even with unique email
raise err2
else: #if error was not (also) due to existing mail problem raise validation errrors
raise err
headers = self.get_success_headers(serializer.data)
# always return 200 OK if no error is shown (i. e. no 201 CREATED that could hint to an existing mail address)
return Response(status=status.HTTP_200_OK,
headers=headers)

rollback saved objects whenever email send failed

Hi I've been wondering how to rollback a saved object on a following method when one method failed.
forms.py
# somewhere class forms
def handle(self, view, request, *args, **kwargs):
# assuming i get an object successfully.
application = models.Application.objects.first()
# do something in its attrs and save
application.name = 'test'
application.save()
# application is fix and send via django.core.mail EmailMessage
self.send_mail(application)
return True
now when I had a good connection everything will work fine. But when it does not it will save the application but will not send the email. Resend is not an option since its a one time send supposedly. Now my idea to rollback the application and throw exception something like "Action Failed. Maybe poor connection. Please try again".
try:
application.save()
self.send_mail(application)
except ConnectionError?:
# rollback ?
raise Exception(msg) ?
Any help and better way to handle it? Thanks for the help and sorry for the trouble.

Notification error

Is there any way to launch custom errors in the admin site like that?:
Currently I throw the error with
raise forms.ValidationError('error')
but shows the debug error screen
Where are you putting the raise forms.ValidationError('error')?
In your form clean() method is a good place to raise custom errors. You can even do def clean_fieldname() to preform specific validation for one field. From the django docs
from django import forms
class ContactForm(forms.Form):
# Everything as before.
...
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "fred#example.com" not in data:
raise forms.ValidationError("You have forgotten about Fred!")
# Always return the cleaned data, whether you have changed it or
# not.
return data
This link also could help

Proper way to catch KeyError in Django?

I am creating an API where a URL is sent to the server. I want to get the username, etc from the URL. I use the following code to do that:
try:
username = request.REQUEST['username']
message = request.REQUEST['message']
time = request.REQUEST['time']
except KeyError:
...
However, there are times when the URL does not have a username, message, time, etc in it. In that case, a KeyError is raised. I want to be able to catch this and know which parameter was missing so I can issue an error response code that tells the user which parameter was missing. In the except area, is there a way to determine where the code failed?
Not cleanly. Use a default of None and test after.
try:
username = request.REQUEST.get('username', None)
...
except ...:
...
else:
if username is None:
...
wouldn't this work? No need for exceptions
if 'username' in request.REQUEST:
username = request.REQUEST['username']
else:
#username not found

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?