"""Some SQLAlchemy specific field types."""
from django.db.models.constants import LOOKUP_SEP
from django_sorcery.db import meta
from rest_framework import fields, relations
[docs]class HyperlinkedIdentityField(relations.HyperlinkedIdentityField):
[docs] def get_url(self, obj, view_name, request, format):
info = meta.model_info(obj.__class__)
# Unsaved objects will not yet have a valid URL.
if not all(getattr(obj, i) for i in info.primary_keys):
return None
if len(info.primary_keys) == 1:
kwargs = {self.lookup_url_kwarg: getattr(obj, self.lookup_field)}
else:
kwargs = {k: getattr(obj, k) for k in info.primary_keys}
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
[docs]class UriField(HyperlinkedIdentityField):
"""Represents a uri to the resource."""
[docs] def get_url(self, obj, view_name, request, format):
"""Same as basic HyperlinkedIdentityField except return uri vs full
url."""
return super().get_url(obj, view_name, None, format)
[docs]class CharMappingField(fields.DictField):
"""Used for Postgresql HSTORE columns for storing key-value pairs."""
child = fields.CharField(allow_null=True)
[docs]class ImplicitExpandableListField(fields.ListField):
"""List field which implicitly expands parent field when child field is
expanded assuming parent field is also expandable by being one of the
choices."""
[docs] def to_internal_value(self, data):
data = super().to_internal_value(data)
for i in data[:]:
parts = i.split(LOOKUP_SEP)
data = list(
({LOOKUP_SEP.join(parts[:i]) for i in range(1, len(parts))} & set(self.child.choices)) | set(data)
)
return data
[docs]class SkippableField(fields.Field):
"""Field which is always skipped on to_representation.
Useful when used together with ``ExpandableModelSerializer`` since it allows
to completely skip expandable field when it is not being expanded.
Especially useful for ``OneToMany`` relations since by default nested
serializer cannot be rendered as none of the PKs of the "many" items are
known unlike ``ManyToOne`` when nested serializer can be rendered with PK.
For example:
.. code::
class FooSerializer(ExpandableModelSerializer):
bar = BarSerializer(many=True)
class Meta:
model = Foo
session = session
fields = "__all__"
expandable_fields = {
"bar": SkippableField()
}
"""
[docs] def get_attribute(self, instance):
raise fields.SkipField