diff options
| author | Tom Christie | 2012-10-09 12:01:17 +0100 |
|---|---|---|
| committer | Tom Christie | 2012-10-09 12:01:17 +0100 |
| commit | 115e61be0900b3d5dd93ff84f20629311aceff9f (patch) | |
| tree | f8a521e65ee4a3671acd3c512cddc1f613c97d82 | |
| parent | dc52ceaaa273f3d3b5248c2ebf655a747fa516db (diff) | |
| download | django-rest-framework-115e61be0900b3d5dd93ff84f20629311aceff9f.tar.bz2 | |
Added quickstart guide
| -rw-r--r-- | docs/index.md | 12 | ||||
| -rw-r--r-- | docs/template.html | 1 | ||||
| -rw-r--r-- | docs/tutorial/quickstart.md | 133 | ||||
| -rw-r--r-- | rest_framework/decorators.py | 3 | ||||
| -rw-r--r-- | rest_framework/templates/rest_framework/base.html | 2 | ||||
| -rw-r--r-- | rest_framework/urlpatterns.py | 25 |
6 files changed, 172 insertions, 4 deletions
diff --git a/docs/index.md b/docs/index.md index f9795c1a..6a9f7980 100644 --- a/docs/index.md +++ b/docs/index.md @@ -58,6 +58,7 @@ Note that the base URL can be whatever you want, but you must include `rest_fram ## Quickstart +Can't wait to get started? The [quickstart guide][quickstart] is the fastest way to get up and running with REST framework. ## Tutorial @@ -118,6 +119,12 @@ Run the tests: ./rest_framework/runtests/runtests.py +## Support + +For support please see the [REST framework discussion group][group], or try the `#restframework` channel on `irc.freenode.net`. + +Paid support is also available from [DabApps], and can include work on REST framework core, or support with building your REST framework API. Please contact [Tom Christie][email] if you'd like to discuss commercial support options. + ## License Copyright (c) 2011-2012, Tom Christie @@ -149,6 +156,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [markdown]: http://pypi.python.org/pypi/Markdown/ [yaml]: http://pypi.python.org/pypi/PyYAML +[quickstart]: tutorial/quickstart.md [tut-1]: tutorial/1-serialization.md [tut-2]: tutorial/2-requests-and-responses.md [tut-3]: tutorial/3-class-based-views.md @@ -183,3 +191,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [migration]: topics/migration.md [changelog]: topics/changelog.md [credits]: topics/credits.md + +[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework +[DabApps]: http://dabapps.com +[email]: mailto:tom@tomchristie.com
\ No newline at end of file diff --git a/docs/template.html b/docs/template.html index 757586dc..d94bef4a 100644 --- a/docs/template.html +++ b/docs/template.html @@ -37,6 +37,7 @@ <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a> <ul class="dropdown-menu"> + <li><a href="{{ base_url }}/tutorial/quickstart{{ suffix }}">Quickstart</a></li> <li><a href="{{ base_url }}/tutorial/1-serialization{{ suffix }}">1 - Serialization</a></li> <li><a href="{{ base_url }}/tutorial/2-requests-and-responses{{ suffix }}">2 - Requests and responses</a></li> <li><a href="{{ base_url }}/tutorial/3-class-based-views{{ suffix }}">3 - Class based views</a></li> diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md new file mode 100644 index 00000000..1e9ed725 --- /dev/null +++ b/docs/tutorial/quickstart.md @@ -0,0 +1,133 @@ +# Quickstart + +We're going to create a simple API to allow admin users to view and edit the users and groups in the system. + +Create a new Django project, and start a new app called `quickstart`. Once you've set up a database and got everything synced and ready to go open up the app's directory and we'll get coding... + +## Serializers + +First up we're going to define some serializers in `quickstart/serializers.py` that we'll use for our data representations. + + from django.contrib.auth.models import User, Group + from rest_framework import serializers + + + class UserSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = User + fields = ('url', 'username', 'email', 'groups') + + + class GroupSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Group + fields = ('url', 'name', 'permissions') + +Notice that we're using hyperlinked relations in this case, with `HyperlinkedModelSerializer`. You can also use primary key and various other relationships, but hyperlinking is good RESTful design. + +## Views + +Right, we'd better right some views then. Open `quickstart/views.py` and get typing. + + from django.contrib.auth.models import User, Group + from rest_framework import generics + from rest_framework.decorators import api_view + from rest_framework.reverse import reverse + from rest_framework.response import Response + from quickstart.serializers import UserSerializer, GroupSerializer + + + @api_view(['GET']) + def api_root(request, format=None): + """ + The entry endpoint of our API. + """ + return Response({ + 'users': reverse('user-list', request=request), + 'groups': reverse('group-list', request=request), + }) + + + class UserList(generics.ListCreateAPIView): + """ + API endpoint that represents a list of users. + """ + model = User + serializer_class = UserSerializer + + + class UserDetail(generics.RetrieveUpdateDestroyAPIView): + """ + API endpoint that represents a single user. + """ + model = User + serializer_class = UserSerializer + + + class GroupList(generics.ListCreateAPIView): + """ + API endpoint that represents a list of groups. + """ + model = Group + serializer_class = GroupSerializer + + + class GroupDetail(generics.RetrieveUpdateDestroyAPIView): + """ + API endpoint that represents a single group. + """ + model = Group + serializer_class = GroupSerializer + +Let's take a moment to look at what we've done here before we move on. We have one function-based view representing the root of the API, and four class-based views which map to our database models, and specify which serializers should be used for representing that data. Pretty simple stuff. + +## URLs + +Okay, let's wire this baby up. On to `quickstart/urls.py`... + + from django.conf.urls import patterns, url, include + from rest_framework.urlpatterns import format_suffix_patterns + from quickstart.views import UserList, UserDetail, GroupList, GroupDetail + + + urlpatterns = patterns('quickstart.views', + url(r'^$', 'api_root'), + url(r'^users/$', UserList.as_view(), name='user-list'), + url(r'^users/(?P<pk>\d+)/$', UserDetail.as_view(), name='user-detail'), + url(r'^groups/$', GroupList.as_view(), name='group-list'), + url(r'^groups/(?P<pk>\d+)/$', GroupDetail.as_view(), name='group-detail'), + ) + + + # Format suffixes + urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'api']) + + + # Default login/logout views + urlpatterns += patterns('', + url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) + ) + +There's a few things worth noting here. + +Firstly the names `user-detail` and `group-detail` are important. We're using the default hyperlinked relationships without explicitly specifying the view names, so we need to use names of the style `{modelname}-detail` to represent the model instance views. + +Secondly, we're modifying the urlpatterns using `format_suffix_patterns`, to append optional `.json` style suffixes to our URLs. + +Finally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browseable API. + +## Settings + +We'd also like to set a few global settings. We'd like to turn on pagination, and we want our API to only be accessible to admin users. + + INSTALLED_APPS = ( + ... + 'rest_framework', + ) + + REST_FRAMEWORK = { + 'PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser'), + 'PAGINATE_BY': 10 + } + +Okay, that's us done. diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 2adbff24..948973ae 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -13,7 +13,8 @@ def api_view(http_method_names): class WrappedAPIView(APIView): pass - WrappedAPIView.http_method_names = [method.lower() for method in http_method_names] + allowed_methods = set(http_method_names) | set(('options',)) + WrappedAPIView.http_method_names = [method.lower() for method in allowed_methods] def handler(self, *args, **kwargs): return func(*args, **kwargs) diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index f213554b..abf3672f 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -39,7 +39,7 @@ {% if user.is_authenticated %} <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> - Welcome, {{ user }} + Logged in as {{ user }} <b class="caret"></b> </a> <ul class="dropdown-menu"> diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py index a040b621..386c78a2 100644 --- a/rest_framework/urlpatterns.py +++ b/rest_framework/urlpatterns.py @@ -2,13 +2,34 @@ from django.conf.urls.defaults import url from rest_framework.settings import api_settings -def format_suffix_patterns(urlpatterns, suffix_required=False, suffix_kwarg=None): +def format_suffix_patterns(urlpatterns, suffix_required=False, + suffix_kwarg=None, allowed=None): """ Supplement existing urlpatterns with corrosponding patterns that also include a '.format' suffix. Retains urlpattern ordering. + + suffix_required: + If `True`, only suffixed URLs will be generated, and non-suffixed + URLs will not be used. Defaults to `False`. + + suffix_kwarg: + The name of the kwarg that will be passed to the view. + Defaults to 'format'. + + allowed: + An optional tuple/list of allowed suffixes. eg ['json', 'api'] + Defaults to `None`, which allows any suffix. + """ suffix_kwarg = suffix_kwarg or api_settings.FORMAT_SUFFIX_KWARG - suffix_pattern = r'\.(?P<%s>[a-z]+)$' % suffix_kwarg + if allowed: + if len(allowed) == 1: + allowed_pattern = allowed[0] + else: + allowed_pattern = '(%s)' % '|'.join(allowed) + suffix_pattern = r'\.(?P<%s>%s)$' % (suffix_kwarg, allowed_pattern) + else: + suffix_pattern = r'\.(?P<%s>[a-z]+)$' % suffix_kwarg ret = [] for urlpattern in urlpatterns: |
