Django Built-In Template Tags in Views

Django’s built-in template tags are simply Python functions which means they can be used in places other than templates such as class-based views.

Here’s an example using pluralize:


from django.views.generic import ListView
from django.template.defaultfilters import pluralize
from myapp.models.Product
class MyView(ListView):
queryset = Product.objects.all()
template_name = 'product.html'
context_object_name = 'products'
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['title'] = len(context['products']) + ' Product' + pluralize(context['products']) + ' Found'
return context

view raw

view.py

hosted with ❤ by GitHub

Microsoft QA Testing Resources

Microsoft continues to improve the tools available for testing IE (finally!) via modern.IE:

  • Free virtual machines (download test versions of IE 6 to 11 in VirtualBox, VMWare Fusion and Parellels formats)
  • Site scan (parses your site for common coding problems)
  • 3 months of free BrowserStack usage (their local testing option is pretty slick)
  • Compatibility reports (scans your site for code no longer supported in modern IE)
  • Screenshots (see how your site looks in 9 common browsers and devices)

The free BrowserStack access is a great deal and worth visiting just for that.

Django, Haystack and Elasticsearch – Part 1

I’m wrapping up a little side project at the moment (more on that very soon) which required full-text search, autocomplete, and a few other bits of search related functionality.

After some research I landed upon the combination of Elasticsearch and the awesome Django application Haystack.

First step was to get Elasticsearch up and running locally on OS X…

1) Download latest zip fromĀ http://www.elasticsearch.org/overview/elkdownloads/. A good spot is:

/opt/elasticsearch-1.1.x

2) Create the following directories:

/opt/elasticsearch-1.1.x/data
/opt/elasticsearch-1.1.x/work
/opt/elasticsearch-1.1.x/logs

3) Add the following to your .profile (allows you to run Elasticsearch from the command prompt without the full path):


# elasticsearch
export ES_HOME=/opt/elasticsearch-1.1.x
PATH=$ES_HOME/bin:$PATH

view raw

.profile

hosted with ❤ by GitHub

4) Update the following values in the Elasticsearch config file:


# /opt/elasticsearch-1.1.x/config/elasticsearch.yml
discovery.zen.ping.multicast.enabled: false
discovery.zen.ping.unicast.hosts: ["127.0.0.1"]
cluster.name: elasticsearch
network.host: 127.0.0.1
http.port: 9200
path.conf: /opt/elasticsearch-1.1.x/config
path.data: /opt/elasticsearch-1.1.x/data
path.work: /opt/elasticsearch-1.1.x/work
path.logs: /opt/elasticsearch-1.1.x/logs

5) Ensure all requirements are installed (django-haystack, pyelasticsearch, requests, simplejson):


pip install django-haystack
pip install pyelasticsearch

view raw

install.sh

hosted with ❤ by GitHub

6) You should now be able to start Elasticsearch:


$ elasticsearch
[2014-05-14 08:15:05,257][INFO ][node ] [Aminedi] version[1.1.1], pid[46224], build[f1585f0/2014-04-16T14:27:12Z]
[2014-05-14 08:15:05,258][INFO ][node ] [Aminedi] initializing …
[2014-05-14 08:15:05,271][INFO ][plugins ] [Aminedi] loaded [], sites []
[2014-05-14 08:15:07,136][INFO ][node ] [Aminedi] initialized
[2014-05-14 08:15:07,136][INFO ][node ] [Aminedi] starting …
[2014-05-14 08:15:07,211][INFO ][transport ] [Aminedi] bound_address {inet[/127.0.0.1:9300]}, publish_address {inet[/127.0.0.1:9300]}
[2014-05-14 08:15:10,262][INFO ][cluster.service ] [Aminedi] new_master [Aminedi][X4diAes4TrOMTk4eAdbhnA][mbp.home][inet[/127.0.0.1:9300]], reason: zen-disco-join (elected_as_master)
[2014-05-14 08:15:10,284][INFO ][discovery ] [Aminedi] elasticsearch/X4diAes4TrOMTk4eAdbhnA
[2014-05-14 08:15:10,298][INFO ][http ] [Aminedi] bound_address {inet[/127.0.0.1:9200]}, publish_address {inet[/127.0.0.1:9200]}
[2014-05-14 08:15:10,784][INFO ][gateway ] [Aminedi] recovered [1] indices into cluster_state
[2014-05-14 08:15:10,785][INFO ][node ] [Aminedi] started

7) Add Haystack to your Django config:


