Upgrade to Pro — share decks privately, control downloads, hide ads and more …

API-Driven Django

API-Driven Django

A tutorial on making API-Driven Django apps.

Philip James

May 10, 2018
Tweet

More Decks by Philip James

Other Decks in Programming

Transcript

  1. Format • Buddy system • Switching pairs • SHcky Notes

    • Learn by trying first • You can get this all at the end
  2. Some Pairing Best PracHces • Less familiar person should type

    first • Switching pairs at different sessions • One machine, one set of hands • No grabbing the keyboard
  3. Goals • Build a working Django API-First App for collecHng

    civic government data • Have that App use the same code for API endpoints as well as rendered templates • Have that app let anyone read, but only authorized users write
  4. CivicAPI • Django app to track votes taken at civic

    government meeHngs • Needs to support rich frontends and API clients • CRUD app on votes taken
  5. •subject: string, required •vote_taken: date and Hme, required •ayes: integer,

    not required •nays: integer, not required Vote Model https://docs.djangoproject.com/en/2.0/ref/models/fields/ google: django model field reference
  6. Pair Exercise - 10 min 1. Make sure reqs are

    installed and project is set up 2. Try to build the model in votes/models.py 3. Add votes to installed_apps in civicapi/seXngs.py 4. python manage.py migrate 5. (extra - see it in admin) https://docs.djangoproject.com/en/2.0/ref/models/fields/ google: django model field reference
  7. To create: • View that lists and creates Votes We

    need: 1. django.views.generic.list.ListView 2. django.views.generic.edit.CreateView 3. Template file 4. URL update 5. (Possibly) a ModelForm
  8. To create: • APIView that lists and creates Votes We

    need: 1. ModelSerializer for Vote 2. rest_framework.generics.ListCreateAPIView
  9. DRF ModelSerializers class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields

    = ('id', 'account_name', 'users', 'created') http://www.django-rest-framework.org/api-guide/serializers/#modelserializer
  10. Pair Exercise - 10 min 1. pipenv install djangorestframework 2.

    add rest_framework to the Django app 3. Try to make the ModelSerializer for Vote in the votes app 4. write a test to verify the serialized data http://www.django-rest-framework.org/api-guide/serializers/#modelserializer
  11. Pair Exercise - 10 min 1. add a VoteList ListCreateAPIView

    to views 2. add the view to urls.py at /votes/ 3. python manage.py runserver 4. open a browser, browse to localhost:8000 5. play around, try to create some votes 6. (opHonal: write a test for the view) http://www.django-rest-framework.org/api-guide/generic-views/#examples
  12. DRF JSONRenderer OrderedDict([('count', 3), ('next', None), ('previous', None), ('results', [OrderedDict([('id',

    1), ('subject', 'More apps should be built in Django!'), ('vote_taken', '2018-04-14T19:51:09Z'), ('ayes', 100), ('nays', 0)]),…])]) {'count': 3, 'next': None, 'previous': None, 'results': [{'id': 1, 'subject': 'More apps should be built in Django!', 'vote_taken': '2018-04-14T19:51:09Z', 'ayes': 100, 'nays': 0},...]}
  13. DRF TemplateHTMLRenderer OrderedDict([('count', 3), ('next', None), ('previous', None), ('results', [OrderedDict([('id',

    1), ('subject', 'More apps should be built in Django!'), ('vote_taken', '2018-04-14T19:51:09Z'), ('ayes', 100), ('nays', 0)]),…])]) <ul> <li><a href=“/votes/1/"> More apps should be built in Django! - 100/0 on 2018-04-14T19:51:09Z </a></li> … </ul>
  14. DRF BrowsableAPIRenderer OrderedDict([('count', 3), ('next', None), ('previous', None), ('results', [OrderedDict([('id',

    1), ('subject', 'More apps should be built in Django!'), ('vote_taken', '2018-04-14T19:51:09Z'), ('ayes', 100), ('nays', 0)]),…])])
  15. Django TemplaHng <ul> {% for user in results %} <li><a

    href="/users/{{ user.id }}/">{{ user.name }}</a></li> {% endfor %} </ul>
  16. Pair Exercise - 20 min 1. add a templates directory

    to votes 2. add a vote_list.html template 3. add the TemplateHTMLRenderer to VoteList’s renderer_classes, along with JSONRenderer and BrowsableAPIRenderer 4. point the template_name to “vote_list.html" 5. Create votes/templates/vote_list.html 6. Browse to /votes/ and see the list. http://www.django-rest-framework.org/api-guide/renderers/#templatehtmlrenderer
  17. CreaHng a Vote with the rendered template <form action="/votes/" method="POST"

    enctype="multipart/form-data"> {% csrf_token %} <p> <label for="subject">Subject</label> <input id="subject" name="subject" type="text"> </p> <p> <label for="vote_taken">Vote taken</label> <input id="vote_taken" name="vote_taken" type="datetime-local"> </p> <p> <label for="ayes">Ayes</label> <input id="ayes" name="ayes" type="number"> </p> <p> <label for="nays">Nays</label> <input id="nays" name="nays" type="number"> </p> <input type="submit" value="Save"> </form>
  18. CreaHng a Vote with the rendered template def create(self, request,

    *args, **kwargs): response = super(VoteList, self).create(request, *args, **kwargs) if request.accepted_renderer.format == 'html' and response.status_code == 201: return redirect('/votes/') return response
  19. Pair Exercise - 5 min 1. add the code snippets

    to vote_list.html and views.py 2. Browse to /votes/ and add a vote via the interface.
  20. Pair Exercise - 10 min 1. add a VoteDetail view,

    using generics.RetrieveUpdateDestroyAPIView 2. Don’t forget the right renderers! 3. add that view to urls.py 4. add a vote.html template 5. point the template_name to “vote.html" 6. Wire up the links in “vote_list.html” 7. Browse to /votes/ and see the list, make sure the links are clickable. http://www.django-rest-framework.org/tutorial/3-class-based-views/#using-generic-class- based-views
  21. Pair Exercise - 10 min 1. add permissions.IsAuthenHcatedOrReadOnly to VoteList

    view and VoteDetail view 2. Browse to /votes/ try adding a vote 3. Open an anonymous browser window, try doing the same thing 4. (opHonal: add some tests for the authed and non- authed requests) http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#adding- required-permissions-to-views
  22. Keeping things DRY •We can use mixins for common funcHonality

    •Capture auth, permissions, renderers in one place •While we’re here, only allow the BrowseableAPIRenderer to staff users
  23. Pair Exercise - 10 min 1. add a VoteAPIMixin that

    captures renderer classes, queryset, serializer_class, and permission classes 2. Make sure the get_renderers snippet is defined in the mixin 3. Add the mixin to both view classes, remove the duplicate code from the views 4. Browse the api and make sure things sHll work. 5. Run tests and make sure things sHll work
  24. Pair Exercise - 10 min 1. add ‘rest_framework.authtoken' to seXngs.py

    2. Add correct authenHcaHon classes to seXngs.py ‘DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ) http://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
  25. Pair Exercise - 10 min 1. Add more tests for

    VoteList and VoteDetail views 2. Pay special akenHon to edge cases with authenHcaHon and renderer type 3. Any more tests you can think of? 4. (opHonal: add ability to edit votes from VoteDetail)