308

Inspired by the question series 'Hidden features of ...', I am curious to hear about your favorite Django tips or lesser known but useful features you know of.

  • Please, include only one tip per answer.
  • Add Django version requirements if there are any.
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Haes
  • 12,891
  • 11
  • 46
  • 50

55 Answers55

221

I'm just going to start with a tip from myself :)

Use os.path.dirname() in settings.py to avoid hardcoded dirnames.

Don't hardcode path's in your settings.py if you want to run your project in different locations. Use the following code in settings.py if your templates and static files are located within the Django project directory:

# settings.py
import os
PROJECT_DIR = os.path.dirname(__file__)
...
STATIC_DOC_ROOT = os.path.join(PROJECT_DIR, "static")
...
TEMPLATE_DIRS = (
    os.path.join(PROJECT_DIR, "templates"),
)

Credits: I got this tip from the screencast 'Django From the Ground Up'.

Haes
  • 12,891
  • 11
  • 46
  • 50
  • Hmm, I never thought of that... usually I downvote people who answer their own questions, but this one is actually useful. – Powerlord Feb 17 '09 at 19:28
  • 75
    You shouldn't downvote people that answer their own questions. It is encouraged, even if it is pre-determined. – Paolo Bergantino Feb 18 '09 at 07:12
  • 19
    This is such a good idea that I still have a hard time understanding why it's not default. How many folks test and deploy on the same machine? – SingleNegationElimination Feb 19 '09 at 18:37
  • 1
    The leading slash in the 2nd argument to join() will cause those paths to be relative to the root. ie /static/ /home/user/static/ – Horn Mar 31 '09 at 14:44
  • I don't understand why this isn't a default. – Andrew Rimmer Apr 10 '09 at 14:36
  • 19
    This alleviates you from always type os.path.join() which gets annoying pretty fast: `j = lambda filename: os.path.join(PROJECT_DIR, filename)`. Then you just need to type `j("static")`. – wr. May 11 '09 at 07:33
  • 13
    If you are on Windows then replace the backslashes: os.path.join(PROJECT_DIR, "templates").replace('\\','/') – Peter Mortensen Jul 16 '09 at 12:44
  • 1
    @Dominic, "When specifying the path, always use forward slashes, even on Windows (e.g. C:/homes/user/mysite/sqlite3.db)." from here: http://docs.djangoproject.com/en/1.1/intro/tutorial01/#database-setup – Agnel Kurian Mar 18 '10 at 10:55
  • @Agnel - why? I do all my Django development on Windows (before deploying to a Linux box), and have never ever had a problem with just using the output of `os.path.join`. What problems might you expect me to see? – Dominic Rodger Mar 18 '10 at 11:49
  • @Dominic, I must confess that I'm a newbie to Django... it's just what I read in the tutorial. You may be right. – Agnel Kurian Mar 18 '10 at 13:49
  • 7
    If you really like to get this fixed in Django leave a comment to http://code.djangoproject.com/ticket/694 asking core devs to reconsider the `wontfix` decision. – sorin Jul 21 '10 at 18:42
  • 2
    you should import os and not os.path a per the python documentation. – Jorge Vargas Aug 24 '10 at 18:47
  • Note that if you have `django.template.loaders.app_directories.Loader` in your `TEMPLATE_LOADERS` setting, you don't really need to specific template_dirs at all. It will automatically find templates if they are located in `appname/templates` for each app. – TM. Feb 13 '11 at 08:06
  • 1
    Alternative: `this_dir = lambda *x: os.path.join(os.path.abspath(os.path.dirname(__file__)), *x)` –  Apr 28 '11 at 20:59
128

Install Django Command Extensions and pygraphviz and then issue the following command to get a really nice looking Django model visualization:

./manage.py graph_models -a -g -o my_project.png
Haes
  • 12,891
  • 11
  • 46
  • 50
119

Use django-annoying's render_to decorator instead of render_to_response.

@render_to('template.html')
def foo(request):
    bars = Bar.objects.all()
    if request.user.is_authenticated():
        return HttpResponseRedirect("/some/url/")
    else:
        return {'bars': bars}

# equals to
def foo(request):
    bars = Bar.objects.all()
    if request.user.is_authenticated():
        return HttpResponseRedirect("/some/url/")
    else:
        return render_to_response('template.html',
                              {'bars': bars},
                              context_instance=RequestContext(request))

Edited to point out that returning an HttpResponse (such as a redirect) will short circuit the decorator and work just as you expect.

Jiaaro
  • 74,485
  • 42
  • 169
  • 190
user20955
  • 2,552
  • 4
  • 24
  • 23
  • How do U pass the request context? – lprsd Jun 18 '09 at 07:27
  • 4
    @becomingGuru - it happens automatically. – Dominic Rodger Jun 19 '09 at 09:06
  • 15
    This is fine, unless you are returning some HttpResponseRedirect()s and some render_to_response()s. Then the redirects fail. – Matthew Schinckel Jul 17 '09 at 05:44
  • @becomingGuru the manual passing of request contexts is such a chore that one wonders why there was no builtin way to do it from the get-go. It would be surprising if the problem weren't automatically solved. – David Berger Nov 14 '09 at 22:22
  • Nice, they should add a direct_to decorator as well (equivalent to direct_to_response). – knipknap Jan 03 '10 at 22:46
  • 17
    I don't like it. "Explicit is better than implicit". The decorator is not telling when exactly it is going to render_to. – Tamás Szelei Jan 29 '10 at 17:38
  • Meh, I like the direct_to_template generic view better as a replacement for render_to_template in my views. – Stijn Debrouwere May 06 '10 at 22:24
  • 2
    @Matthew Schinckel it actually doesn't mess up redirects - if you return an HttpResponse object it just passes it along without modifying it – Jiaaro May 13 '10 at 18:52
  • 20
    I believe this approach is now redundant as of Django 1.3, see django.shortcuts.render() http://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render – Dolph Mar 16 '11 at 04:02
  • @Dolph: it's still not quite the same. `return render(request, template)` versus `@render_to(template`. See the difference? django-annoying's `render_to` is taking the `request` from the arguments, django's `render` requires it to be passed. – Chris Morgan Sep 05 '11 at 00:41
