允许ORM结构在属性访问时进行密封,以防止它们执行查询。
项目描述
Django应用程序,提供查询集密封功能,强制使用only()/defer()和select_related()/prefetch_related()。
安装
pip install django-seal
使用
# models.py
from django.db import models
from seal.models import SealableModel
class Location(SealableModel):
latitude = models.FloatField()
longitude = models.FloatField()
class SeaLion(SealableModel):
height = models.PositiveIntegerField()
weight = models.PositiveIntegerField()
location = models.ForeignKey(Location, models.CASCADE, null=True)
previous_locations = models.ManyToManyField(Location, related_name='previous_visitors')
默认情况下,在密封对象属性访问时将引发UnsealedAttributeAccess警告
>>> location = Location.objects.create(latitude=51.585474, longitude=156.634331)
>>> sealion = SeaLion.objects.create(height=1, weight=100, location=location)
>>> sealion.previous_locations.add(location)
>>> SeaLion.objects.only('height').seal().get().weight
UnsealedAttributeAccess:: Attempt to fetch deferred field "weight" on sealed <SeaLion instance>.
>>> SeaLion.objects.seal().get().location
UnsealedAttributeAccess: Attempt to fetch related field "location" on sealed <SeaLion instance>.
>>> SeaLion.objects.seal().get().previous_locations.all()
UnsealedAttributeAccess: Attempt to fetch many-to-many field "previous_locations" on sealed <SeaLion instance>.
您可以通过过滤警告将警告提升为异常。这在例如运行测试套件时断言没有执行未密封的属性访问非常有用。
>>> import warnings
>>> from seal.exceptions import UnsealedAttributeAccess
>>> warnings.filterwarnings('error', category=UnsealedAttributeAccess)
>>> SeaLion.objects.only('height').seal().get().weight
Traceback (most recent call last)
...
UnsealedAttributeAccess:: Attempt to fetch deferred field "weight" on sealed <SeaLion instance>.
>>> SeaLion.objects.seal().get().location
Traceback (most recent call last)
...
UnsealedAttributeAccess: Attempt to fetch related field "location" on sealed <SeaLion instance>.
>>> SeaLion.objects.seal().get().previous_locations.all()
Traceback (most recent call last)
...
UnsealedAttributeAccess: Attempt to fetch many-to-many field "previous_locations" on sealed <SeaLion instance>.
或者,您可以配置日志记录以捕获警告,将未密封的属性访问记录到py.warnings记录器,这是一种在不停机的情况下从生产日志中识别和解决未密封属性访问的好方法。
>>> import logging
>>> logging.captureWarnings(True)
可密封的管理器也可以在模型定义时自动密封,以避免必须系统地调用seal(),通过将seal=True传递给SealableModel子类、SealableManager和SealableQuerySet.as_manager。
from django.db import models
from seal.models import SealableManager, SealableModel, SealableQuerySet
class Location(SealableModel, seal=True):
latitude = models.FloatField()
longitude = models.FloatField()
class SeaLion(SealableModel):
height = models.PositiveIntegerField()
weight = models.PositiveIntegerField()
location = models.ForeignKey(Location, models.CASCADE, null=True)
previous_locations = models.ManyToManyField(Location, related_name='previous_visitors')
objects = SealableManager(seal=True)
others = SealableQuerySet.as_manager(seal=True)
开发
修改您的更改,然后通过tox运行测试
tox