Supress warnings of 4xx response code of views in Django - django

I am trying to suppress some specific warning message of a view that's returning a 404 when the object is not found. I have tried:
try:
city = Cities.objects.get(id='abcd')
except ObjectDoesNotExist:
with warnings.catch_warnings():
return bad_request_response(_('City not found.'), resp_status=HTTP_404_NOT_FOUND)
But django seems to go around that and shows in the logs:
WARNING Not Found: /cities/abcd/
I am working with Django 1.11. I see it the docs that this is the expected behavior https://docs.djangoproject.com/en/1.11/topics/logging/#django-request but I am trying to go around that. Any hint is welcome

Related

Meaningful error message if Redirect after Post fails (Testing Django)

After successful POST a redirect should happen (PRG Pattern)
response = admin_client.post(url, data)
assert response.status_code == 302
If this test fails, I get a very meaningless error message.
AssertionError assert 200 == 302
Since the status is 200, I know that the form data which I send did not validate.
Is there a django-way to get the error message of the django form validation into the exception?
Example: I would like to see something like "foo is require" (if "foo" is not in data)
Django's custom TestCase class is a subclass of unittest.TestCase and all of its assert functions have a msg argument which when passed are used as the error message when an assertion fails. Also since you want the forms error to be displayed, you can get the form instance from the context and get its errors. Something like this should work:
assertEqual(response.status_code, 302, str(response.context['form'].errors))
Note forms also have form.errors.as_data() which will show the exceptions raised during form validation, or form.errors.as_json() any of which you might find more usable. Reference Form.errors

How to follow Django redirect using django-pytest?

In setting up a ArchiveIndexView in Django I am able to successfully display a list of items in a model by navigating to the page myself.
When going to write the test in pytest to verify navigating to the page "checklist_GTD/archive/" succeeds, the test fails with the message:
> assert response.status_code == 200
E assert 301 == 200
E + where 301 = <HttpResponsePermanentRedirect status_code=301, "text/html; charset=utf-8", url="/checklist_GTD/archive/">.status_code
test_archive.py:4: AssertionError
I understand there is a way to follow the request to get the final status_code. Can someone help me with how this done in pytest-django, similar to this question? The documentation on pytest-django does not have anything on redirects. Thanks.
pytest-django provides both an unauthenticated client and a logged-in admin_client as fixtures. Really simplifies this sort of thing. Assuming for the moment that you're using admin_client because you just want to test the redirect as easily as possible, without having to log in manually:
def test_something(admin_client):
response = admin_client.get(url, follow=True)
assert response.status_code == 200
If you want to log in a standard user:
def test_something(client):
# Create user here, then:
client.login(username="foo", password="bar")
response = client.get(url, follow=True)
assert response.status_code == 200
By using follow=True in either of these, the response.status_code will equal the return code of the page after the redirect, rather than the access to the original URL. Therefore, it should resolve to 200, not 301.
I think it's not documented in pytest-django because the option is inherited from the Django test client it subclasses from (making requests).
UPDATE:
I'm getting downvoted into oblivion but I still think my answer is better so let me explain.
I still think there is a problem with Shacker's answer, where you can set follow=True and get a response code of 200 but not at the URL you expect. For example, you could get redirected unexpectedly to the login page, follow and get a response code of 200.
I understand that I asked a question on how to accomplish something with pytest and the reason I'm getting downvoted is because I provided an answer using Django's built-in TestCase class. However, the correct answer for the test is/was more important to me at the time than exclusively using pytest. As noted below, my answer still works with pytest's test discovery so I think the answer is still valid. After all, pytest is built upon Django's built-in TestCase. And my answer asserts the response code of 200 came from where I expected it to come from.
The best solution would be to modify pytest to include the expected_url as a parameter. If anyone is up for doing this I think it would be a big improvement. Thanks for reading.
ORIGINAL CONTENT:
Answering my own question here. I decided to include final expected URL using the built-in Django testing framework's assertRedirects and verify that it (1) gets redirected initially with 302 response and (2) eventually succeeds with a code 200 at the expected URL.
from django.test import TestCase, Client
def test_pytest_works():
assert 1==1
class Test(TestCase):
def test_redirect(self):
client = Client()
response = client.get("/checklist_GTD/archive/")
self.assertRedirects(response, "/expected_redirect/url", 302, 200)
Hat tip to #tdsymonds for pointing me in the right direction. I appreciated Shacker's answer but I have seen in some scenarios the redirect result being 200 when the page is redirected to an undesirable URL. With the solution above I am able to enforce the redirect URL, which pytest-django does not currently support.
Please note: This answer is compliant with the auto-discover feature of pytest-django and is thus not incompatible (it will auto-discover both pytest-django and Django TestCase tests).

