如何优化Djiango-ORM
1.工具推荐
请摒弃简单粗暴的print
大家经常在windows中安装pycharm开发,项目部署在虚拟机中,在本地浏览器中查看效果,这种方式在调试上会有点麻烦,django-debug-toolbar 的出现,就解决了这个问题
使用步骤:
- 使用 pip install django-debug-toolbar安装django-debug-toolbar。(注意Django版本和debug_toolbar的版本兼容问题)
- 在项目根目录下创建好static目录,并在在setting中添加APPS
INSTALLED_APPS = [
'debug_toolbar',
]
STATIC_URL = '/static/'
- 在URL中添加如下配置
from django.conf import settings```python
from django.urls import include, path```python
from django.conf.urls import include, url # For django versions before 2.0```python
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
# For django versions before 2.0:
# url(r'^__debug__/', include(debug_toolbar.urls)),
- 在setting中添加 Middleware
MIDDLEWARE = [
# ...
'debug_toolbar.middleware.DebugToolbarMiddleware',
# ...
]
如果你的Django版本是2.0以前的版本则:
MIDDLEWARE_CLASSES = [
# ...
'debug_toolbar.middleware.DebugToolbarMiddleware',
# ...
]
- 在settings.py中添加INTERNAL_IPS = (‘127.0.0.1’,),(从哪些ip访问站点,显示debug_toolbar)
INTERNAL_IPS = ('127.0.0.1',)
- 请确保setting中DEBUG选项为true
- 添加DEBUG_TOOLBAR_PANELS选项
DEBUG_TOOLBAR_PANELS = [
'debug_toolbar.panels.versions.VersionsPanel',
'debug_toolbar.panels.timer.TimerPanel',
'debug_toolbar.panels.settings.SettingsPanel',
'debug_toolbar.panels.headers.HeadersPanel',
'debug_toolbar.panels.request.RequestPanel',
'debug_toolbar.panels.sql.SQLPanel',
'debug_toolbar.panels.staticfiles.StaticFilesPanel',
'debug_toolbar.panels.templates.TemplatesPanel',
'debug_toolbar.panels.cache.CachePanel',
'debug_toolbar.panels.signals.SignalsPanel',
'debug_toolbar.panels.logging.LoggingPanel',
'debug_toolbar.panels.redirects.RedirectsPanel',
]
最后
2.什么是懒加载
由于 Django 的 ORM 是惰性的,它只取出当前查询所需响应最小的数据。存在于外键和多对多关系时,ORM不检索关联对象的数据如果你调用关联对象时Django的ORM会再次查询数据库。因此如果用户量大的时候访问网站时势必会对数据库造成巨大压力!
有句话说的是:只要你在序列化中使用嵌套关系,你就在拿你的性能开玩笑!
问题根源
cmd:python manage.py shell #进入shell调试 或者ipython
from authorization.models import User
users=User.objects.all()
print(users.query) #打印sql语句 ,第一次查询user
user=users[0]
user_menu=user.menu.all()
print(user_menu.query) #第二次查询menu
3.预加载
如何解决Django-ORM中的多次查询呢?其实Django已经提供了方法—预加载
- select_related —预加载单个关联对象(ForeignKey)
- prefetch_related —预加载多个关联对象(ManyToManyField)
具体列子如下:
class CustomerSerializer(serializers.ModelSerializer):
orders = OrderSerializer(many=True, read_only=True)
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('orders')
return queryset
调用例子:
customer_qs = Customers.objects.all()
customer_qs = CustomerSerializer.setup_eager_loading(customer_qs) # Set up eager loading to avoid N+1 selects
post_data = CustomerSerializer(customer_qs, many=True).data
具体的代码实例
from django.db import models
from django.contrib.auth.models import User
from django.db import models
from django.contrib.auth.models import User
class Event(models.Model):
creator = models.ForeignKey(User)
name = models.TextField()
event_date = models.DateTimeField()
class Attendee(models.Model):
events = models.ManyToManyField(Event, related_name='attendees')
organization = models.ForeignKey(Organization, null=True)
class Organization(models.Model):
name = models.TextField()
预加载代码如下:
class EventSerializer(serializers.ModelSerializer):
creator = serializers.StringRelatedField()
attendees = AttendeeSerializer(many=True)
unaffiliated_attendees = AttendeeSerializer(many=True)
@staticmethod
def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """
# select_related for "to-one" relationships
queryset = queryset.select_related('creator')
# prefetch_related for "to-many" relationships
queryset = queryset.prefetch_related(
'attendees',
'attendees__organization')
# Prefetch for subsets of relationships
queryset = queryset.prefetch_related(
Prefetch('unaffiliated_attendees',
queryset=Attendee.objects.filter(organization__isnull=True))
)
return queryset
总结:以上是django中实现预加载功能,总之:任何时候你通过 ORM 查询嵌套关系时,你都应该考虑建立适合的预加载