最喜欢的Django技巧和功能?

308

受到“Hidden features of…”问题系列的启发,我很想听听您所知道的Django技巧或较少人知但实用的功能。

  • 每个答案请仅包含一个技巧。
  • 如果有要求,请注明Django版本。
55个回答

221

我先给大家提供一个小技巧:)

在settings.py文件中使用os.path.dirname()来避免硬编码的目录名。

如果你想在不同的位置运行项目,就不要在settings.py中硬编码路径。如果你的模板和静态文件都位于Django项目目录内,请在settings.py中使用以下代码:

# 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"),
)

致谢:我从视频教程 'Django从零开始' 中获得了这个提示。


75
不应该对回答自己问题的人进行负评。这是受到鼓励的,即使事先已经确定了答案。 - Paolo Bergantino
19
这个想法非常好,我仍然很难理解为什么它不是默认设置。有多少人会在同一台机器上进行测试和部署? - SingleNegationElimination
19
这可以让你不再频繁地输入 os.path.join(),这种操作很快就会变得烦人:j = lambda filename: os.path.join(PROJECT_DIR, filename)。然后你只需要输入 j("static") - wr.
13
如果您使用的是Windows系统,请将反斜杠替换为正斜杠: os.path.join(PROJECT_DIR, "templates").replace('\','/') - Peter Mortensen
7
如果您真的希望在Django中解决这个问题,请在http://code.djangoproject.com/ticket/694上留言,要求核心开发人员重新考虑“不修复(wontfix)”的决定。 - sorin
显示剩余9条评论

128

很好,无法在Windows上正确安装pygraphviz,但仍然可以使用graphviz从dot文件转换。 - monkut
我喜欢与他人分享模型图,这绝对是一个加一的体验。 - BozoJoe
这个有SVG选项吗? - Benbob
输出图像现在似乎有问题了。 - Brian Wigginton

119

使用django-annoyingrender_to装饰器代替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))

编辑提示:返回一个HttpResponse(例如重定向)将会打破装饰器并且与你的预期一样工作。


4
@becomingGuru - 它会自动发生。 - Dominic Rodger
15
如果你同时返回一些 HttpResponseRedirect() 和 render_to_response(),那么情况就不一样了,重定向会失败。 - Matthew Schinckel
17
我不喜欢它。“ 明确胜于含蓄 ”。装饰器没有明确说明何时进行 render_to 。 - Tamás Szelei
2
@Matthew Schinckel 实际上它不会破坏重定向 - 如果您返回一个HttpResponse对象,它只是将其传递而不修改它。 - Jiaaro
20
我认为这种方法在Django 1.3中已经过时,可以查看django.shortcuts.render() http://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render - Dolph
显示剩余5条评论

101

我在我的网站模板中使用了一组自定义标签。为了实现自动加载(记得要DRY),我找到了以下方法:

from django import template
template.add_to_builtins('project.app.templatetags.custom_tag_module')
如果你将这个代码放在默认加载的模块中(例如你的主urlconf文件),你就可以在任何模板中使用来自你的自定义标签模块的标签和过滤器,而不需要使用 {% load custom_tag_module %}
传递给template.add_to_builtins()的参数可以是任何模块路径,你的自定义标签模块不必存在于特定应用程序中。例如,它也可以是项目根目录中的一个模块(例如'project.custom_tag_module')。

@Steef,你刚刚为我节省了大量的时间/烦恼/字节,谢谢。 - orokusaki
非常好,谢谢。 另外,一个自定义标签的存储库将是共享资源的好地方,你觉得呢? - Leandro Ardissone
这很棒,直到其他人需要维护你的代码。要考虑“最小魔法原则”。 - Rich

96

Virtualenv虚拟环境 + Python = 救命稻草,如果你正在开发多个Django项目,并且它们并不都依赖于相同版本的Django/应用程序。


3
你能否为使用Django的虚拟环境添加一些教程链接? - BozoJoe
2
@BozoJoe:在你的终端中执行以下命令:virtualenv myNewEnv --no-site-packages; . myNewEnv/bin/activate; pip install django; 就可以了! - SingleNegationElimination

87
不要硬编码你的URL!使用url名称,并使用reverse函数获取URL本身。在定义URL映射时,给你的URL命名。
urlpatterns += ('project.application.views'
   url( r'^something/$', 'view_function', name="url-name" ),
   ....
)