Return 422 status code in ModelViewSet

For interoperability with EmberData it seems I need to reply with 422 (Unprocessable Entity) instead of 400 (Bad Request) whenever validation errors occur. I have two questions:
How can I specify the response status code when using a ModelViewSet?
Why is the 422 not listed in the list of possible return codes?
And bonus:
Why is EmberData expecting 422? This is not part of the JSONAPi spec, as far as I can see.
422 is part of the WebDAV DRF which error codes aren't in DRF.
This doesn't stop you to use it. They are just a human readable version of the number itself.
One option would be to override rest_framework.exceptions.ValidationError.status_code and set it to 422.
Edit - Changing the default error code:
# At the top of a views.py file, by the ends of import add:
from rest_framework.exceptions import ValidationError
ValidationError.status_code = 422

raise 404 instead of 500 in urls.py django

I have a website powered by django. There are few url patterns which i donot catch
say www.mysite.com/12233445 www.mysite.com/###$$$ www.mysite.com/alphabetagamma this leads to 500 error page, i want all such non-standard urls to redirect to 404 error page. Any clue which is the best method of doing it?
By using custom handler500ΒΆ you can change the response status code from 500 to 404. make your project to return this post may help you get the idea how to do it - http://ilian.i-n-i.org/custom-404-not-found-page-with-django-cms/
But as #Daniel said it isn't normal to get 500 for not found pages. Turn on the debug mode and check what error leads to 500.
Have you created a 404.html template? This happens when you have DEBUG = False in your settings.py, yet you haven't created a 404.html template. This raises a TemplateDoesNotExist exception, hence the error 500 response.
go to your views, perform all the get/filter operations within a try except block.
try:
variable = object.get(some checks here)
except: object.DoesNotExist
raise Http404
return (whatever)

how show personalized error with get_object_or_404

I would like to know how to show personalized errors with the get_object_or_404 method. I don't want the normal Http404 pages, but I want to display a custom message with the message: the result is none.
Thanks :)
The get_object_or_404() is essentially a simple 5-line function. Unless you have some specific reason for using it, just do:
try:
instance = YourModel.objects.get(pk=something)
except YourModel.DoesNotExist:
return render_to_response('a_template_with_your_error_message.html')
If for whatever reason you have to use get_object_or_404(), you can try putting it in a try: ... except Http404: ... block, but I honestly can't think of a plausible reason for that.
As stated by michael, when using get_object_or_404 you cannot customize the message given on http 404. The message provided in DEBUG does offer information about the exception however: "No MyModel matches the given query."
Check out the doc on this. There are three arguments: Model, *args, and **kwargs. The last two are used to build an argument for either get() or filter() on the Model.
The reason I wrote, however, is to address the question of why we would want to use a helper function such as get_object_or_404() instead of, for example, catching it with an exception like Model.DoesNotExist.
The later solution couples the view layer to the model layer. In an effort to relax this coupling we can take advantage of the controlled coupling offered in the django.shortcuts module[1].
And why exactly aren't you using your server's capeability to do just that?
get_object_or_404() is redirecting to the default 404 page right?
If you are on debug mode you won't see it but when deployed django will just refer to the server's 404 html page.
That can't be done with that shortcut. It will only raise a Http404 exception. Your best bet is a try catch if you want full control. Eg.
try:
obj = Model.objects.get(pk = foo)
except:
return HttpResponseRedirect('/no/foo/for/you')
#or
return render_to_response ...