Django testing, issue with localised error messages - django

I am making the first steps with using Django localisation, and I have just faced the first difficulty. I am trying to test that something in models works as expected. For invalid input, a specific ValidationError should occur. The code in the test looks like this:
try:
p = ...
p.full_clean()
self.fail('Expected exception not raised')
except ValidationError as e:
dict_err = dict(e)
self.assertIn('category', dict_err.keys())
self.assertEqual(len(dict_err.keys()), 1, 'Encountered more than one error')
self.assertIn('blank', dict_err['category'][0])
As long as the site operates in English (LANGUAGE_CODE = 'en-uk'), the caught error looks like this:
ValidationError({'category': ['This field cannot be blank.']})
To check that I've got exactly this ValidationError, I am searching for the work 'blank' in the error message (last line of the code above).
It fails as soon as I switch the site to another language. E.g. for German it looks so:
ValidationError({'category': ['Dieses Feld darf nicht leer sein.']})
Looking for 'blank' obviously fails the test. How could I check the error message? I expect, somewhere deep in Django, there should be a unique identifier of this message (so that Django knows how to translate). But I don't see any traces of it in the error value. I would expect something like
self.assertIn(_(id), dict_err['category'][0])
would do the trick, if I could guess what id should be, and if I understand correctly what _() does.

By further trial and error, I've found that for simple messages the message ID is simply the original English message. That is, one can do:
...
expected_error = _("This field cannot be blank.")
self.assertIn(expected_error, dict_err['category'][0])
or even
self.assertEqual(expected_error, dict_err['category'][0])
It works then independent of the site language.
For errors using named-string interpolation, the code would look like follows. Assuming the English-language error message is Value 'x' is not a valid choice., we do:
expected_error = _("Value %(value)r is not a valid choice.") % {'value': 'x'}
self.assertEqual(expected_error, dict_err['category'][0])
I've found that the easiest way to determine the respective message ID is to search for the part of the English error message in the venv files. For example, I found the error above searching for "is not a valid choice", which is obviously an immutable part of the message.
Special thanks to #MosesKoledoye: his comment to my question hinted in the right direction.

Related

Django: How to next process Stripe's ACH Verification Error

I want to be able to act on an error that happens when I run one of the commands to interface stripe. I can see the error, but can't seem to capture it.
When I run a the verification for stripe's ACH payment format and use bad deposist, I get the following error:
in handle_error_response
raise err
stripe.error.CardError: Request req_UyfXgBVRSOqUuJ: The amounts provided do not match the amounts that were sent to the bank account.
How do I take this and do something meaningful with it. My code looks like this:
ank_account_response = customer.sources.retrieve(request.stripe_id)
bank_account_response.verify(amounts=[request._post['deposit_1'], request._post['deposit_2']])
The error appears on the last line of code. I want either set do something like output = bank_account_response.verify... or try: bank_account_response, but I can't get it to work.
Thoughts?
You should handle error response something like below.
try:
bank_account_response.verify(amounts=[request._post['deposit_1'], request._post['deposit_2']])
except CARD_ERROR as card_error:
return JsonResponse(status_code=400,
message=card_error.message)

TypeError: expected string or bytes-like object User.id

I'm trying to register an new Transaction object on the DB using Django, but I'm having TypeError: expected string or bytes-like object when I try to do user_id = user.id I can't really understand why this is happening, since I do the same steps when registering a new Bank object (as shown on prints below). I've tried to debug and the local variables have the correct value, I've also tried to cast user.id with string or int, but none of them worked.
traceback console error create Transaction method create Bank method
models.py
Firstly, please don't post code or errors as images; they are text, they should be posted as text in the question.
However I don't see anything in any of those snippets that suggest the error is with the user - that line is probably highlighted because it's the last in that multi-line call.
Rather, the error looks to be in the reference to date.today - if that's the datetime.date class, then today is a method, which you would need to call:
Transaction.objects.create(date=date.today(), ... )
Or, since that field has a default anyway, you could leave out the date attribute from the create call altogether.

Model validation: can I access ValidationError code?

