Skip to content

Instantly share code, notes, and snippets.

@nemesifier
Last active March 1, 2021 04:34
Show Gist options
  • Save nemesifier/8132696 to your computer and use it in GitHub Desktop.
Save nemesifier/8132696 to your computer and use it in GitHub Desktop.
Django Rest Framework Dynamic relationships mixin
# USAGE EXAMPLE:
# https://github.com/nemesisdesign/nodeshot/blob/09d5307fa38861339a660ba96b2c79f9c19ec92a/nodeshot/core/layers/models/__init__.py
from django.core.urlresolvers import NoReverseMatch
from rest_framework import serializers
from rest_framework.fields import Field
from rest_framework.reverse import reverse
class DynamicRelationshipsMixin(object):
"""
Django Rest Framework Serializer Mixin
which adds the possibility to dynamically add relationships to a serializer.
To add a relationship, use the class method "add_relationship", this way:
>>> SerializerName.add_relationship('relationship_name', 'view_name', 'lookup_field')
for example:
>>> from nodeshot.core.nodes.serializers import NodeDetailSerializer
>>> NodeDetailSerializer.add_relationship(**{
'name': 'comments',
'view_name': 'api_node_comments',
'lookup_field': 'slug'
})
"""
_relationships = {}
@classmethod
def add_relationship(_class, name, view_name, lookup_field):
""" adds a relationship to serializer
:param name: relationship name (dictionary key)
:type name: str
:param view_name: view name as specified in urls.py
:type view_name: str
:param lookup_field: lookup field, usually slug or id/pk
:type lookup_field: str
:returns: None
"""
_class._relationships[name] = (view_name, lookup_field)
def get_lookup_value(self, obj, string):
if '.' in string:
levels = string.split('.')
value = getattr(obj, levels.pop(0))
if value is not None:
for level in levels:
value = getattr(value, level)
return value
else:
return None
else:
return getattr(obj, string)
def get_relationships(self, obj):
request = self.context['request']
format = self.context['format']
relationships = {}
# loop over private _relationship attribute
for key, value in self._relationships.iteritems():
# retrieve view_name and name of lookup field by splitting tuple
view_name, lookup_field = value
lookup_value = self.get_lookup_value(obj, lookup_field)
# populate new dictionary with links
relationships[key] = reverse(view_name,
args=[lookup_value],
request=request,
format=format)
return relationships
@AKuederle
Copy link

This looks really cool! i wonder, is there any way to specify the type of relationship? I would like to add HyperlinkRelatedFields in that manner. Maybe my understanding of the inner workings of Django is not good enough, but is that possible?

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