Coverage for rest_framework/relations : 76%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
|
""" Serializer fields that deal with relationships.
These fields allow you to specify the style that should be used to represent model relationships, including hyperlinks, primary keys, or slugs. """
##### Relational fields #####
# Not actually Writable, but subclasses may need to be. """ Base class for related model fields.
This represents a relationship using the unicode representation of the target. """
# 'null' is to be deprecated in favor of 'required' warnings.warn('The `null` keyword argument is deprecated. ' 'Use the `required` keyword argument instead.', DeprecationWarning, stacklevel=2) kwargs['required'] = not kwargs.pop('null')
else: # Reverse except Exception: msg = ('Serializer related fields must include a `queryset`' + ' argument or set `read_only=True') raise Exception(msg)
### We need this stuff to make form choices work...
return self.to_native(obj)
""" Return a readable representation for use with eg. select widgets. """ desc = smart_text(obj) ident = smart_text(self.to_native(obj)) if desc == ident: return desc return "%s - %s" % (desc, ident)
# If self._choices is set, then somebody must have manually set # the property self.choices. In this case, just return self._choices. return self._choices
# Otherwise, execute the QuerySet in self.queryset to determine the # choices dynamically. Return a fresh ModelChoiceIterator that has not been # consumed. Note that we're instantiating a new ModelChoiceIterator *each* # time _get_choices() is called (and, thus, each time self.choices is # accessed) so that we can ensure the QuerySet has not been consumed. This # construct might look complicated but it allows for lazy evaluation of # the queryset.
# Setting choices also sets the choices on the widget. # choices can be any iterable, but we call list() on it because # it will be consumed more than once. self._choices = self.widget.choices = list(value)
### Regular serializer stuff...
return self.to_native(obj)
else: # Also support non-queryset iterables. # This allows us to also support plain lists of related items.
# Form data # Non-form data else:
else:
### PrimaryKey relationships
""" Represents a relationship as a pk value. """
'does_not_exist': _("Invalid pk '%s' - object does not exist."), 'incorrect_type': _('Incorrect type. Expected pk value, received %s.'), }
# TODO: Remove these field hacks...
""" Return a readable representation for use with eg. select widgets. """ return desc
# TODO: Possibly change this to just take `obj`, through prob less performant
raise Exception('Writable related fields must include a `queryset` argument')
msg = self.error_messages['does_not_exist'] % smart_text(data) raise ValidationError(msg)
# To-many relationship
# Prefer obj.serializable_value for performance reasons # RelatedManager (reverse relationship)
# Forward relationship else: # Also support non-queryset iterables. # This allows us to also support plain lists of related items.
# To-one relationship # Prefer obj.serializable_value for performance reasons # RelatedObject (reverse relationship)
# Forward relationship
### Slug relationships
""" Represents a relationship using a unique field on the target. """
'does_not_exist': _("Object with %s=%s does not exist."), 'invalid': _('Invalid value.'), }
raise Exception('Writable related fields must include a `queryset` argument')
(self.slug_field, smart_text(data)))
### Hyperlinked relationships
""" Represents a relationship using hyperlinking. """
'no_match': _('Invalid hyperlink - No URL match'), 'incorrect_match': _('Invalid hyperlink - Incorrect URL match'), 'configuration_error': _('Invalid hyperlink due to configuration error'), 'does_not_exist': _("Invalid hyperlink - object does not exist."), 'incorrect_type': _('Incorrect type. Expected url string, received %s.'), }
# These are all pending deprecation
except KeyError: raise ValueError("Hyperlinked field requires 'view_name' kwarg")
# These are pending deprecation msg = 'pk_url_kwarg is pending deprecation. Use lookup_field instead.' warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
""" Given an object, return the URL that hyperlinks to the object.
May raise a `NoReverseMatch` if the `view_name` and `lookup_field` attributes are not configured to correctly match the URL conf. """
# Only try pk if it has been explicitly set. # Otherwise, the default `lookup_field = 'pk'` has us covered. pk = obj.pk kwargs = {self.pk_url_kwarg: pk} try: return reverse(view_name, kwargs=kwargs, request=request, format=format) except NoReverseMatch: pass
# Only try slug if it corresponds to an attribute on the object. # If the lookup succeeds using the default slug params, # then `slug_field` is being used implicitly, and we # we need to warn about the pending deprecation. msg = 'Implicit slug field hyperlinked fields are pending deprecation.' \ 'You should set `lookup_field=slug` on the HyperlinkedRelatedField.' warnings.warn(msg, PendingDeprecationWarning, stacklevel=2) except NoReverseMatch: pass
raise NoReverseMatch()
""" Return the object corresponding to a matched URL.
Takes the matched URL conf arguments, and the queryset, and should return an object instance, or raise an `ObjectDoesNotExist` exception. """
filter_kwargs = {'pk': pk} else: raise ObjectDoesNotExist()
msg = ( "Using `HyperlinkedRelatedField` without including the request " "in the serializer context is deprecated. " "Add `context={'request': request}` when instantiating " "the serializer." ) warnings.warn(msg, DeprecationWarning, stacklevel=4)
# If the object has not yet been saved then we cannot hyperlink to it. return
# Return the hyperlink, or error if incorrectly configured. except NoReverseMatch: msg = ( 'Could not resolve URL for hyperlinked relationship using ' 'view name "%s". You may have failed to include the related ' 'model in your API, or incorrectly configured the ' '`lookup_field` attribute on this field.' ) raise Exception(msg % view_name)
# Convert URL -> model instance pk # TODO: Use values_list raise Exception('Writable related fields must include a `queryset` argument')
# If needed convert absolute URLs to relative path
raise ValidationError(self.error_messages['incorrect_match'])
match.args, match.kwargs) except (ObjectDoesNotExist, TypeError, ValueError): raise ValidationError(self.error_messages['does_not_exist'])
""" Represents the instance, or a property on the instance, using hyperlinking. """
# These are all pending deprecation
except KeyError: msg = "HyperlinkedIdentityField requires 'view_name' argument" raise ValueError(msg)
# These are pending deprecation msg = 'pk_url_kwarg is pending deprecation. Use lookup_field instead.' warnings.warn(msg, PendingDeprecationWarning, stacklevel=2) msg = 'slug_url_kwarg is pending deprecation. Use lookup_field instead.' warnings.warn(msg, PendingDeprecationWarning, stacklevel=2) msg = 'slug_field is pending deprecation. Use lookup_field instead.' warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
warnings.warn("Using `HyperlinkedIdentityField` without including the " "request in the serializer context is deprecated. " "Add `context={'request': request}` when instantiating the serializer.", DeprecationWarning, stacklevel=4)
# By default use whatever format is given for the current context # unless the target is a different type to the source. # # Eg. Consider a HyperlinkedIdentityField pointing from a json # representation to an html property of that representation... # # '/snippets/1/' should link to '/snippets/1/highlight/' # ...but... # '/snippets/1/.json' should link to '/snippets/1/highlight/.html' format = self.format
# Return the hyperlink, or error if incorrectly configured. except NoReverseMatch: msg = ( 'Could not resolve URL for hyperlinked relationship using ' 'view name "%s". You may have failed to include the related ' 'model in your API, or incorrectly configured the ' '`lookup_field` attribute on this field.' ) raise Exception(msg % view_name)
""" Given an object, return the URL that hyperlinks to the object.
May raise a `NoReverseMatch` if the `view_name` and `lookup_field` attributes are not configured to correctly match the URL conf. """ except NoReverseMatch: pass
if self.pk_url_kwarg != 'pk': # Only try pk lookup if it has been explicitly set. # Otherwise, the default `lookup_field = 'pk'` has us covered. kwargs = {self.pk_url_kwarg: obj.pk} try: return reverse(view_name, kwargs=kwargs, request=request, format=format) except NoReverseMatch: pass
slug = getattr(obj, self.slug_field, None) if slug: # Only use slug lookup if a slug field exists on the model kwargs = {self.slug_url_kwarg: slug} try: return reverse(view_name, kwargs=kwargs, request=request, format=format) except NoReverseMatch: pass
raise NoReverseMatch()
### Old-style many classes for backwards compat
warnings.warn('`ManyRelatedField()` is deprecated. ' 'Use `RelatedField(many=True)` instead.', DeprecationWarning, stacklevel=2) kwargs['many'] = True super(ManyRelatedField, self).__init__(*args, **kwargs)
warnings.warn('`ManyPrimaryKeyRelatedField()` is deprecated. ' 'Use `PrimaryKeyRelatedField(many=True)` instead.', DeprecationWarning, stacklevel=2) kwargs['many'] = True super(ManyPrimaryKeyRelatedField, self).__init__(*args, **kwargs)
warnings.warn('`ManySlugRelatedField()` is deprecated. ' 'Use `SlugRelatedField(many=True)` instead.', DeprecationWarning, stacklevel=2) kwargs['many'] = True super(ManySlugRelatedField, self).__init__(*args, **kwargs)
warnings.warn('`ManyHyperlinkedRelatedField()` is deprecated. ' 'Use `HyperlinkedRelatedField(many=True)` instead.', DeprecationWarning, stacklevel=2) kwargs['many'] = True super(ManyHyperlinkedRelatedField, self).__init__(*args, **kwargs) |