最喜欢的Django技巧和功能?

308

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

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

13

这个网站很有趣,但是页面上的跳来跳去让我感到不舒服。 - rslite

12

在 Django 1.2+ 中,使用 .exists() 而不是评估整个 queryset 来检查是否返回任何结果,对于之前的版本则使用 .count()。

exists() 和 count() 都会清除 order by 子句并从数据库中检索单个整数。但是 exists() 总是返回 1,而 count() 可能返回更高的值,在这些值上将手动应用限制。has_result 用于 exists(),get_count 用于 count()。如果你想知道更多信息,请参考源代码。

由于它们都返回单个整数,因此没有模型实例化、加载模型属性到内存中或在你的数据库和应用程序之间传递大型 TextFields。

如果已经评估了查询,则 .count() 计算 len(cached_result),而 .exists() 计算 bool(cached_result)

效率低下 - 示例 1

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

不高效的 - 示例 2

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

高效 - 示例1

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

高效 - 示例 2

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

考虑到数据库交互是大多数 Django 应用的核心,即使像这样的小改进也会产生很大的影响。 - Filip Dupanović

12

如果您对模型进行更改

./manage.py dumpdata appname > appname_data.json  
./manage.py reset appname
django-admin.py loaddata appname_data.json

5
如果你独自工作,那么这样就可以了。但如果你要在团队中工作和/或有你不想手动修改的固定内容 - 那么请使用 South。这是处理模型修改的正确方法。 - Jakub Gocławski

11

使用 signals 动态添加访问方法。

我在 django-photologue 中看到了这种技术:对于任何添加的 Size 对象,post_init 信号将添加对应的方法到 Image 模型中。如果添加了一个名为“giant”的尺寸,获取大分辨率图片的方法将是 image.get_giant_url()

通过从 post_init 信号调用 add_accessor_methods 来生成这些方法:

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

请查看photologue.models的源代码以了解其在实际应用中的使用。


11

从settings.py中删除数据库访问信息

我在我的Django网站的settings.py文件中做了一件事情,即从/etc目录下的文件中加载数据库访问信息。这样,访问设置(数据库主机、端口、用户名、密码)可以针对每台机器进行不同的设置,并且像密码这样的敏感信息不会出现在我的项目仓库中。您可能希望通过使工作人员使用不同的用户名连接来以类似的方式限制对其的访问。

您还可以通过环境变量传递数据库连接信息,甚至只是一个配置文件的键或路径,并在settings.py中处理它。

例如,以下是如何获取我的数据库配置文件:

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

毋庸置疑,您需要确保除了数据库管理员和Django本身之外,任何用户都无法访问DB_CONFIG文件。 默认情况下,应将Django引用到开发人员自己的测试数据库中。可能还有一个更好的解决方案,可以使用ast模块而不是execfile,但我还没有进行研究。

我做的另一件事是使用不同的用户来处理DB管理员任务和其他所有任务。在我的manage.py中,我添加了以下前言:

# 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

配置文件位于/etc/django/db_regular.py,适用于只能访问Django数据库的用户,具有SELECT、INSERT、UPDATE和DELETE权限;而/etc/django/db_admin.py适用于具有这些权限以及CREATE、DROP、INDEX、ALTER和LOCK TABLES权限的用户。(命令migrate来自South。)这为我提供了一些保护,防止Django代码在运行时干扰我的模式,并限制了SQL注入攻击可能造成的损害(尽管您仍应检查和过滤所有用户输入)。

(复制自我的答案another question)


9

不要在本地主机上运行Django开发服务器,而应该在适当的网络接口上运行。例如:

python manage.py runserver 192.168.1.110:8000

或者
python manage.py runserver 0.0.0.0:8000

然后,您不仅可以轻松使用 Fiddler (http://www.fiddler2.com/fiddler2/) 或类似 HTTP Debugger (http://www.httpdebugger.com/) 的工具来检查您的 HTTP 标头,还可以从局域网中的其他计算机访问您的开发站点进行测试。

请确保受到防火墙的保护,尽管开发服务器是最小化和相对安全的。


"相对而言"是一个好词,一旦我向外界暴露了内置的静态资源服务并感到后悔。虽然从技术上讲它与devserver无关,但它在同一段代码中 - Django,所以我会将这种经验推广到devserver。 - Tomasz Zieliński

7

Django Debug Toolbar非常棒。虽然不是一个工具栏,但它可以在侧边栏中显示有关页面来源的各种信息 - 数据库查询,发送到模板的上下文变量,信号等等。


7

在自定义视图装饰器中使用wraps装饰器以保留视图的名称、模块和文档字符串。例如:

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

注意:如果作者没有定义__name__属性,则不适用于基于类的视图(那些具有__call__方法定义)。作为解决方法,请使用以下方法:

from django.utils.decorators import available_attrs
...
    @wraps(fun, assigned=available_attrs(fun))

6
使用“应用程序”文件夹来组织您的应用程序,而无需编辑PYTHONPATH
当我想要像这样组织我的文件夹时,这很方便:
apps/
    foo/
    bar/
site/
settings.py
urls.py

无需覆盖PYTHONPATH或在每个导入中添加应用程序,例如:

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

在你的settings.py文件中添加:
import os
import sys
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, os.path.join(PROJECT_ROOT, "apps"))

并且你已经准备好了 :-)

我在http://codespatter.com/2009/04/10/how-to-add-locations-to-python-path-for-reusable-django-apps/看到了这个。


5

使用Django模板呈现表单而非as_(ul|table|p)(),本文展示了如何使用模板来渲染自定义表单。

更改以下内容以使其正常工作:

  • from django import newforms as forms 更改为 from django import forms
  • from django.newforms.forms import BoundField 更改为 from django.forms.forms import BoundField

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