Testing for links in a page content in Django - django

Good day!
So going to my question,
I have a template with some links and I want to assert that they are indeed there in my page.
My html section,
<section>
<p>Welcome to open radio. We help you get on air!</p>
<p>Login</p>
<p>View all our stations</p>
<p>SignUp!</p>
</section>
My test now,
response = self.client.get(reverse("home"))
assert reverse("userlogin") in response.content
assert reverse("liststations") in response.content
assert reverse("userregistration") in response.content
My test passes and I understand I am not really asserting for the links here but rather for the strings of URLs. How may I test for links specifically?

Django's own test suite uses this to assert a certain url is in the response:
response = self.client.get(reverse("home"))
self.assertContains(response, 'Login' % reverse("userlogin"), html=True)
...
self.assertContains handles both the fact that response is a response object, and that both sides are html and only have to be equivalent, not necessarily equal.
This is assuming you're using (a subclass of) django.test.SimpleTestCase.

Related

Doing auto redirect using CherryPy

I've just discovered CherryPy. I am going through the tutorial, so far so good. While doing it I wanted to create a "BUSY DOING WORK" splash screen, essentially I have a python function that for example updates an sqlite table with 10000 records. What I want to do is get CherryPy to display a busy.html page while the database is being updated, when the database operation completes I want to redirect the user back to the main.html page.
So far I have only come across
dev update_db(self):
#Code to update the database goes here
return "busy.html"<----show user that work is being done
#database has completed
CherryPy.redirect "main.html"
But return simply exits the function. Is there anyway of doing presenting the user with a temporary splashscreen, while the database is being updated then returning the user back to another page once its complete.
I suppose an alternative is to have a message flash across the top of the existing page, But I don't know if CherryPy has a flash message feature much like Flask.
IMHO, you can achieve this with generators and here is a link from latest (v3.8) cherrypy documentation. However, you should take into account the following issue in the docs
Streaming generators are sexy, but they play havoc with HTTP. CherryPy allows you to stream output for specific situations: pages which take many minutes to produce, or pages which need a portion of their content immediately output to the client. Because of the issues outlined above, it is usually better to flatten (buffer) content rather than stream content. Do otherwise only when the benefits of streaming outweigh the risks.
Generators have some limitations as written in the documentation
you cannot manually modify the status or headers within your page handler if that handler method is a streaming generator, because the method will not be iterated over until after the headers have been written to the client. This includes raising exceptions like HTTPError, NotFound, InternalRedirect and HTTPRedirect. To use a streaming generator while modifying headers, you would have to return a generator that is separate from (or embedded in) your page handler.
Because the headers have already been written to the client when streaming, raising redirection exception cannot help to redirect to different page after your long running task. If I were you, I would yield this
<meta http-equiv="refresh" content="0;URL='/anotherpage'" />
or this at the final yield
<script>window.location.href="/anotherpage";</script>
I coded and example for you. I hope this gives you an idea.
# encoding: utf-8
import time
import cherrypy
class Root(object):
#cherrypy.expose
def index(self):
content = '''<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("body").append("<p>jQuery Ready</p>");
setTimeout(function(){
$("body").html("Redirecting Please Wait...")
}, 2500);
});
</script>
</head>
<body>
<p>Page Content1</p>
<p>Page Content2</p>
<p>Page Content3</p>
<p>Page Content4</p>
<p>Page Content5</p>
<p>Page Content Final</p>
</body>
</html>
'''
for line in content.split("\n"):
yield line
time.sleep(1)
else:
yield '''<meta http-equiv="refresh" content="5;URL='/anotherpage'" />'''
index._cp_config = {'response.stream': True}
#cherrypy.expose
def anotherpage(self):
return "Another Page"
cherrypy.quickstart(Root(), "/")

Flask - how to get query string parameters into the route parameters

