Package: django-seal
Django application providing queryset sealing capability to force appropriate usage ofonly()/defer()andselect_related()/prefetch_related().Installationpipinstalldjango-sealUsage# models.pyfromdjango.dbimportmodelsfromseal.modelsimportSealableModelclassLocation(SealableModel):latitude=models.FloatField()longitude=models.FloatField()classSeaLion(SealableModel):height=models.PositiveIntegerField()weight=models.PositiveIntegerField()location=models.ForeignKey(Location,models.CASCADE,null=True)previous_locations=models.ManyToManyField(Location,related_name='previous_visitors')By defaultUnsealedAttributeAccesswarnings will be raised on sealed objects attributes accesses>>>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().weightUnsealedAttributeAccess::Attempttofetchdeferredfield"weight"onsealed<SeaLioninstance>.>>>SeaLion.objects.seal().get().locationUnsealedAttributeAccess:Attempttofetchrelatedfield"location"onsealed<SeaLioninstance>.>>>SeaLion.objects.seal().get().previous_locations.all()UnsealedAttributeAccess:Attempttofetchmany-to-manyfield"previous_locations"onsealed<SeaLioninstance>.You canelevate the warnings to exceptions by filtering them. This is useful to assert no unsealed attribute accesses are
performed when running your test suite for example.>>>importwarnings>>>fromseal.exceptionsimportUnsealedAttributeAccess>>>warnings.filterwarnings('error',category=UnsealedAttributeAccess)>>>SeaLion.objects.only('height').seal().get().weightTraceback(mostrecentcalllast)...UnsealedAttributeAccess::Attempttofetchdeferredfield"weight"onsealed<SeaLioninstance>.>>>SeaLion.objects.seal().get().locationTraceback(mostrecentcalllast)...UnsealedAttributeAccess:Attempttofetchrelatedfield"location"onsealed<SeaLioninstance>.>>>SeaLion.objects.seal().get().previous_locations.all()Traceback(mostrecentcalllast)...UnsealedAttributeAccess:Attempttofetchmany-to-manyfield"previous_locations"onsealed<SeaLioninstance>.Or you canconfigure logging to capture warningsto log unsealed attribute accesses to thepy.warningslogger which is a
nice way to identify and address unsealed attributes accesses from production logs without taking your application down if some
instances happen to slip through your battery of tests.>>>importlogging>>>logging.captureWarnings(True)Sealable managers can also be automatically sealed at model definition time to avoid having to callseal()systematically
by passingseal=TruetoSealableModelsubclasses,SealableManagerandSealableQuerySet.as_manager.fromdjango.dbimportmodelsfromseal.modelsimportSealableManager,SealableModel,SealableQuerySetclassLocation(SealableModel,seal=True):latitude=models.FloatField()longitude=models.FloatField()classSeaLion(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)DevelopmentMake your changes, and then run tests via tox:tox