Skip to content

Instantly share code, notes, and snippets.

@kyle-eshares
Created October 15, 2016 18:01
Show Gist options
  • Save kyle-eshares/5eaed8a5c299e5282d066a1fbc03152c to your computer and use it in GitHub Desktop.
Save kyle-eshares/5eaed8a5c299e5282d066a1fbc03152c to your computer and use it in GitHub Desktop.
Strict ForeignKeys
from __future__ import unicode_literals
from django.db import models
from django.db.models.fields.related_descriptors import ForwardManyToOneDescriptor # noqa
class RelationNotLoaded(Exception):
pass
class StrictForwardManyToOne(ForwardManyToOneDescriptor):
def __get__(self, instance, cls=None):
try:
return getattr(instance, self.cache_name)
except AttributeError:
raise RelationNotLoaded(
'Relation `{rel}` not loaded. Use `select_related` or '
'`fetch_{rel}`'.format(rel=self.field.name)
)
def explicit_get(self, instance, cls=None):
return super(StrictForwardManyToOne, self).__get__(instance, cls)
class StrictForeignKey(models.ForeignKey):
def contribute_to_class(self, cls, name, **kwargs):
super(StrictForeignKey, self).contribute_to_class(cls, name, **kwargs)
# Override the descriptor defined by ForeignObject
descriptor = StrictForwardManyToOne(self)
setattr(cls, self.name, descriptor)
# Add a method so you don't always have to use select_related
fetch_name = 'fetch_{rel}'.format(rel=self.name)
setattr(cls, fetch_name, lambda inst: descriptor.explicit_get(inst))
# Create your models here.
class Author(models.Model):
name = models.TextField()
class Book(models.Model):
title = models.TextField()
author = StrictForeignKey(Author, on_delete=models.PROTECT, related_name='books')
@fcurella
Copy link

Nice work @kyle-eshares!

I'd like to use this snippet for a client of mine. Would it be possible for you to license it?

@fjsj
Copy link

fjsj commented Nov 25, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment