Drop out attribute

This commit is contained in:
Tom Christie 2013-04-30 08:24:33 +01:00
parent 81c3b4f250
commit 21ae3a6691
6 changed files with 42 additions and 61 deletions

View File

@ -374,16 +374,20 @@ Returns the field instance that should be used to represent the pk field.
### get_nested_field ### get_nested_field
**Signature**: `.get_nested_field(self, model_field)` **Signature**: `.get_nested_field(self, model_field, related_model, to_many)`
Returns the field instance that should be used to represent a related field when `depth` is specified as being non-zero. Returns the field instance that should be used to represent a related field when `depth` is specified as being non-zero.
Note that the `model_field` argument will be `None` for reverse relationships. The `related_model` argument will be the model class for the target of the field. The `to_many` argument will be a boolean indicating if this is a to-one or to-many relationship.
### get_related_field ### get_related_field
**Signature**: `.get_related_field(self, model_field, to_many=False)` **Signature**: `.get_related_field(self, model_field, related_model, to_many)`
Returns the field instance that should be used to represent a related field when `depth` is not specified, or when nested representations are being used and the depth reaches zero. Returns the field instance that should be used to represent a related field when `depth` is not specified, or when nested representations are being used and the depth reaches zero.
Note that the `model_field` argument will be `None` for reverse relationships. The `related_model` argument will be the model class for the target of the field. The `to_many` argument will be a boolean indicating if this is a to-one or to-many relationship.
### get_field ### get_field
**Signature**: `.get_field(self, model_field)` **Signature**: `.get_field(self, model_field)`

View File