101

There's a set of custom tags I use all over my site's templates. Looking for a way to autoload it (DRY, remember?), I found the following:

from django import template
template.add_to_builtins('project.app.templatetags.custom_tag_module')

If you put this in a module that's loaded by default (your main urlconf for instance), you'll have the tags and filters from your custom tag module available in any template, without using {% load custom_tag_module %}.

The argument passed to template.add_to_builtins() can be any module path; your custom tag module doesn't have to live in a specific application. For example, it can also be a module in your project's root directory (eg. 'project.custom_tag_module').

Steef
  • 33,059
  • 4
  • 45
  • 36
96

Virtualenv + Python = life saver if you are working on multiple Django projects and there is a possibility that they all don't depend on the same version of Django/an application.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
phillc
  • 7,137
  • 1
  • 22
  • 15
87

Don't hard-code your URLs!

Use url names instead, and the reverse function to get the URL itself.

When you define your URL mappings, give names to your URLs.

urlpatterns += ('project.application.views'
   url( r'^something/$', 'view_function', name="url-name" ),
   ....
)

Make sure the name is unique per URL.

I usually have a consistent format "project-appplication-view", e.g. "cbx-forum-thread" for a thread view.

UPDATE (shamelessly stealing ayaz's addition):

This name can be used in templates with the url tag.

Community
  • 1
  • 1
hasen
  • 161,647
  • 65
  • 194
  • 231
  • 1
    I agree 100% on this one. I started out using hard coded urls, and it bit me on a project when I changed the url format around a bit to accommodate some changes. I took the time to go back and dig through everything and replace hard coded urls. My only big complaint is that url tag errors kill the whole page while hard coded only messes up the individual link. – ricree Sep 04 '09 at 08:34
  • 21
    This shouldn't be a hidden feature, this is best practice and the only way to fly. – Skylar Saveland Nov 01 '09 at 09:04
  • 1
    @skyl It's hardly "the only way to fly". I was at a Django dev sprint and Adrian Holovaty (one of the creators of Django) said he doesn't even use the `url` tag... His stance is that urls shouldn't be changing anyway (if you want to be friendly to your users). – TM. Jul 19 '10 at 03:55
  • you can use this in templates, too, as in `{% url path.to.view.name arg1 arg2 %}` http://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#url – SingleNegationElimination Oct 09 '10 at 06:29
  • If you use jinja2, just add `reverse` like this `environment.filters['url'] = django.core.urlresolvers.reverse` and you can use it in your templates like so: `{{ 'view-name'|url(arg1, arg2)|e }}` (the "e" is needed to escape some characters for inclusion in HTML) – SingleNegationElimination Oct 09 '10 at 06:34
81

Use django debug toolbar. For example, it allows to view all SQL queries performed while rendering view and you can also view stacktrace for any of them.

Dan Abramov
  • 264,556
  • 84
  • 409
  • 511
Eugene Morozov
  • 15,081
  • 3
  • 25
  • 32
79

Don't write your own login pages. If you're using django.contrib.auth.

The real, dirty secret is that if you're also using django.contrib.admin, and django.template.loaders.app_directories.load_template_source is in your template loaders, you can get your templates free too!

# somewhere in urls.py
urlpatterns += patterns('django.contrib.auth',
    (r'^accounts/login/$','views.login', {'template_name': 'admin/login.html'}),
    (r'^accounts/logout/$','views.logout'),
)
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
66

Context processors are awesome.

Say you have a different user model and you want to include that in every response. Instead of doing this:

def myview(request, arg, arg2=None, template='my/template.html'):
    ''' My view... '''
    response = dict()
    myuser = MyUser.objects.get(user=request.user)
    response['my_user'] = myuser
    ...
    return render_to_response(template,
                              response,
                              context_instance=RequestContext(request))

Context processes give you the ability to pass any variable to your templates. I typically put mine in 'my_project/apps/core/context.py:

def my_context(request):
    try:
        return dict(my_user=MyUser.objects.get(user=request.user))
    except ObjectNotFound:
        return dict(my_user='')

In your settings.py add the following line to your TEMPLATE_CONTEXT_PROCESSORS

TEMPLATE_CONTEXT_PROCESSORS = (
    'my_project.apps.core.context.my_context',
    ...
)

Now every time a request is made it includes the my_user key automatically.

Also signals win.

I wrote a blog post about this a few months ago so I'm just going to cut and paste:

Out of the box Django gives you several signals that are incredibly useful. You have the ability to do things pre and post save, init, delete, or even when a request is being processed. So lets get away from the concepts and demonstrate how these are used. Say we’ve got a blog

from django.utils.translation import ugettext_lazy as _
class Post(models.Model):
    title = models.CharField(_('title'), max_length=255)
    body = models.TextField(_('body'))
    created = models.DateTimeField(auto_now_add=True)

So somehow you want to notify one of the many blog-pinging services we’ve made a new post, rebuild the most recent posts cache, and tweet about it. Well with signals you have the ability to do all of this without having to add any methods to the Post class.

import twitter

from django.core.cache import cache
from django.db.models.signals import post_save
from django.conf import settings

def posted_blog(sender, created=None, instance=None, **kwargs):
    ''' Listens for a blog post to save and alerts some services. '''
    if (created and instance is not None):
        tweet = 'New blog post! %s' instance.title
        t = twitter.PostUpdate(settings.TWITTER_USER,
                               settings.TWITTER_PASSWD,
                               tweet)
        cache.set(instance.cache_key, instance, 60*5)
       # send pingbacks
       # ...
       # whatever else
    else:
        cache.delete(instance.cache_key)
post_save.connect(posted_blog, sender=Post)

There we go, by defining that function and using the post_init signal to connect the function to the Post model and execute it after it has been saved.

Stephen Fuhry
  • 12,624
  • 6
  • 56
  • 55
notzach
  • 861
  • 6
  • 5
  • 4
    Django's Signals are a must-have feature for me these days, when comparing web frameworks. Writing a loosely coupled forum, say, that can listen for, say, updates from a "signature" module, but not actually require that module to work, and that can also work with compatible modules implementing the same feature, is great. I don't know why signals aren't more well known and popular. – Lee B Nov 14 '09 at 22:34
  • Signals are very important to avoid tight coupling and code mess in general if we use some reusable apps in our project. You provided an excellent example for loose coupling of django apps, +1 for this. – Lukasz Korzybski May 20 '10 at 09:38
  • Do you know if signals are async ? – Kedare Jul 16 '10 at 23:21
  • "Say you have a different user model and you want to include that in every response." - Put the user into the [session](http://docs.djangoproject.com/en/1.2/topics/http/sessions/). That saves you a database hit for every request. – jammon Mar 03 '11 at 10:17
  • Signals' calls are synchronous. In my opinion, some sort of asynchronous job mechanism is more suitable for, say, publishing on Twitter/Facebook/etc (ie - rabbitmq), so users of out website don't hang on request. – gorsky Apr 05 '11 at 19:39
58

When I was starting out, I didn't know that there was a Paginator, make sure you know of its existence!!

hasen
  • 161,647
  • 65
  • 194
  • 231
46

Use IPython to jump into your code at any level and debug using the power of IPython. Once you have installed IPython just put this code in wherever you want to debug:

from IPython.Shell import IPShellEmbed; IPShellEmbed()()

Then, refresh the page, go to your runserver window and you will be in an interactive IPython window.

I have a snippet set up in TextMate so I just type ipshell and hit tab. I couldn't live without it.

sheats
  • 33,062
  • 15
  • 45
  • 44
43

Run a development SMTP server that will just output whatever is sent to it (if you don't want to actually install SMTP on your dev server.)

command line:

python -m smtpd -n -c DebuggingServer localhost:1025
Carl G
  • 17,394
  • 14
  • 91
  • 115
41

From the django-admin documentation:

If you use the Bash shell, consider installing the Django bash completion script, which lives in extras/django_bash_completion in the Django distribution. It enables tab-completion of django-admin.py and manage.py commands, so you can, for instance...

  • Type django-admin.py.
  • Press [TAB] to see all available options.
  • Type sql, then [TAB], to see all available options whose names start with sql.
John
  • 29,546
  • 11
  • 78
  • 79
40

The ./manage.py runserver_plus facilty which comes with django_extensions is truly awesome.

It creates an enhanced debug page that, amongst other things, uses the Werkzeug debugger to create interactive debugging consoles for each point in the stack (see screenshot). It also provides a very useful convenience debugging method dump() for displaying information about an object/frame.

enter image description here

To install, you can use pip:

pip install django_extensions
pip install Werkzeug

Then add 'django_extensions' to your INSTALLED_APPS tuple in settings.py and start the development server with the new extension:

./manage.py runserver_plus

This will change the way you debug.

DavidWinterbottom
  • 6,420
  • 5
  • 38
  • 39
37

I like to use the Python debugger pdb to debug Django projects.

This is a helpful link for learning how to use it: http://www.ferg.org/papers/debugging_in_python.html

Harold
  • 5,147
  • 1
  • 19
  • 15
  • 13
    This is a godsend. To give a little more info, just add this: "import pdb; pdb.set_trace()" on any line of your code. Refresh your page. It will hang. Now go to your terminal window where you are running the development server. It should now be a interactive shell where you can access all variables as they are at that point in your code where you pasted the debug code. – priestc Sep 01 '09 at 06:00
37

When trying to exchange data between Django and another application, request.raw_post_data is a good friend. Use it to receive and custom-process, say, XML data.

Documentation: http://docs.djangoproject.com/en/dev/ref/request-response/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chefsmart
  • 6,873
  • 9
  • 42
  • 47
36

Use Jinja2 alongside Django.

If you find the Django template language extremely restricting (like me!) then you don't have to be stuck with it. Django is flexible, and the template language is loosely coupled to the rest of the system, so just plug-in another template language and use it to render your http responses!

I use Jinja2, it's almost like a powered-up version of the django template language, it uses the same syntax, and allows you to use expressions in if statements! no more making a custom if-tags such as if_item_in_list! you can simply say %{ if item in list %}, or {% if object.field < 10 %}.

But that's not all; it has many more features to ease template creation, that I can't go though all of them in here.

hasen
  • 161,647
  • 65
  • 194
  • 231
  • I use and enjoy Jinja2 as well, but I've found that there are some couplings to the "contrib" applications. Particularly, the admin tool is pretty heavily tied to Django templates. Also, I had to recreate the login decorators in contrib.auth to be Jinja2 friendly, but not too hard. – Joe Holloway Feb 15 '09 at 21:27
  • 24
    Don't replace the template system with jinja2, just "add" it, don't remove the django templtes. Use Jinja2 for your own views, and let the admin interface continue to use the django template language. – hasen Feb 17 '09 at 19:26
  • 4
    I agree hartily with this. Django's limited syntax is tolerable, most of the time, but when you get to the point of making custom tags and find out just how hard that actually is, Jinja2 is a breath of fresh air – SingleNegationElimination Feb 18 '09 at 05:34
  • Also, if you want to do *any* metaprogramming on template source, Jinja2 is much more pleasant, since you can directly access the AST of parsed templates. Walking the AST makes tasks like finding out which templates extend a base template, or listing the unbound variables in a template source block, almost trivially easy. – rcoder Oct 13 '09 at 17:04
  • 5
    Thankfully in Django 1.2 the IF tag is alot smarter – Nixarn Jan 21 '10 at 09:47
  • Make sure to set TEMPLATE_DEBUG = False in settings.py. For some reason TEMPLATE_DEBUG == True gives you cryptic error messages. – Carl G Feb 14 '10 at 00:20
35

Add assert False in your view code to dump debug information.

zgoda
  • 12,775
  • 4
  • 37
  • 46
34

This adds to the reply above about Django URL names and reverse URL dispatching.

The URL names can also be effectively used within templates. For example, for a given URL pattern:

url(r'(?P<project_id>\d+)/team/$', 'project_team', name='project_team')

you can have the following in templates:

<a href="{% url project_team project.id %}">Team</a>
Community
  • 1
  • 1
ayaz
  • 10,406
  • 6
  • 33
  • 48
27

Since Django "views" only need to be callables that return an HttpResponse, you can easily create class-based views like those in Ruby on Rails and other frameworks.

There are several ways to create class-based views, here's my favorite:

from django import http

class RestView(object):
    methods = ('GET', 'HEAD')

    @classmethod
    def dispatch(cls, request, *args, **kwargs):
        resource = cls()
        if request.method.lower() not in (method.lower() for method in resource.methods):
            return http.HttpResponseNotAllowed(resource.methods)
        try:
            method = getattr(resource, request.method.lower())
        except AttributeError:
            raise Exception("View method `%s` does not exist." % request.method.lower())
        if not callable(method):
            raise Exception("View method `%s` is not callable." % request.method.lower())
        return method(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return http.HttpResponse()

    def head(self, request, *args, **kwargs):
        response = self.get(request, *args, **kwargs)
        response.content = ''
        return response

You can add all sorts of other stuff like conditional request handling and authorization in your base view.

Once you've got your views setup your urls.py will look something like this:

from django.conf.urls.defaults import *
from views import MyRestView

urlpatterns = patterns('',
    (r'^restview/', MyRestView.dispatch),
)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mmalone
  • 1,816
  • 16
  • 10
  • 2
    FWIW, the django authors actually use class-based views in a few places, e.g. contrib.formtools: http://code.djangoproject.com/browser/django/trunk/django/contrib/formtools/wizard.py – mazelife Nov 14 '09 at 22:21
  • 3
    If you add a __call__ method you could create a class called RestfulResource and then have your urls.py point to instances. – Stephen Paulger Dec 03 '09 at 10:00
  • 1
    New (Django 1.3?) generic views are class-based. – gorsky Apr 05 '11 at 19:43
21

Instead of using render_to_response to bind your context to a template and render it (which is what the Django docs usually show) use the generic view direct_to_template. It does the same thing that render_to_response does but it also automatically adds RequestContext to the template context, implicitly allowing context processors to be used. You can do this manually using render_to_response, but why bother? It's just another step to remember and another LOC. Besides making use of context processors, having RequestContext in your template allows you to do things like:

<a href="{{MEDIA_URL}}images/frog.jpg">A frog</a> 

which is very useful. In fact, +1 on generic views in general. The Django docs mostly show them as shortcuts for not even having a views.py file for simple apps, but you can also use them inside your own view functions:

from django.views.generic import simple

def article_detail(request, slug=None):
    article = get_object_or_404(Article, slug=slug)
    return simple.direct_to_template(request, 
        template="articles/article_detail.html",
        extra_context={'article': article}
    )
mazelife
  • 2,069
  • 17
  • 12
  • Save even more LOC by using the @render_to decorator available in django-annoying. http://bitbucket.org/offline/django-annoying/ – pithyless Mar 01 '10 at 21:37
  • 6
    ..or use new `render` shortcut method from Django 1.3 (http://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render) – gorsky Apr 05 '11 at 19:46
20

I don't have enough reputation to reply to the comment in question, but it's important to note that if you're going to use Jinja, it does NOT support the '-' character in template block names, while Django does. This caused me a lot of problems and wasted time trying to track down the very obscure error message it generated.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user61000
  • 540
  • 5
  • 10
  • One note that may or may not be applicable to "obscure error messages from jinja". Make sure to set TEMPLATE_DEBUG = False in settings.py. For some reason this will give you meaningful errors from Jinja templates. – Carl G Feb 14 '10 at 00:19
19

Everybody knows there is a development server you can run with "manage.py runserver", but did you know that there is a development view for serving static files (CSS / JS / IMG) as well ?

Newcomers are always puzzled because Django doesn't come with any way to serve static files. This is because the dev team think it is the job for a real life Web server.

But when developing, you may not want to set up Apache + mod_wisgi, it's heavy. Then you can just add the following to urls.py:

(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
        {'document_root': '/path/to/media'}),

Your CSS / JS / IMG will be available at www.yoursite.com/site_media/.

Of course, don't use it in a production environment.

Bite code
  • 578,959
  • 113
  • 301
  • 329
  • 6
    I use this in dev mode, and just to make sure I don't forget to turn this off in production, I wrap that URL rule in a DEBUG only conditional. – sghael Nov 14 '10 at 02:22
19

django.db.models.get_model does allow you to retrieve a model without importing it.

James shows how handy it can be: "Django tips: Write better template tags — Iteration 4 ".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
19

The webdesign app is very useful when starting to design your website. Once imported, you can add this to generate sample text:

{% load webdesign %}
{% lorem 5 p %}
Ross Light
  • 4,769
  • 1
  • 26
  • 37
18

I learned this one from the documentation for the sorl-thumbnails app. You can use the "as" keyword in template tags to use the results of the call elsewhere in your template.

For example:

{% url image-processor uid as img_src %}
<img src="{% thumbnail img_src 100x100 %}"/>

This is mentioned in passing in the Django templatetag documentation, but in reference to loops only. They don't call out that you can use this elsewhere (anywhere?) as well.

  • 7
    If a keyword as "as" can be used with a template-tag depends of this particular tag. It is not defined by django itself but by single tags, depending on their meaning. Have a look in the mentioned url-tag to see how "as" is used: http://code.djangoproject.com/browser/django/trunk/django/template/defaulttags.py – vikingosegundo Sep 07 '09 at 01:51
16

PyCharm IDE is a nice environment to code and especially debug, with built-in support for Django.

Art
  • 23,747
  • 29
  • 89
  • 101
16

django.views.generic.list_detail.object_list -- It provides all the logic & template variables for pagination (one of those I've-written-that-a-thousand-times-now drudgeries). Wrapping it allows for any logic you need. This gem has saved me many hours of debugging off-by-one errors in my "Search Results" pages and makes the view code cleaner in the process.

jds
  • 861
  • 6
  • 14
  • 1
    You can find the new version of the book's chapter on Generic Views on http://www.djangobook.com/en/2.0/chapter11/ . The one on the comment goes to the Django pre-1.0 version of the book (Django book 1.0) – Esteban Küber May 06 '09 at 20:54
14

Use xml_models to create Django models that use an XML REST API backend (instead of a SQL one). This is very useful especially when modelling third party APIs - you get all the same QuerySet syntax that you're used to. You can install it from PyPI.

XML from an API:

<profile id=4>
    <email>joe@example.com</email>
    <first_name>Joe</first_name>
    <last_name>Example</last_name>
    <date_of_birth>1975-05-15</date_of_birth>
</profile>

And now in python:

class Profile(xml_models.Model):
    user_id = xml_models.IntField(xpath='/profile/@id')
    email = xml_models.CharField(xpath='/profile/email')
    first = xml_models.CharField(xpath='/profile/first_name')
    last = xml_models.CharField(xpath='/profile/last_name')
    birthday = xml_models.DateField(xpath='/profile/date_of_birth')

    finders = {
        (user_id,):  settings.API_URL +'/api/v1/profile/userid/%s',
        (email,):  settings.API_URL +'/api/v1/profile/email/%s',
    }

profile = Profile.objects.get(user_id=4)
print profile.email
# would print 'joe@example.com'

It can also handle relationships and collections. We use it every day in heavily used production code, so even though it's beta it's very usable. It also has a good set of stubs that you can use in your tests.

(Disclaimer: while I'm not the author of this library, I am now a committer, having made a few minor commits)

godswearhats
  • 2,155
  • 1
  • 14
  • 6
14

Use database migrations. Use South.

vad
  • 1,196
  • 9
  • 22
13

Just found this link: http://lincolnloop.com/django-best-practices/#table-of-contents - "Django Best Practices".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nanda Kishore
  • 2,789
  • 5
  • 38
  • 61
12

Instead of evaluating whole queryset to check whether you got back any results, use .exists() in Django 1.2+ and .count() for previous versions.

Both exists() and count() clears order by clauses and retrieves a single integer from DB. However exists() will always return 1 where as count may return higher values on which limits will be applied manually. Source for has_result used in exists() and get_count used in count() for the curious.

Since they both return a single integer, there's no model instantiation, loading model attributes in memory and no large TextFields being passed between your DB and app.

If you have already evaluated the query, .count() computes len(cached_result) and .exists() computes bool(cached_result)

Not efficient - Example 1

books = Books.objects.filter(author__last_name='Brown')
if books:
    # Do something

Not efficient - Example 2

books = Books.objects.filter(author__last_name='Brown')
if len(books):
    # Do something

Efficient - Example 1

books = Books.objects.filter(author__last_name='Brown')
if books.count():
    # Do something

Efficient - Example 2

books = Books.objects.filter(author__last_name='Brown')
if books.exists():
    # Do something
tarequeh
  • 1,799
  • 18
  • 18
12

If you make changes into model

./manage.py dumpdata appname > appname_data.json  
./manage.py reset appname
django-admin.py loaddata appname_data.json
Ezequiel Marquez
  • 1,148
  • 16
  • 26
  • 5
    It will do if you work alone. But if you work in a team and/or have fixtures you don't want to modify by hand - then use [South](http://pypi.python.org/pypi/South). It's the proper way to handle model modifications. – Jakub Gocławski Jun 24 '11 at 20:57
11

Use signals to add accessor-methods on-the-fly.

I saw this technique in django-photologue: For any Size object added, the post_init signal will add the corresponding methods to the Image model. If you add a site giant, the methods to retrieve the picture in giant resolution will be image.get_giant_url().

The methods are generated by calling add_accessor_methods from the post_init signal:

def add_accessor_methods(self, *args, **kwargs):
    for size in PhotoSizeCache().sizes.keys():
        setattr(self, 'get_%s_size' % size,
                curry(self._get_SIZE_size, size=size))
        setattr(self, 'get_%s_photosize' % size,
                curry(self._get_SIZE_photosize, size=size))
        setattr(self, 'get_%s_url' % size,
                curry(self._get_SIZE_url, size=size))
        setattr(self, 'get_%s_filename' % size,
                curry(self._get_SIZE_filename, size=size))

See the source code of photologue.models for real-world usage.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
11

Remove Database Access Information from settings.py

One thing I've done in my Django site's settings.py is load database access info from a file in /etc. This way the access setup (database host, port, username, password) can be different for each machine, and sensitive info like the password isn't in my project's repository. You might want to restrict access to the workers in a similar manner, by making them connect with a different username.

You could also pass in the database connection information, or even just a key or path to a configuration file, via environment variables, and handle it in settings.py.

For example, here's how I pull in my database configuration file:

g = {}
dbSetup = {}
execfile(os.environ['DB_CONFIG'], g, dbSetup)
if 'databases' in dbSetup:
    DATABASES = dbSetup['databases']
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            # ...
        }
    }

Needless to say, you need to make sure that the file in DB_CONFIG is not accessible to any user besides the db admins and Django itself. The default case should refer Django to a developer's own test database. There may also be a better solution using the ast module instead of execfile, but I haven't researched it yet.

Another thing I do is use separate users for DB admin tasks vs. everything else. In my manage.py, I added the following preamble:

# Find a database configuration, if there is one, and set it in the environment.
adminDBConfFile = '/etc/django/db_admin.py'
dbConfFile = '/etc/django/db_regular.py'
import sys
import os
def goodFile(path):
    return os.path.isfile(path) and os.access(path, os.R_OK)
if len(sys.argv) >= 2 and sys.argv[1] in ["syncdb", "dbshell", "migrate"] \
    and goodFile(adminDBConfFile):
    os.environ['DB_CONFIG'] = adminDBConfFile
elif goodFile(dbConfFile):
    os.environ['DB_CONFIG'] = dbConfFile

Where the config in /etc/django/db_regular.py is for a user with access to only the Django database with SELECT, INSERT, UPDATE, and DELETE, and /etc/django/db_admin.py is for a user with these permissions plus CREATE, DROP, INDEX, ALTER, and LOCK TABLES. (The migrate command is from South.) This gives me some protection from Django code messing with my schema at runtime, and it limits the damage an SQL injection attack can cause (though you should still check and filter all user input).

(Copied from my answer to another question)

Community
  • 1
  • 1
Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
9

Instead of running the Django dev server on localhost, run it on a proper network interface. For example:

python manage.py runserver 192.168.1.110:8000

or

python manage.py runserver 0.0.0.0:8000

Then you can not only easily use Fiddler (http://www.fiddler2.com/fiddler2/) or another tool like HTTP Debugger (http://www.httpdebugger.com/) to inspect your HTTP headers, but you can also access your dev site from other machines on your LAN to test.

Make sure you are protected by a firewall though, although the dev server is minimal and relatively safe.

vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
chefsmart
  • 6,873
  • 9
  • 42
  • 47
  • "Relatively" is a good word, once I exposed the built-in static assets serving to the outer word and regret it. While technically it's not devserver-related, it's within the same piece of code - Django, so I would extrapolate this experience to the devserver as well. – Tomasz Zieliński Sep 09 '10 at 08:47
7

Use wraps decorator in custom views decorators to preserve view's name, module and docstring. E.g.

try:
    from functools import wraps
except ImportError:
    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.

def view_decorator(fun):
    @wraps(fun)
    def wrapper():
        # here goes your decorator's code
    return wrapper

Beware: will not work on a class-based views (those with __call__ method definition), if the author hasn't defined a __name__ property. As a workaround use:

from django.utils.decorators import available_attrs
...
    @wraps(fun, assigned=available_attrs(fun))
trybik
  • 482
  • 6
  • 16
7

The Django Debug Toolbar is really fantastic. Not really a toolbar, it actually brings up a sidepane that tells you all sorts of information about what brought you the page you're looking at - DB queries, the context variables sent to the template, signals, and more.

Mark Snidovich
  • 1,055
  • 7
  • 11
6

Using an 'apps' folder to organize your applications without editing PYTHONPATH

This has come handy when I want to organize my folders like this:

apps/
    foo/
    bar/
site/
settings.py
urls.py

without overwritting PYTHONPATH or having to add apps to every import like:

from apps.foo.model import *
from apps.bar.forms import *

In your settings.py add

import os
import sys
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(PROJECT_ROOT, "apps"))

and you are ready to go :-)

I saw this at http://codespatter.com/2009/04/10/how-to-add-locations-to-python-path-for-reusable-django-apps/

Javi Romero
  • 327
  • 5
  • 14
5

Use djangorecipe to manage your project

  • If you're writing a new app, this recipe makes testing it outside of a project really easy
  • It allows you to manage dependencies for a project (e.g. what version of some app it should depend on)

All you have to do to get started is this:

  1. Create a folder for your new website (or library)
  2. Create a buildout.cfg with following content in it:

    
    [buildout]
    parts=django
    
    [django]
    recipe=djangorecipe
    version=1.1.1
    project=my_new_site
    settings=development
    
  3. Grab a bootstrap.py to get a local installation of buildout and place it within your directory. You can either go with the official one (sorry, Markdown didn't like part of the full link :-/ ) or with one that uses distribute instead of setuptools as described by Reinout van Rees.
  4. python bootstrap.py (or python bootstrap_dev.py if you want to use distribute).
  5. ./bin/buildout

That's it. You should now have a new folder "my_new_site", which is your new django 1.1.1 project, and in ./bin you will find the django-script which replaces the manage.py on a normal installation.

What's the benefit? Let's say you want to use something like django-comment-spamfighter in your project. All you'd have to do is change your buildout.cfg to something like this:


[buildout]
parts=django

[django]
recipe=djangorecipe
version=1.1.1
project=my_new_site
settings=development
eggs=
    django-comments-spamfighter==0.4

Note that all I did was to add the last 2 lines which say, that the django-part should also have the django-comments-spamfighter package in version 0.4. The next time you run ./bin/buildout, buildout will download that package and modify ./bin/django to add it to its PYTHONPATH.

djangorecipe is also suited for deploying your project with mod_wsgi. Just add the wsgi=true setting to the django-part of your buildout.cfg and a "django.wsgi" will appear in your ./bin folder :-)

And if you set the test option to a list of applications, the djangorecipe will create a nice wrapper for you that runs all the tests for the listed application in your project.

If you want to develop a single app in a standalone environment for debugging etc., Jakob Kaplan-Moss has a quite complete tutorial on his blog

Jeff
  • 3,252
  • 3
  • 23
  • 13
Horst Gutmann
  • 10,910
  • 2
  • 28
  • 31
5

Use reverse in your urlconf.

This is one of those tricks where I don't understand why it isn't the default.

Here's a link to where I picked it up: http://andr.in/2009/11/21/calling-reverse-in-django/

Here's the code snippet:

from django.conf.urls.defaults import *
from django.core.urlresolvers import reverse
from django.utils.functional import lazy
from django.http import HttpResponse

reverse_lazy = lazy(reverse, str)

urlpatterns = patterns('',
url(r'^comehere/', lambda request: HttpResponse('Welcome!'), name='comehere'),
url(r'^$', 'django.views.generic.simple.redirect_to',
{'url': reverse_lazy('comehere')}, name='root')
)
jfenwick
  • 1,319
  • 15
  • 17
  • 4
    What does that mean and why would I want to do it? – Dominic Rodger Jul 16 '10 at 09:04
  • 1
    Reverse is like the URL tag, which "Returns an absolute URL (i.e., a URL without the domain name) matching a given view function and optional parameters." I avoid harding code absolute URLs. When I go to deploy on a web server, the app may not be the root mount point, so in the test server a URL like /app/url/ might work, but on the deploy server it might be /mountpoint/app/url. Reverse works in views.py. But it doesn't work in urls.py. It gives the error: "The included urlconf foo.urls doesn’t have any patterns in it". This trick saves you from needing to wrap the URLconf in a view. – jfenwick Jul 16 '10 at 14:25
  • 2
    -1, this is a horrible hack to solve the root url problem. `PythonOption django.root` is the correct way. See the relevant documentation here: http://docs.djangoproject.com/en/1.2/howto/deployment/modpython/#basic-configuration – Agos Dec 01 '10 at 12:39
5

Automatically set 'DEBUG' attribute on production environment (settings.py)

import socket

if socket.gethostname() == 'productionserver.com':
    DEBUG = False
else:
    DEBUG = True

By: http://nicksergeant.com/2008/automatically-setting-debug-in-your-django-app-based-on-server-hostname/

Ahmet Recep Navruz
  • 713
  • 11
  • 14
5

Render form via django template instead of as_(ul|table|p)().

This article shows, how to use template to render CusstomForms instead of as_p(), as_table()...

To make it work change

  • from django import newforms as forms to from django import forms
  • from django.newforms.forms import BoundField to from django.forms.forms import BoundField
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
4

This is a really easy way to never have to import another one of your models again in your python shell.

First, install IPython (If you don't use IPython, what's wrong with you?). Next, create a python script, ipythonrc.py, in your django project directory with the following code in it:

from django.db.models.loading import get_models 
for m in get_models(): 
     globals()[m.__name__] = m 
#NOTE: if you have two models with the same name you'll only end up with one of them

Then, in your ~/.ipython/ipythonrc file, put the following code in the "Python files to load and execute" section:

execfile /path/to/project/ipythonrc.py

Now every time you start up IPython or run ./manage.py shell you will have all your models already imported and ready to use. No need to ever import another model again.

You can also put any other code you execute a lot in your ipythonrc.py file to save yourself time.

sheats
  • 33,062
  • 15
  • 45
  • 44
  • 7
    consider using "django command extensions" which which adds the ./manage.py shell_plus command which does this for you (several other cool stuff, too.) – Carl G Feb 14 '10 at 06:24
4

Changing Django form field properties on init

Sometimes it's useful to pass extra arguments to a Form class.

from django import forms
from mymodels import Group

class MyForm(forms.Form):
    group=forms.ModelChoiceField(queryset=None)
    email=forms.EmailField()
    some_choices=forms.ChoiceField()


    def __init__(self,my_var,*args,**kwrds):
        super(MyForm,self).__init__(*args,**kwrds)
        self.fields['group'].queryset=Group.objects.filter(...)
        self.fields['email'].widget.attrs['size']='50'
        self.fields['some_choices']=[[x,x] for x in list_of_stuff]

source: Dzone snippets

jbochi
  • 28,816
  • 16
  • 73
  • 90
  • Brilliant! I've just been wrapping my Form classes in functions which doesn't seem as Pythonic as this. – Spike Mar 10 '11 at 22:33
2

django_extensions from https://github.com/django-extensions/django-extensions is just great.

Few nice ./manage.py commands:

  • shell_plus - autoimports models from all INSTALLED_APPS
  • show_urls - prints all urls defined in all apps in project
  • runscript - runs any script in project context (you can use models and other Django-related modules)
gorsky
  • 2,282
  • 1
  • 16
  • 22
2

PyCharm and Wingware IDE is great tool if you have money to pay for the license.

Since I am a poor developer, I use PyDev with Eclipse.

kelvinfix
  • 2,915
  • 8
  • 37
  • 49
1

Create dynamic models for sets of legacy tables with the same structure:

class BaseStructure(models.Model):
    name = models.CharField(max_length=100)
    address = models.CharField(max_length=100)

    class Meta:
        abstract=True

class DynamicTable(models.Model):
    table_name = models.CharField(max_length=20)

    def get_model(self):
        class Meta:
            managed=False
            table_name=self.table_name

        attrs = {}
        attrs['Meta'] = Meta

        # type(new_class_name, (base,classes), {extra: attributes})
        dynamic_class = type(self.table_name, (BaseStructure,), attrs) 
        return dynamic_class

customers = DynamicTable.objects.get(table_name='Customers').get_model()
me = customers.objects.get(name='Josh Smeaton')
me.address = 'Over the rainbow'
me.save()

This assumes that you have legacy tables with the same structure. Instead of creating a model to wrap each of the tables, you define one base model, and dynamically construct the class needed to interact with a specific table.

Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
1

A bit late to the party. But Django Canvas has recently come out and it deserves a place here.

Don't start your project with django-admin.py startproject. Instead you can use something like Django Canvas to help piece together a blank project with the modules you need.

You go to that site, tick some options and then download a blank project, so simple.

It has all the common things like South schema migrations and Command Extensions as well as a lot of other best practices mentioned here. Plus it has a great start.sh/shart.bat script that will install python, virtualenv, pip, django and whatever you need to start from a fresh copy of windows, osx or linux.

Benbob
  • 13,876
  • 18
  • 79
  • 114
1

Use asynchronous tasks. Use Celery

vad
  • 1,196
  • 9
  • 22
1

Read Unbreaking Django if you haven't already. It contains lots of useful information regarding django pitfalls.

tback
  • 11,138
  • 7
  • 47
  • 71
1

Use isapi-wsgi and django-pyodbc to run Django on Windows using IIS and SQL Server!

Jason Baker
  • 192,085
  • 135
  • 376
  • 510
0

When passing variables from a view to a template the response dictionary can become tedious to type out. I find it nice to just pass all the local variables at once using locals() .

def show_thing(request, thing_id):
    thing = Thing.objects.get(pk=thing_id)
    return render_to_response('templates/things/show.html', locals())

(Not a hidden feature per se but nevertheless helpful when new to Python and or Django.)

Edit: Obviously it's better to be explicit than implicit but this approach can be helpful during development.

droidballoon
  • 720
  • 10
  • 17
0

dir() & raise ValueError()

For debugging / exploring the state of things during development, I use the following trick:

...
  to_see = dir(inspect_this_thing)
  to_see2 = inspect_this_thing.some_attribute
  raise ValueError("Debugging")
...

This is especially helpful when you're working on parts of django that aren't particularly well documented (form.changed_fields is one I used this on recently).

locals().

Instead of writing out every variable for the template context, use the python builtin locals() command which creates a dictionary for you:

#This is tedious and not very DRY
return render_to_response('template.html', {"var1": var1, "var2":var2}, context_instance=RequestContext(request))

#95% of the time this works perfectly
return render_to_response('template.html', locals(), context_instance=RequestContext(request))

#The other 4.99%
render_dict = locals()
render_dict['also_needs'] = "this value"
return render_to_response('template.html', render_dict, context_instance=RequestContext(request))
Ted
  • 12,122
  • 4
  • 31
  • 39
0

Django hasn't got app settings, so i made my own app_settings.py detection. At the bottom of the settings.py i added this code:

import sys, os
# Append application settings without triggering the __init__.
for installed_app in INSTALLED_APPS:
    # Ignore django applications
    if not installed_app.startswith('django.'):
        # Find the app (and the settings file)
        for path in sys.path:
            path = os.path.join(path, installed_app, 'app_settings.py')
            if os.path.isfile(path):
                # Application settings found
                exec open(path).read()

It detects app_settings.py in all the INSTALLED_APPS. Instead of importing it, it will read the contents of the app_settings file and will execute it inline. If app_settings is imported directly all sort of Django import errors will be raised (because Django isn't initialized yet).

So my app/app_settings.py will look like this:

MIDDLEWARE_CLASSES += (
    'app.middleware.FancyMiddleware',
)

Now the application only has to be added to the INSTALLED_APPS, instead of finding all application settings and add them to the settings.py (middleware, urls...)

Note: It would be better if Django had a hook to append extra settings, so application settings could be added on startup (or in runtime).

Willian
  • 2,385
  • 15
  • 17