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) |