How do I troubleshoot in Django? - python

I come from a PHP background and I've been trying to learn Python but I'm having a lot of trouble when it comes to debugging as I'm not yet sure how to go about this in Django or Python in general.
I'm used to being able to print_r or var_dump everything in PHP. I could do it in the controller, In a service layer or the even the model and data would show up in my web browser.
I can't do this in Django. Depending on what I'm doing, attempting to just perform a print on an object from my view will bring the page down or output something to my console that doesn't really help me. Here's an example:
class Page(View):
def get(self, request, *args, **kwargs):
response = Data.objects.all()
# for whatever reason, I want to print something right now:
print response
# return JsonResponse({'success':response})
The above will take down my page completely with a notice saying:
The view didn't return an HttpResponse object. It returned None instead.
There are some instances while working with CBV's where I noticed I could get the data to just dump somewhere such as the console. But it wouldn't be anything that would help me. For instance if I was trying to to take a look at the contents of response from above, it would just show up like so:
[object Object] [object Object] [object Object]
A var_dump would have allowed me to actually see inside of it.
So I'm guessing I'm going about this all wrong. Do people just dump data when they're debugging in Python? If they do, how do you perform this, and does it show up in the web browser or the console? If not, how do I go handle basic troubleshooting in Django? Example scenarios:
I want to see the contents of a list or dictionary
I want to see the raw sql query being performed by the ORM
I want to see if a function is being executed by slipping some text inside to be outputted on the front end