I have two directly related questions.
Django documentation recommends raising ValidationError with a code:
# Good
ValidationError(_('Invalid value'), code='invalid')
# Bad
ValidationError(_('Invalid value'))
How can I access this code in tests? All my attempts to use as_data, as_json, or simply .code on the caught exception fail. Unfortunately, the suggestions I see all relate to form validation. My test validates the models.
It is almost the same question as asked before (I don't use forms).
The related question: the same documentation page linked above gives a few examples of how to raise ValidationError, and while "Raising ValidationError" section recommends using the code, "Using validation in practice" never mentions it again, and the examples there don't use code. I wonder if that's an indication of this feature being stale.
I learned how to debug Django tests in PyCharm, and it helped me find a solution. For others' sake:
The error codes are accessible via exception.error_dict[field_name][err_no].code. For example, the following checks a very specific error is raised:
def test_negative_photo_number(self):
"""Cannot create photo with negative photo number"""
with self.assertRaises(ValidationError) as ve_context:
self.create_photo(album_number=1, photo_number=-2)
e = ve_context.exception
print(e.error_dict)
self.assertEqual(len(e.error_dict.keys()), 1, 'Encountered more than one problematic field')
self.assertEqual(len(e.error_dict['number']), 1, 'Encountered more than one error')
self.assertEqual(e.error_dict['number'][0].code, 'min_value')
For ValidationError raised outside field validators (e.g. by model.clean method), replace field name ('number' above) with __all__.

Django - Passing a filtered result to a template

Inside of my Django view I am trying to retrieve results from my database and then pass them on to my template with the following code:
f = request.GET.get('f')
try:
fb_friends_found= UserProfile.objects.filter(facebookid__in=f).values('facebookid')
i = fb_friends_found[0] #To get the dictionary inside of the list
results = i['facebookid'] #To retrieve the value for the 'facebookid' key
variables = RequestContext (request, {'results': results })
return render_to_response('findfriends.html', variables)
I carried out the first three lines within the 'try' block using manage.py shell and this worked fine, printing the correct 'facebookid'.
Unfortunately I can't get it to work in my browser. Any suggestions?
Do you have a specific problem you're running into, such as an exception?
I feel like you should get some kind of exception if you have a try block without an except statement.
try:
# something
except Exception: # but be more specific
print "exception occurred"
Otherwise, the code looks good, and if nothing is rendering in your browser, I'd look into the template. Unless... you're hiding errors in your try block, in which case you should remove the try block and let the error occur to understand what's wrong.

Reverse Not Found: Sending Request Context in from templates

N.B This question has been significantly edited before the first answer was given.
Hi,
I'm fairly new to django, so apologies if I'm missing something obvious.
I've got a urls.py file that looks like this:
urlpatterns = patterns(
'',
(r'^$', 'faros.lantern.views.home_page'),
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', 'django.contrib.auth.views.logout'),
(r'^about/$', 'faros.lantern.views.about_page_index', {}, 'about_page_index'),
(r'^about/(?P<page_id>([a-z0-9]+/)?)$', 'faros.lantern.views.about_page', {}, 'about_page'),
)
Views that looks like this:
def about_page_index(request):
try:
return render_to_response('lantern/about/index.html', context_instance=RequestContext(request))
except TemplateDoesNotExist:
raise Http404
def about_page(request, page_id):
page_id = page_id.strip('/ ')
try:
return render_to_response('lantern/about/' + page_id + '.html', context_instance=RequestContext(request))
except TemplateDoesNotExist:
raise Http404
And a template that includes this:
Contact
Contact
I'm getting this error message:
Caught an exception while rendering: Reverse for '<function about_page at 0x015EE730>' with arguments '()' and keyword arguments '{'page_id': u'contact'}' not found. The first reverse works fine (about_page_index), generating the correct URL without error messages.
I think this is because the request argument to the about_page view (request) is used, so I need to pass it in when I generate the URL in my template. Problem is, I don't know how to get to it, and searching around isn't getting me anywhere. Any ideas?
Thanks,
Dom
p.s. As an aside, does that method of handling static "about" type pages in an app look horrific or reasonable? I'm essentially taking URLs and assuming the path to the template is whatever comes after the about/ bit. This means I can make the static pages look like part of the app, so the user can jump into the about section and then right back to where they came from. Comments/Feedback on whether this is djangoic or stupid appreciated!
If I guess correctly from the signature of your view function (def about_page(request, page_id = None):), you likely have another URL configuration that points to the same view but that does not take a page_id parameter. If so, the django reverse function will see only one of these, and it's probably seeing the one without the named page_id regex pattern. This is a pretty common gotcha with reverse! :-)
To get around this, assign a name to each of the url patterns (see Syntax of the urlpatterns variable). In the case of your example, you'd do:
(r'^about/(?P<page_id>([a-z]+/)?)$', 'faros.lantern.views.about_page',
{}, 'about_with_page_id')
and then in the template:
Contact
Edit
Thanks for posting the updated urls.py. In the url template tag, using the unqualified pattern name should do the trick (note that I'm deleting the lantern.views part:
Contact
Contact
Edit2
I'm sorry I didn't twig to this earlier. Your pattern is expressed in a way that django can't reverse, and this is what causes the mismatch. Instead of:
r'^about/(?P<page_id>([a-z]+/)?)$'
use:
r'^about/(?P<page_id>[a-z0-9]+)/$'
I created a dummy project on my system that matched yours, reproduced the error, and inserted this correction to success. If this doesn't solve your problem, I'm going to eat my hat! :-)