# add to installed apps
INSTALLED_APPS = (
'haystack',
)
# haystack search using elasticsearch
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:9200/',
'INDEX_NAME': 'haystack',
},
}
# http://django-haystack.readthedocs.org/en/latest/signal_processors.html
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# increase the default number of results (from 20)
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 40

view raw

config.py

hosted with ❤ by GitHub

8) After you’ve added your search indexes, you can use manage.py to rebuild the search index:

$ python manage.py rebuild_index

How To View Live MYSQL Queries

Here’s a simple trick for viewing MySQL queries as they hit your server – enable query logging to a file and then tail -f the file:


mysql> SHOW VARIABLES LIKE "general_log%";
+—————-+———————————–+
| Variable_name | Value |
+—————-+———————————–+
| general_log | OFF |
| general_log_file | /opt/local/var/db/mysql56/out.log |
+—————-+———————————–+
2 rows in set (0.00 sec)
mysql> SET GLOBAL general_log = 'ON';
Query OK, 0 rows affected (0.01 sec)

view raw

log_on.sql

hosted with ❤ by GitHub


tail -f /opt/local/var/db/mysql56/out.log

view raw

tail.sh

hosted with ❤ by GitHub

Just be sure to turn it off when you’re done:


mysql> SET GLOBAL general_log = 'OFF';

view raw

log_off.sql

hosted with ❤ by GitHub

Django Template Tag for Truncating Characters at a Word Boundary

The default truncatechars template tag truncates a string if it is longer than the specified number of characters but does so exactly at the character count, irrespective of whether it’s the middle of a word or not.

Here’s a smarter version that clips the text at the word boundary:


from django import template
register = template.Library()
# truncate chars but leaving last word complete
@register.filter(name='smarttruncatechars')
def smart_truncate_chars(value, max_length):
if len(value) > max_length:
# limits the number of characters in value to max_length (blunt cut)
truncd_val = value[:max_length]
# check if the next upcoming character after the limit is not a space,
# in which case it might be a word continuing
if value[max_length] != ' ':
# rfind will return the last index where matching the searched character,
# in this case we are looking for the last space
# then we only return the number of character up to that last space
truncd_val = truncd_val[:truncd_val.rfind(' ')]
return truncd_val + '…'
return value

Nginx, PHP5-FPM and Permission Denied Errors

After upgrading to Nginx 1.4.7 and PHP 5.4.28 (or 5.5.12), you may start seeing errors like the following:

2014/05/03 13:27:41 [crit] 4202#0: *1 connect() to unix:/var/run/php5-fpm.sock failed (13: Permission denied) while connecting to upstream, client: xx.xxx.xx.xx, server: localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "xx.xx.xx.xx"

The fix is to edit /etc/php5/fpm/pool.d/www.conf and set the listen.mode to 666 (be sure to uncomment the line as well):

listen.mode = 0666

How to Prevent iOS Standalone Mode Web Apps From Opening Links in Safari (CoffeeScript Version)

Apple conveniently let’s users add any site to their home screen as a standalone web application that acts much like a native application.

But there’s one problem – by default all links open in Safari rather than in the web application. Very annoying.

I found a few bits of code out there to workaround this issue, the best of which came from Stanislav Kostadinov. Since we use mostly CoffeeScript, here’s a CS version:


# based on http://stanislav.it/how-to-prevent-ios-standalone-mode-web-apps-from-opening-links-in-safari/
if window.navigator.standalone?
noddy = false
remotes = false
document.addEventListener 'click', (event) ->
noddy = event.target
while noddy.nodeName isnt 'A' and noddy.nodeName isnt 'HTML'
noddy = noddy.parentNode
if noddy.href? and !!~ noddy.href.indexOf('http') and (!!~ noddy.href.indexOf(document.location.host) || remotes)
event.preventDefault()
document.location.href = noddy.href
return
, false

LinkedIn API – Unable to Retrieve Access Token

UPDATE – LinkedIn has released a fix for this issue.


Ran into a fun little bug with the LinkedIn API this morning:

'{"error":"server_error","error_description":"the authorization server encountered an unexpected condition : Unable to retrieve access token"}'

Turns out that if a user revokes access to your application using their LinkedIn security settings, the API will throw this error if they ever try to re-authorize the application.

The only way to resolve the issue is to create a new application in the developer portal and use the new API keys. Not good, especially considering that LinkedIn generates unique user IDs for each application. So if you’re making use of the user ID in any sort of way, you’d have to figure out a way to reconcile the users (perhaps via their email address).

LinkedIn is aware of the issue and has said the fix is ‘a few weeks’ away. At least it’s not a few months…

On a side note – be sure to update your LinkedIn applications with OAuth2.0 redirect URLs.