mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-02 19:40:13 +03:00
Implementation of serialization process as discussed here: https://groups.google.com/forum/?fromgroups#\!topic/django-rest-framework/BRsLJ92JRrk. Including docs and tests.
This commit is contained in:
parent
610da51d65
commit
529effdfc7
|
@ -426,7 +426,8 @@ The JSON representation of the same `Tag` example object could now look somethin
|
|||
|
||||
These examples cover the default behavior of generic foreign key representation. However, you may also want to write to generic foreign key fields through your API.
|
||||
|
||||
By default, the `GenericRelatedField` can be used with `read_only=False` only if you use `HyperlinkedRelatedField` as representation for every model you register with your `GenericRelatedField`.
|
||||
By default, a `GenericRelatedField` iterates over its nested serializers and returns the value of the first serializer, that is actually able to perform `from_native`` on the input value without any errors.
|
||||
Note, that (at the moment) only `HyperlinkedRelatedField` is able to serialize model objects out of the box.
|
||||
|
||||
This `Tag` serializer is able to write to it's generic foreign key field:
|
||||
|
||||
|
@ -454,14 +455,14 @@ The following operations would create a `Tag` object with it's `tagged_object` p
|
|||
tag_serializer.is_valid()
|
||||
tag_serializer.save()
|
||||
|
||||
If you feel that this default behavior doesn't suit your needs, you can subclass `GenericRelatedField` and override its `determine_serializer_for_data` method to implement your own way of decision-making.
|
||||
If you feel that this default behavior doesn't suit your needs, you can subclass `GenericRelatedField` and override its `determine_deserializer_for_data` or `determine_serializer_for_data` respectively to implement your own way of decision-making.
|
||||
|
||||
A few things you should note:
|
||||
|
||||
* Although `GenericForeignKey` fields can be set to any model object, the `GenericRelatedField` only handles models explicitly defined in its configuration dictionary.
|
||||
* Reverse generic keys, expressed using the `GenericRelation` field, can be serialized using the regular relational field types, since the type of the target in the relationship is always known.
|
||||
* You can mix `ModelSerializer` and `HyperlinkedRelatedField` in one `GenericRelatedField` configuration dictionary for deserialization purposes. It is considered bad practice though.
|
||||
* If you mix `ModelSerializer` and `HyperlinkedRelatedField` in one `GenericRelatedField` configuration dictionary, the serialization process (PUT/POST) will raise a `ConfigurationError`.
|
||||
* Please take into account that the order in which you register serializers matters as far as write operations are concerned.
|
||||
* Unless you provide custom serializer determination, only `HyperlinkedRelatedFields` provide write access to generic model relations.
|
||||
|
||||
For more information see [the Django documentation on generic relations][generic-relations].
|
||||
|
||||
|
|
|
@ -54,14 +54,11 @@ class GenericRelatedField(serializers.WritableField):
|
|||
def from_native(self, value):
|
||||
# Get the serializer responsible for input resolving
|
||||
serializer = self.determine_serializer_for_data(value)
|
||||
if serializer is None:
|
||||
raise ConfigurationError('Could not determine a valid serializer for value "%r"' % value)
|
||||
serializer.initialize(self.parent, self.source)
|
||||
|
||||
# The following is necessary due to the inconsistency of the `from_native` argument count when a serializer
|
||||
# accepts files.
|
||||
args = [value]
|
||||
import inspect
|
||||
if len(inspect.getargspec(serializer.from_native).args) > 2:
|
||||
args.append(None)
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
return serializer.from_native(value)
|
||||
|
||||
def determine_deserializer_for_data(self, value):
|
||||
|
@ -73,37 +70,14 @@ class GenericRelatedField(serializers.WritableField):
|
|||
return serializer
|
||||
|
||||
def determine_serializer_for_data(self, value):
|
||||
# While one could easily execute the "try" block within from_native and reduce operations, I consider the
|
||||
# concept of serializing is already very naive and vague, that's why I'd go for stringency with the deserialization
|
||||
# process here.
|
||||
for serializer in six.itervalues(self.serializers):
|
||||
if not isinstance(serializer, serializers.HyperlinkedRelatedField):
|
||||
raise ConfigurationError('Please use HyperlinkedRelatedField as serializers on GenericRelatedField \
|
||||
instances with read_only=False or set read_only=True.')
|
||||
|
||||
# This excerpt is an exact copy of ``rest_framework.relations.HyperlinkedRelatedField``, Line 363
|
||||
# From here until ...
|
||||
try:
|
||||
http_prefix = value.startswith('http:') or value.startswith('https:')
|
||||
except AttributeError:
|
||||
msg = self.error_messages['incorrect_type']
|
||||
raise ValidationError(msg % type(value).__name__)
|
||||
|
||||
if http_prefix:
|
||||
# If needed convert absolute URLs to relative path
|
||||
value = urlparse.urlparse(value).path
|
||||
prefix = get_script_prefix()
|
||||
if value.startswith(prefix):
|
||||
value = '/' + value[len(prefix):]
|
||||
try:
|
||||
match = resolve(value)
|
||||
serializer.from_native(value)
|
||||
# Returns the first serializer that can handle the value without errors.
|
||||
return serializer
|
||||
except Exception:
|
||||
raise ValidationError(self.error_messages['no_url_match'])
|
||||
|
||||
# ... here
|
||||
|
||||
matched_serializer = None
|
||||
for serializer in six.itervalues(self.serializers):
|
||||
if serializer.view_name == match.url_name:
|
||||
matched_serializer = serializer
|
||||
|
||||
if matched_serializer is None:
|
||||
raise ValidationError(self.error_messages['incorrect_url_match'])
|
||||
return matched_serializer
|
||||
pass
|
||||
return None
|
||||
|
|
|
@ -241,10 +241,11 @@ class TestGenericRelatedFieldSerialization(TestCase):
|
|||
|
||||
serializer = TagSerializer(data={
|
||||
'tag': 'reminder',
|
||||
'tagged_item': reverse('note-detail', kwargs={'pk': self.note.pk})
|
||||
'tagged_item': 'just a string'
|
||||
})
|
||||
self.assertRaises(ConfigurationError, serializer.is_valid)
|
||||
|
||||
|
||||
def test_not_registered_view_name(self):
|
||||
class TagSerializer(serializers.ModelSerializer):
|
||||
tagged_item = GenericRelatedField({
|
||||
|
|
Loading…
Reference in New Issue
Block a user