Im very much new to Flask, and one of the starting requirements is that i need SEO friendly urls.
I have a route, say
#app.route('/sales/')
#app.route(/sales/<address>)
def get_sales(addr):
# do some magic here
# render template of sales
and a simple GET form that submits an address.
<form action={{ url_for('get_sales') }}>
<input type='text' name='address'>
<input type=submit>
</form>
On form submission, the request goes to /sales/?address=somevalue and not to the standard route. What options do I have to have that form submit to /sales/somevalue ?
I feel like I'm missing something very basic.
You would need to use JavaScript to achieve this so your template would become:
<input type='text' id='address'>
<button onclick="sendUrl();">submit</button>
<script>
function sendUrl(){
window.location.assign("/sales/"+document.getElementById("address").value);
}
</script>
and your routes similar to before:
#app.route('/sales/')
#app.route('/sales/<address>')
def get_sales(address="Nowhere"):
# do some magic here
# render template of sales
return "The address is "+address
However, this is not the best way of doing this kind of thing. An alternative approach is to have flask serve data and use a single-page-application framework in javascript to deal with the routes from a user interface perspective.
There is a difference between the request made when the form is submitted and the response returned. Leave the query string as is, as that is the normal way to interact with a form. When you get a query, process it then redirect to the url you want to display to the user.
#app.route('/sales')
#app.route('/sales/<address>')
def sales(address=None):
if 'address' in request.args:
# process the address
return redirect(url_for('sales', address=address_url_value)
# address wasn't submitted, show form and address details
I'm not sure there's a way to access the query string like that. The route decorators only work on the base url (minus the query string)
If you want the address in your route handler then you can access it like this:
request.args.get('address', None)
and your route handler will look more like:
#pp.route('/sales')
def get_sales():
address = request.args.get('address', None)
But if I were to add my 2 cents, you may want to use POST as the method for your form posting. It makes it easier to semantically separate getting data from the Web server (GET) and sending data to the webserver (POST) :)

Csrf token verification fails between two Django web applications

I am trying to pass csrf token between two web application to make one POST data to the other.
"client" application (C) asks csrf token to "server" application (S) via a GET operation.
S responds to C with a form:
<form id='csrfRequestForm' name='csrfForm' action='http://{{ context_path }}/ajax/getcsrf' method='post'>
<!-- csrf token -->
{% csrf_token %}
<!-- datas to POST follow -->
...
</form>
C has to submit this form to action (mapped on a url used by S) in order to POST datas to S.
When C tries to do it, csrf verification fails. I've checked GET's result and csrf token is received with the form. I have django.middleware.csrf.CsrfViewMiddleware keyword listed under MIDDLEWARE CLASSES in settings.py and RequestContext is passed when rendering form's view with render_to_response(... RequestContext(request))
What am I doing wrong?
Thanks
Try defining your context and returning it like this...
context = RequestContext(request, {
'request': request
})
return render_to_response(..., context_instance=context)
This is by design, and disallows for cross site POST execution. One option you have is to mark the methods you would like to be able to execute as safe, as per the django docs:
https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
I wasn't able to resolve it in your way, but I managed out how to do it:
C go directly to S via javascript opening a popup with:
window.open("http://<S_address>/<path_to_request_form>");
In this way, user using C that is logged via a third party authentication server (I forgot to mention it earlier, sorry), is still logged in the popup window in S and receives the form in it with a correct csrf token. I don't know if it's correct but it works.
Thanks for your time

Unit Testing a Django Form Containing Multiple Submit Buttons

I am writing unit tests for a page that uses several Submit buttons to control logical flow through my Django application.
Unfortunately, I can't figure out how to get the response to return the submit values in the unit testing framework. The Django unit testing documentation for post indicates its form is the following:
post(path, data={}, content_type=MULTIPART_CONTENT, follow=False, **extra)
In the case of a Delete button of the form:
<input type="submit" name="delete" value="Delete" />
I've tried placing the Delete value in as data, i.e.:
response = self.client.post(url, {'name':'delete'}, follow=True)
but that doesn't seem to work. I need to have the name values in order to exercise the code paths that they trigger. In the views, the logic takes the form of:
if 'delete' in request.POST:
<do something>
I'm assuming that I make use of **extra somehow to get these values but I haven't had much luck with it either.
Any suggestions?
The data dictionary should map input names to values. In your case, the name is delete, and the value is Delete. So the dictionary should be:
{'delete': 'Delete'}

jquery-autocomplete does not work with my django app