@ -79,34 +79,26 @@ Let's take a look at a quick example of using REST framework to build a simple m
We'll create a read-write API for accessing users and groups. We'll create a read-write API for accessing users and groups.
Here's our `views.py` module: Here's our project's root `urls.py` module:
from django.conf.urls.defaults import url, patterns, include from django.conf.urls.defaults import url, patterns, include
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from rest_framework import viewsets, routers from rest_framework import viewsets, routers
# ViewSets define the view behavior. # ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet): class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all() model = User
fields = ('url', 'email', 'is_staff', 'groups')
class GroupViewSet(viewsets.ModelViewSet): class GroupViewSet(viewsets.ModelViewSet):
queryset = Group.objects.all() model = Group
fields = ('url', 'name')
And our `urls.py` setup:
from django.conf.urls.defaults import url, patterns, include
from myapp import views
from rest_framework import routers
# Routers provide an easy way of automatically determining the URL conf
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet, name='user') router.register(r'users', views.UserViewSet, name='user')
router.register(r'groups', views.GroupViewSet, name='group') router.register(r'groups', views.GroupViewSet, name='group')
# Wire up our API using automatic URL routing. # Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browseable API. # Additionally, we include login URLs for the browseable API.
urlpatterns = patterns('', urlpatterns = patterns('',

View File

@ -4,7 +4,6 @@ REST framework 2.3 is geared towards making it easier and quicker to build your
## ViewSets & Routers ## ViewSets & Routers
**TODO**
## Easier Serializers ## Easier Serializers
@ -132,13 +131,21 @@ If you've been customizing this behavior, for example perhaps to use `rst` marku
Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated. Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.
## ModelSerializers and reverse relationships
The support for adding reverse relationships to the `fields` option on a `ModelSerializer` class means that the `get_related_field` and `get_nested_field` method signatures have now changed.
In the unlikely event that you're providing a custom serializer class, and implementing these methods you should note the new call signature for both methods is now `(self, model_field, related_model, to_many)`. For revese relationships `model_field` will be `None`.
The old-style signature will continue to function but will raise a `PendingDeprecationWarning`.
--- ---
# Other notes # Other notes
## Explict view attributes ## More explicit style
The usage of `model` attribute in generic Views is still supported, but it's usage is being discouraged in favour of the more explict `queryset` attribute. The usage of `model` attribute in generic Views is still supported, but it's usage is being discouraged in favour of the setting the mode explict `queryset` and `serializer_class` attributes.
For example, the following is now the recommended style for using generic views: For example, the following is now the recommended style for using generic views:
@ -146,9 +153,9 @@ For example, the following is now the recommended style for using generic views:
queryset = MyModel.objects.all() queryset = MyModel.objects.all()
serializer_class = MyModelSerializer serializer_class = MyModelSerializer
Using an explict `queryset` attribute makes the functioning of the view more clear than using the shortcut `model` attribute. Using an explict `queryset` and `serializer_class` attributes makes the functioning of the view more clear than using the shortcut `model` attribute.
It also makes the usage of an overridden `get_queryset()` method more obvious. It also makes the usage of the `get_queryset()` or `get_serializer_class()` methods more obvious.
class AccountListView(generics.RetrieveAPIView): class AccountListView(generics.RetrieveAPIView):
serializer_class = MyModelSerializer serializer_class = MyModelSerializer
@ -167,6 +174,10 @@ It also makes the usage of an overridden `get_queryset()` method more obvious.
The 2.3 release series will be the last series to provide compatiblity with Django 1.3. The 2.3 release series will be the last series to provide compatiblity with Django 1.3.
## Version 2.2 API changes
All API changes in 2.2 that previously raised `PendingDeprecationWarning` will now raise a `DeprecationWarning`, which is loud by default.
## What comes next? ## What comes next?
The plan for the next few months is to concentrate on addressing outstanding tickets. 2.4 is likely to deal with relatively small refinements to the existing API. The plan for the next few months is to concentrate on addressing outstanding tickets. 2.4 is likely to deal with relatively small refinements to the existing API.

View File

@ -20,11 +20,17 @@ class GenericAPIView(views.APIView):
""" """
# You'll need to either set these attributes, # You'll need to either set these attributes,
# or override `get_queryset`/`get_serializer_class`. # or override `get_queryset()`/`get_serializer_class()`.
queryset = None queryset = None
serializer_class = None serializer_class = None
# This shortcut may be used instead of setting either or both
# of the `queryset`/`serializer_class` attributes, although using
# the explicit style is generally preferred.
model = None
# If you want to use object lookups other than pk, set this attribute. # If you want to use object lookups other than pk, set this attribute.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk' lookup_field = 'pk'
# Pagination settings # Pagination settings
@ -39,15 +45,6 @@ class GenericAPIView(views.APIView):
# Determines if the view will return 200 or 404 responses for empty lists. # Determines if the view will return 200 or 404 responses for empty lists.
allow_empty = True allow_empty = True
# This shortcut may be used instead of setting either (or both)
# of the `queryset`/`serializer_class` attributes, although using
# the explicit style is generally preferred.
model = None
# This shortcut may be used instead of setting the `serializer_class`
# attribute, although using the explicit style is generally preferred.
fields = None
# The following attributes may be subject to change, # The following attributes may be subject to change,
# and should be considered private API. # and should be considered private API.
model_serializer_class = api_settings.DEFAULT_MODEL_SERIALIZER_CLASS model_serializer_class = api_settings.DEFAULT_MODEL_SERIALIZER_CLASS
@ -193,16 +190,15 @@ class GenericAPIView(views.APIView):
if serializer_class is not None: if serializer_class is not None:
return serializer_class return serializer_class
assert self.model is not None or self.queryset is not None, \ assert self.model is not None, \
"'%s' should either include a 'serializer_class' attribute, " \ "'%s' should either include a 'serializer_class' attribute, " \
"or use the 'queryset' or 'model' attribute as a shortcut for " \ "or use the 'model' attribute as a shortcut for " \
"automatically generating a serializer class." \ "automatically generating a serializer class." \
% self.__class__.__name__ % self.__class__.__name__
class DefaultSerializer(self.model_serializer_class): class DefaultSerializer(self.model_serializer_class):
class Meta: class Meta:
model = self.model or self.queryset.model model = self.model
fields = self.fields
return DefaultSerializer return DefaultSerializer
def get_queryset(self): def get_queryset(self):

View File

@ -677,6 +677,8 @@ class ModelSerializer(Serializer):
def get_nested_field(self, model_field, related_model, to_many): def get_nested_field(self, model_field, related_model, to_many):
""" """
Creates a default instance of a nested relational field. Creates a default instance of a nested relational field.
Note that model_field will be `None` for reverse relationships.
""" """
class NestedModelSerializer(ModelSerializer): class NestedModelSerializer(ModelSerializer):
class Meta: class Meta:
@ -686,6 +688,8 @@ class ModelSerializer(Serializer):
def get_related_field(self, model_field, related_model, to_many): def get_related_field(self, model_field, related_model, to_many):
""" """
Creates a default instance of a flat relational field. Creates a default instance of a flat relational field.
Note that model_field will be `None` for reverse relationships.
""" """
# TODO: filter queryset using: # TODO: filter queryset using:
# .using(db).complex_filter(self.rel.limit_choices_to) # .using(db).complex_filter(self.rel.limit_choices_to)

View File

@ -344,32 +344,6 @@ class TestOverriddenGetObject(TestCase):
self.assertEqual(response.data, self.data[0]) self.assertEqual(response.data, self.data[0])
class TestFieldsShortcut(TestCase):
"""
Test cases for setting the `fields` attribute on a view.
"""
def setUp(self):
class OverriddenFieldsView(generics.RetrieveUpdateDestroyAPIView):
queryset = BasicModel.objects.all()
fields = ('text',)
class RegularView(generics.RetrieveUpdateDestroyAPIView):
queryset = BasicModel.objects.all()
self.overridden_fields_view = OverriddenFieldsView()
self.regular_view = RegularView()
def test_overridden_fields_view(self):
Serializer = self.overridden_fields_view.get_serializer_class()
self.assertEqual(Serializer().fields.keys(), ['text'])
self.assertEqual(Serializer().opts.model, BasicModel)
def test_not_overridden_fields_view(self):
Serializer = self.regular_view.get_serializer_class()
self.assertEqual(Serializer().fields.keys(), ['id', 'text'])
self.assertEqual(Serializer().opts.model, BasicModel)
# Regression test for #285 # Regression test for #285
class CommentSerializer(serializers.ModelSerializer): class CommentSerializer(serializers.ModelSerializer):