请确保每个URL的名称是唯一的。
我通常使用统一的格式"project-application-view",例如"cbx-forum-thread"用于帖子视图。 更新(无耻地窃取ayaz's addition):
此名称可以在模板中与url标签一起使用。

1
我完全同意这一点。我最初使用硬编码的URL,但在某个项目中,当我稍微改变了URL格式以适应一些更改时,它给我带来了麻烦。我花时间回去挖掘并替换了硬编码的URL。我唯一的大抱怨是URL标记错误会导致整个页面崩溃,而硬编码只会影响单个链接。 - ricree
21
这不应该是一个隐藏的功能,而是最佳实践和唯一的可行之路。 - Skylar Saveland
1
@skyl 这并不是“唯一的飞行方式”。我参加了 Django 开发冲刺,Adrian Holovaty(Django 的创始人之一)说他甚至不使用 url 标签... 他的立场是 URL 不应该改变(如果你想对用户友好)。 - TM.
你也可以在模板中使用它,例如 {% url path.to.view.name arg1 arg2 %} http://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#url - SingleNegationElimination
如果您使用jinja2,只需像这样添加'reverse':environment.filters ['url'] = django.core.urlresolvers.reverse,然后您可以在您的模板中像这样使用它:{{ 'view-name'|url(arg1, arg2)|e }}(需要“e”来转义一些字符以包含在HTML中)。 - SingleNegationElimination

81

使用Django调试工具栏。例如,它允许您查看在渲染视图时执行的所有SQL查询,并且您还可以查看任何查询的堆栈跟踪。


79

不要自己编写登录页面。 如果你正在使用django.contrib.auth。

真正的、肮脏的秘密是,如果你也在使用django.contrib.admin,并且django.template.loaders.app_directories.load_template_source在你的模板加载器中,那么你也可以免费获取你的模板!

# somewhere in urls.py
urlpatterns += patterns('django.contrib.auth',
    (r'^accounts/login/$','views.login', {'template_name': 'admin/login.html'}),
    (r'^accounts/logout/$','views.logout'),
)

1
太酷了!我不知道我们可以重复使用管理员登录页面。谢谢! - Joshua Partogi

66

上下文处理器非常棒。

假设您有一个不同的用户模型,并且您想在每个响应中包含它。而不是这样做:

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))

上下文处理能够让你向模板中传递任何变量。我一般会将它们放在'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='')

在您的settings.py文件中,将以下行添加到您的TEMPLATE_CONTEXT_PROCESSORS中。
TEMPLATE_CONTEXT_PROCESSORS = (
    'my_project.apps.core.context.my_context',
    ...
)

现在每次发出请求时,它会自动包含my_user键。
此外,signals也很重要。
几个月前,我写了一篇关于这个的博客文章,所以我只需要复制粘贴:
Django开箱即用地提供了几个非常有用的信号。您可以在保存、初始化、删除甚至处理请求时执行操作。因此,让我们远离概念,演示如何使用它们。假设我们有一个博客。
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)

你希望通知其中一个博客ping服务,告诉他们我们发布了新文章,重建最近的文章缓存,并发推特。使用信号(signal),你无需为Post类添加任何方法就可以完成以上所有操作。

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)

通过定义该函数并使用post_init信号将函数连接到Post model并在保存后执行它。

在这里,我们可以使用post_init信号来将函数与Post model连接起来,并在其保存后执行。

4
比较不同的Web框架时,Django的信号是我现在必备的功能。例如,编写一个松散耦合的论坛,它可以监听“签名”模块的更新,但并不需要该模块才能工作,并且还可以与实现相同功能的兼容模块一起使用,这非常棒。但是我不知道为什么信号不太出名和流行。 - Lee B
如果我们在项目中使用可重用的应用程序,信号非常重要,以避免紧密耦合和代码混乱。您为Django应用程序的松散耦合提供了很好的示例,给您点赞(+1)。 - Lukasz Korzybski
你知道信号是否是异步的吗? - Kedare
假设您有一个不同的用户模型,并且希望在每个响应中包含它。将用户放入会话中。这可以为每个请求节省一次数据库访问。 - jammon
信号调用是同步的。在我看来,某种异步作业机制更适合于例如在Twitter/Facebook等发布(即-rabbitmq),这样我们网站的用户不会因请求而挂起。 - gorsky

58

当我刚开始学习时,我不知道有一个分页器,请确保您知道它的存在!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接