11from rest_framework .exceptions import ValidationError
2+ from rest_framework .fields import MISSING_ERROR_MESSAGE
23from rest_framework .relations import *
3- from rest_framework_json_api .utils import format_relation_name , get_related_resource_type , \
4- get_resource_type_from_queryset , get_resource_type_from_instance
54from django .utils .translation import ugettext_lazy as _
65
6+ from rest_framework_json_api .exceptions import Conflict
7+ from rest_framework_json_api .utils import format_relation_name , Hyperlink , \
8+ get_resource_type_from_queryset , get_resource_type_from_instance
9+
710
811class HyperlinkedRelatedField (HyperlinkedRelatedField ):
912 """
@@ -40,23 +43,26 @@ def to_internal_value(self, data):
4043
4144
4245class ResourceRelatedField (PrimaryKeyRelatedField ):
43- lookup_field = 'pk'
44- view_name = None
45-
46-
47-
46+ self_link_view_name = None
47+ related_link_view_name = None
48+ related_link_lookup_field = 'pk'
4849
4950 default_error_messages = {
5051 'required' : _ ('This field is required.' ),
5152 'does_not_exist' : _ ('Invalid pk "{pk_value}" - object does not exist.' ),
52- 'incorrect_type' : _ ('Incorrect type. Expected pk value , received {data_type}.' ),
53+ 'incorrect_type' : _ ('Incorrect type. Expected resource identifier object , received {data_type}.' ),
5354 'incorrect_relation_type' : _ ('Incorrect relation type. Expected {relation_type}, received {received_type}.' ),
5455 'no_match' : _ ('Invalid hyperlink - No URL match.' ),
5556 }
5657
57- def __init__ (self , view_name = None , ** kwargs ):
58- self .lookup_field = kwargs .pop ('lookup_field' , self .lookup_field )
59- self .lookup_url_kwarg = kwargs .pop ('lookup_url_kwarg' , self .lookup_field )
58+ def __init__ (self , self_link_view_name = None , related_link_view_name = None , ** kwargs ):
59+ if self_link_view_name is not None :
60+ self .self_link_view_name = self_link_view_name
61+ if related_link_view_name is not None :
62+ self .related_link_view_name = related_link_view_name
63+
64+ self .related_link_lookup_field = kwargs .pop ('related_link_lookup_field' , self .related_link_lookup_field )
65+ self .related_link_url_kwarg = kwargs .pop ('related_link_url_kwarg' , self .related_link_lookup_field )
6066
6167 # We include this simply for dependency injection in tests.
6268 # We can't add it as a class attributes or it would expect an
@@ -65,30 +71,80 @@ def __init__(self, view_name=None, **kwargs):
6571
6672 super (ResourceRelatedField , self ).__init__ (** kwargs )
6773
68- def get_url (self , obj , view_name , request ):
74+ def use_pk_only_optimization (self ):
75+ # We need the real object to determine its type...
76+ return False
77+
78+ def conflict (self , key , ** kwargs ):
6979 """
70- Given an object, return the URL that hyperlinks to the object.
80+ A helper method that simply raises a validation error.
81+ """
82+ try :
83+ msg = self .error_messages [key ]
84+ except KeyError :
85+ class_name = self .__class__ .__name__
86+ msg = MISSING_ERROR_MESSAGE .format (class_name = class_name , key = key )
87+ raise AssertionError (msg )
88+ message_string = msg .format (** kwargs )
89+ raise Conflict (message_string )
90+
91+ def get_url (self , name , view_name , kwargs , request ):
92+ """
93+ Given a name, view name and kwargs, return the URL that hyperlinks to the object.
7194
7295 May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
7396 attributes are not configured to correctly match the URL conf.
7497 """
75- # Unsaved objects will not yet have a valid URL.
76- if hasattr (obj , 'pk' ) and obj .pk is None :
98+
99+ # Return None if the view name is not supplied
100+ if not view_name :
101+ return None
102+
103+ # Return the hyperlink, or error if incorrectly configured.
104+ try :
105+ url = self .reverse (view_name , kwargs = kwargs , request = request )
106+ except NoReverseMatch :
107+ msg = (
108+ 'Could not resolve URL for hyperlinked relationship using '
109+ 'view name "%s".'
110+ )
111+ raise ImproperlyConfigured (msg % view_name )
112+
113+ if url is None :
77114 return None
78115
79- lookup_value = getattr (obj , self .lookup_field )
80- kwargs = {self .lookup_url_kwarg : lookup_value }
81- return self .reverse (view_name , kwargs = kwargs , request = request )
116+ return Hyperlink (url , name )
117+
118+ def get_links (self ):
119+ request = self .context .get ('request' , None )
120+ view = self .context .get ('view' , None )
121+ return_data = OrderedDict ()
122+ self_kwargs = view .kwargs .copy ()
123+ self_kwargs .update ({'related_field' : self .field_name if self .field_name else self .parent .field_name })
124+ self_link = self .get_url ('self' , self .self_link_view_name , self_kwargs , request )
125+
126+ related_kwargs = {self .related_link_url_kwarg : view .kwargs [self .related_link_lookup_field ]}
127+ related_link = self .get_url ('related' , self .related_link_view_name , related_kwargs , request )
128+
129+ if self_link :
130+ return_data .update ({'self' : self_link })
131+ if related_link :
132+ return_data .update ({'related' : related_link })
133+ return return_data
82134
83135 def to_internal_value (self , data ):
84136 expected_relation_type = get_resource_type_from_queryset (self .queryset )
137+ if not isinstance (data , dict ):
138+ self .fail ('incorrect_type' , data_type = type (data ).__name__ )
85139 if data ['type' ] != expected_relation_type :
86- self .fail ('incorrect_relation_type' , relation_type = expected_relation_type , received_type = data ['type' ])
140+ self .conflict ('incorrect_relation_type' , relation_type = expected_relation_type , received_type = data ['type' ])
87141 return super (ResourceRelatedField , self ).to_internal_value (data ['id' ])
88142
89143 def to_representation (self , value ):
90- return {
91- 'type' : format_relation_name (get_resource_type_from_instance (value )),
92- 'id' : str (value .pk )
93- }
144+ if self .pk_field is not None :
145+ pk = self .pk_field .to_representation (value .pk )
146+ else :
147+ pk = value .pk
148+
149+ return OrderedDict ([('type' , format_relation_name (get_resource_type_from_instance (value ))), ('id' , str (pk ))])
94150
0 commit comments