The basic problem is that you are used to how PHP is wired into the entire request/response chain, and this is not how Python is configured when developing web applications.
In PHP-world, the server guarantees a response, which closes the request/response cycle. The PHP file is directly requested by the browser, so you are unaware of what is actually happening in the background.
A typical PHP request, is just the same as a request for any other static asset, like a plain index.html file or logo.gif. The browser requests, the web server accepts the request, and then returns the response; the only difference being if a file with .php is requested, then it goes through an intermediary step where the PHP interpreter evaluates the file, and sends the result back to the client.
In Python however, when a request is made that is mapped to a Python backend process (sometimes called upstream process); the webserver waits for a response from the process (the timeout for this can be adjusted). If a response isn't received within the defined timeout, the webserver sends a timeout error page (504 ERROR).
It is the responsibility of the Python process to send a proper response (with all headers, etc.) as is expected by the browser. In PHP, this is hidden from you (as the developer), because the PHP engine will add these extra information for you. So when you have Python code that does not send such a response (as in your case), django helps you out by printing a friendly error message.
In your case - the view is not returning a response; its just printing something. This print statement will go to the standard output (or error stream) of the application (will be printed on the console if you launched it on the shell, or written to the server's log, etc.) it will not be sent back to the client (the browser).
In order to debug django applications:
Make sure DEBUG = True is set in your settings.py
Run your application with python manage.py runserver
Now, when you do any print statement, it will be shown on the console and if there is an error in your application code - as long as you are returning a valid response - you will get a rich error page along with a stack trace to help identify the problem.
There is no more "debugging statements and printing things on the browser" as you are developing; this is just how Python is wired into the web world.
For your other questions:
I want to see the contents of a list or dictionary
Simply print it. The output will be on your console (the same place where you wrote python manage.py runserver)
I want to see the raw sql query being performed by the ORM
If you are testing things out in the django shell, you can just put .query at the end of your ORM call to see the query being sent:
>>> result = MyModel.objects.filter(foo='bar')
>>> result.query
(query printed here)
For a more rich debugging experience, install django_debug_toolbar.
I want to see if a function is being executed by slipping some text inside to be outputted on the front end
There is no "outputting to the front end". For such things, you can just print() what you need, or even better use the logging system.

First, the reason you're getting an error isn't because you're printing, it's because you commented out the return:
class Page(View):
def get(self, request, *args, **kwargs):
response = Data.objects.all()
# for whatever reason, I want to print something right now:
print response
return JsonResponse({'success':response}) # <-- dont comment this out
Secondly, printing arbitrary objects won't always provide the best amount of information. If something defines a __str__ or __unicode__ method, that's what will be printed to the console. Otherwise, the object name along with its memory id will be printed. Not the most useful. Printing an object doesn't do a "deep" print.
You can try to print out the locals and globals:
print(locals())
print(globals())
Or serialise an object to JSON and print that:
print(json.dumps(response)) # some objects may not be serialisable to JSON though.
But you may not get the amount of detail you want from that either. The alternative is to run your webserver in debug mode, and raise an exception:
# settings.py
DEBUG = True
# views.py
def my_view(request):
raise Exception('debug')
.. and rely on django to show you the debug error page, which includes a stack trace, and allows you to inspect all the variables available in each frame.

First off, Django views require some sort of HttpResponse object to be returned. From the docs-
In contrast to HttpRequest objects, which are created automatically by Django, HttpResponse objects are your responsibility. Each view you write is responsible for instantiating, populating and returning an HttpResponse.
There are many classes that that you can use to return that response (render_to_response, HttpResponseRedirect, JsonResponse and many, many more- https://docs.djangoproject.com/en/dev/ref/request-response/#httpresponse-subclasses). Your line # return JsonResponse({'success':response}) will do that if you get rid of the #.
Instead of a var_dump, you can use Python's dir function. All attributes of a class or class instance will be shown. See- Is there a function in Python to print all the current properties and values of an object?
You can print a dictionary. There are many ways to do it.
for key,value in your_dictionary.items():
print(key, ":", value)
Easy to do. print Data.objects.all().query . See - How to show the SQL Django is running
Or you could add print statements inside the function (or a decorator that states the function is executing).
These are pretty basic parts of Django and Python. Not to be rude, but your time would probably be better spent doing some tutorials in full than embarking on your own projects. Once it clicks, it will be very simple. MVC/MVT frameworks have a different structure than PHP and you'll need to get used to working within it or will be frustrated.

Related

Django file based email backend

It's probably something very obvious, but I can't seem to figure it out.
This snippet is from Django's file based email backend (django.core.mail.backends.filebased.py)
def write_message(self, message):
self.stream.write(message.message().as_bytes() + b"\n")
My question is. How can I find out what class is message an object of?
Context for why:
My code sends emails along various execution paths. I want to leverage Django's filebased backend, instead of firing live emails during debugging and unit testing (or creating my own file based system). The relevant code has a MIMEMultipart object currently (with utf-8 coded text) that works fine for production. I need to be able to convert that into an object that can be printed legibly by the above snippet.
PS: I come from a C++ background where this would've been an easy question to answer.
You can use python's builtin type function.
type(obj)
Or if you want to check if it is an instance of a specific object use isinstance function.
if isinstance(obj, Obj_Class):
# do something

Django test client get returns 404 instead of 200

I'm very new to Django and testing....
I'm testing my app and every time I do threads_page = self.client.get('/threads/1/') it returns a 404 status instead of 200 (that url works, the 1 is the subject id).
I found a thread with the same problem and there was a response with the issue (Django test client get returns 404 however works in shell) but I still don't know how to solve it.
The problem is that in my views I have a get_object_or_404 with an argument but I don't know how to pass the argument in the test:
views.py
def threads(request, subject_id):
subject = get_object_or_404(Subject, pk=subject_id)
return render(request, 'forum/threads.html', {'subject': subject})
This is my test right now
def test_check_threads_content_is_correct(self):
threads_page = self.client.get('/threads/1/')
self.assertEqual(threads_page.status_code, 200)
Thank you!
*This is my code in Github in case it helps
https://github.com/IreneG5/we_are_social_forum
I have had very similar issues. I never really found a problem and resorted to having 2 urls - one with the parameter and one without. In views.py I change def threads(request, subject_id): to def threads(request, subject_id=None): and then add at the top of the function:
if not subject_id:
subject_id = request.GET.get('subject')
and in my test:
threads_page = self.client.get('/threads/', {'subject': '1'})
If someone has an answer that works with parameters in the URLs then take that answer instead - and I will use it too! But if not, then this should get the job done.
One more important part to Django testing - the test database.
The testing normally uses a separate, starts off empty with every test, database. There are a couple of ways of putting data into the database. The basic concept is that by running the tests on a "test database" instead of the production database, you can precisely control exactly what is in the database so that your testing will be consistent and so that any tests (e.g., test of a delete) won't mess up your production data. It took me a while to understand it all myself. The Django docs explain a lot of this, though not always so clearly. But if you have tests that expect data, even if it is data that you have had in the regular database "forever", it won't be in the test database unless you somehow specifically add it.

Send form data to MongoDB using Flask

I'm having troubles sending my form data to MongoDB with a Flask setup. The form looks something like this: https://jsfiddle.net/8gqLtv7e
On the client-side, I see no errors. But when I submit the form, I receive a 500 Internal Server Error and I'm having a hard time finding the solution. The problem is the last line below in my views.py file:
#app.route('/recordReport', methods=['POST'])
def recordReport():
homeReportsCollection = db["reports"]
address=request.form.get['address']
rgbImage=request.form.get['rgb']
homeReportsCollection.insert({'address':address, 'rgb':rgbImage})
Because if I replace that with return json.dumps({'status':'OK', 'address':'address', 'rgb':'rgbImage'}), I can see the correct data in my browser. Am just not able to send it to a collection in MongoDB.
This answer is a summary of the comments (where a solution was found).
Have you tried typecasting address and rgbImage to String before inserting?
Type invalidation is the root of many common bugs in DB operations.
There used to be a bug in Mongo back in 2013. The data would be inserted into the collection. But Mongo would not return a correct status response. That led to servers going 500. Have you tried verifying if the data was indeed inserted into the collection?
Additionally run your flask app in debug=True mode. That might give additional data.
Flask has very good debug traceback reporting support. This is generally a good idea. In fact this should be the first thing to do when encountering an error.
So this is weird, I turned on debug=True and I get the following error: ValueError: View function did not return a response. BUT the data did actually get sent to DB via homeReportsCollection.insert({'address':address, 'rgb':rgbImage}) line. I see it in my collection. How do I fix the error? Because the user is redirected to /recordReport.
So the data was indeed inserted into the collection. It is possibly a Flask only issue. The traceback says it all. Flask requires that something is returned by a view method.
Try returning something in your function recordReport(). Return anything you want. It could be an OK message. It could be the _id of the document you just inserted into the collection. Just don't return None. Try this.
This behaviour is documented in this SO question.
Yeah, I returned an HTML template and no error now.
This is indeed the solution. Return something other than None from your flask view methods. This also validates the behaviour observed by asker in the question:
Because if I replace that with return json.dumps({'status':'OK', 'address':'address', 'rgb':'rgbImage'}), I can see the correct data in my browser.

Get variable from url in view

I want to make an HTTP GET request to ip:port/this1234 and use "1234" as a variable in Python code. The "1234" is an arbitrary int. How do I get the int as an argument to my view?
#app.route('/stop####')
def stop####():
global stopped
if stopped:
call(["./stop.sh"], shell = True)
stopped = False
return "started"
return "already started"
You'll want to take a look at the Quickstart guide.
Meanwhile, you can get POST data using
myvar = request.form["myvar"]
and GET using
myvar = request.args.get("myvar")
The guide then goes on to mention some error handling recommendations and references the more in depth request object page.
We recommend accessing URL parameters with get or by catching the KeyError because users might change the URL and presenting them a 400 bad request page in that case is not user friendly.
For a full list of methods and attributes of the request object, head
over to the request documentation.
You might also want to look at routing a bit. I'm uncertain what you're trying to accomplish with the pound sign in your routing (EDIT: I see what you mean on re-reading; see edit at bottom). Note the quote below from a comment on SitePoint.
browsers don't send the #whatever part of the URL to the server in the HTTP request when requesting the page
Ok, so if you want to pass the value in the URI, I recommend something more like: example.com/this/1234 and your routing rule would look like #app.route('/this/<myVar>') above your def my_func(myVar):
Finally, at some level, killing any process based off of an http request seems awful daring, but you know your environment best, and for all I know, it might not even be exposed to the internet. Goodluck, and don't forget to be safe about this.
I think the obvious way to do it would be:
#app.route('/kill/<int:pid>')
def kill(pid):
.....

Getting error while trying to add header with Set-Cookie in GAE

I am trying to include external python module in my project for working with sessions. It's named gmemsess.py. It tries to add Set-Cookie header in the response and an error appears:
rh.response.headers.add_header('Set-Cookie','%s=%s; path=/;'%(name,self._sid))
AttributeError: HeaderDict instance has no attribute 'add_header'
I read documentation and everything seems ok, but it doesn't work. Why this error can appear?
Also, I use webapp2 to manage subdomains. May be there is something goes wrong because of this?
The headers.add_header method should absolutely work if you are using stock AppEngine, but I am guessing that you are using a framework -- and there are plenty of them, like Bottle -- that uses a custom replacement for webob's Response object.
A little bit of time with Google reveals that there is at least one identifiable class called HeaderDict that extends MultiDict, and I think that is what you are dealing with. In that case, you should to into gmemsess.py and change the line
rh.response.headers.add_header('Set-Cookie','%s=%s; path=/;'%(name,self._sid))
to read
rh.response.headers['Set-Cookie'] = '%s=%s; path=/;'%(name,self._sid)
That should fix you right up.
Disregard -- see comments below
Is that module written to work with App Engine? The response objects used by App Engine do not have an add_header method, see the docs.
Instead, there's a dict-like object headers that you can just assign values to like
response.headers['Set-Cookie'] = "whatever your cookie value is"

Categories

Resources