I have a problem with the jquery-autocomplete pluging and my django script. I want an easy to use autocomplete plugin. And for what I see this (http://code.google.com/p/jquery-autocomplete/) one seems very usefull and easy. For the django part I use this (http://code.google.com/p/django-ajax-selects/) I modified it a little, because the out put looked a little bit weired to me. It had 2 '\n' for each new line, and there was no Content-Length Header in the response. First I thought this could be the problem, because all the online examples I found had them. But that was not the problem.
I have a very small test.html with the following body:
<body>
<form action="" method="post">
<p><label for="id_tag_list">Tag list:</label>
<input id="id_tag_list" name="tag_list" maxlength="200" type="text" /> </p>
<input type="submit" value="Submit" />
</form>
</body>
And this is the JQuery call to add autocomplete to the input.
function formatItem_tag_list(row) {
return row[2]
}
function formatResult_tag_list(row) {
return row[1]
}
$(document).ready(function(){
$("input[id='id_tag_list']").autocomplete({
url:'http://gladis.org/ajax/tag',
formatItem: formatItem_tag_list,
formatResult: formatResult_tag_list,
dataType:'text'
});
});
When I'm typing something inside the Textfield Firefox (firebug) and Chromium-browser indicates that ther is an ajax call but with no response. If I just copy the line into my browser, I can see the the response. (this issue is solved, it was a safety feature from ajax not to get data from another domain)
For example when I am typing Bi in the textfield, the url "http://gladis.org/ajax/tag?q=Bi&max... is generated. When you enter this in your browser you get this response:
4|Bier|Bier
43|Kolumbien|Kolumbien
33|Namibia|Namibia
Now my ajax call get the correct response, but there is still no list showing up with all the possible entries. I tried also to format the output, but this doesn't work either. I set brakepoints to the function and realized that they won't be called at all.
Here is a link to my minimum HTML file http://gladis.org/media/input.html
Has anybody an idea what i did wrong. I also uploaded all the files as a small zip at http://gladis.org/media/example.zip.
Thank you for your help!
[Edit]
here is the urls conf:
(r'^ajax/(?P<channel>[a-z]+)$', 'ajax_select.views.ajax_lookup'),
and the ajax lookup channel configuration
AJAX_LOOKUP_CHANNELS = {
# the simplest case, pass a DICT with the model and field to search against :
'tag' : dict(model='htags.Tag', search_field='text'),
}
and the view:
def ajax_lookup(request,channel):
""" this view supplies results for both foreign keys and many to many fields """
# it should come in as GET unless global $.ajaxSetup({type:"POST"}) has been set
# in which case we'll support POST
if request.method == "GET":
# we could also insist on an ajax request
if 'q' not in request.GET:
return HttpResponse('')
query = request.GET['q']
else:
if 'q' not in request.POST:
return HttpResponse('') # suspicious
query = request.POST['q']
lookup_channel = get_lookup(channel)
if query:
instances = lookup_channel.get_query(query,request)
else:
instances = []
results = []
for item in instances:
results.append(u"%s|%s|%s" % (item.pk,lookup_channel.format_item(item),lookup_channel.format_result(item)))
ret_string = "\n".join(results)
resp = HttpResponse(ret_string,mimetype="text/html")
resp['Content-Length'] = len(ret_string)
return resp
You probably need a trailing slash at the end of the URL.
Also, your jQuery selector is wrong. You don't need quotes within the square brackets. However, that selector is better written like this anyway:
$("input#id_tag_list")
or just
$("#id_tag_list")
Separate answer because I've just thought of another possibility: is your static page being served from the same domain as the Ajax call (gladis.org)? If not, the same-domain policy will prevent Ajax from being loaded.
As an aside, assuming your document.ready is in your Django template, it would be a good idea to utilize the {% url %} tag rather than hardcoding your URL.
$(document).ready(function(){
$("input[id='id_tag_list']").autocomplete({
url:'{% url my_tag_lookup %}',
dataType:'text'
});
});
This way the JS snippet will be rendered with the computed URL and your code will remain portable.
I found a solution, but well I still don't know why the first approach didn't worked out. I just switched to a different library. I choose http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/. This one is actually promoted by jQuery and it works ;)