Slide 1

Slide 1 text

API-Driven Django Philip James PyCon 2018 @phildini

Slide 2

Slide 2 text

Who am I?

Slide 3

Slide 3 text

Format • Buddy system • Switching pairs • SHcky Notes • Learn by trying first • You can get this all at the end

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

https://www.djangoproject.com/

Slide 7

Slide 7 text

http://www.django-rest-framework.org/

Slide 8

Slide 8 text

CivicAPI • Django app to track votes taken at civic government meeHngs • Needs to support rich frontends and API clients • CRUD app on votes taken

Slide 9

Slide 9 text

Create Retrieve Update Destroy

Slide 10

Slide 10 text

GeXng started

Slide 11

Slide 11 text

Pipenv https://docs.pipenv.org/ GeXng started

Slide 12

Slide 12 text

mkdir civicapi && cd civicapi pipenv --three install django GeXng started

Slide 13

Slide 13 text

pipenv run vs. pipenv shell GeXng started

Slide 14

Slide 14 text

pipenv shell django-admin.py startproject civicapi . GeXng started

Slide 15

Slide 15 text

django-admin.py startapp votes GeXng started

Slide 16

Slide 16 text

python manage.py migrate GeXng started

Slide 17

Slide 17 text

python manage.py createsuperuser GeXng started

Slide 18

Slide 18 text

python manage.py runserver GeXng started

Slide 19

Slide 19 text

{Check data in admin}

Slide 20

Slide 20 text

•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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

{Show Code}

Slide 23

Slide 23 text

Views

Slide 24

Slide 24 text

Django Generic Views?

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

What about an API?

Slide 27

Slide 27 text

Two op&ons: 1. Add separate API views 2. Munge JSON responses into exisHng views

Slide 28

Slide 28 text

What’s beker for the client? What’s beker for the dev?

Slide 29

Slide 29 text

Rich frontends, client-first == API-Driven, API First

Slide 30

Slide 30 text

StarHng with

Slide 31

Slide 31 text

To create: • APIView that lists and creates Votes We need: 1. ModelSerializer for Vote 2. rest_framework.generics.ListCreateAPIView

Slide 32

Slide 32 text

DRF ModelSerializers http://www.django-rest-framework.org/api-guide/serializers/#modelserializer

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

{Show Code}

Slide 36

Slide 36 text

DRF ListCreateAPIView

Slide 37

Slide 37 text

DRF ListCreateAPIView class UserList(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

{Show Code}

Slide 40

Slide 40 text

DRF Renderers

Slide 41

Slide 41 text

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},...]}

Slide 42

Slide 42 text

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)]),…])])

Slide 43

Slide 43 text

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)]),…])])

Slide 44

Slide 44 text

Django TemplaHng

Slide 45

Slide 45 text

DRF Template Gotcha! In se,ngs.py: REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.paginaHon.PageNumberPaginaHon', 'PAGE_SIZE': 10, }

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

{Show Code}

Slide 48

Slide 48 text

Tip! ?format=

Slide 49

Slide 49 text

Break!

Slide 50

Slide 50 text

Why not use the BrowseableAPIRenderer for everything?

Slide 51

Slide 51 text

CreaHng a Vote with the rendered template

Slide 52

Slide 52 text

CreaHng a Vote with the rendered template {% csrf_token %}

Subject

Vote taken

Ayes

Nays

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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.

Slide 55

Slide 55 text

{Review Code}

Slide 56

Slide 56 text

Vote Detail

Slide 57

Slide 57 text

DRF RetrieveUpdateDestroyAPIView class UserDetail(generics.RetrieveUpdateDestroyAPIView): queryset = User.objects.all() serializer_class = UserSerializer

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

{Show Code}

Slide 60

Slide 60 text

AuthenHcaHon

Slide 61

Slide 61 text

Reading vs. WriHng

Slide 62

Slide 62 text

Session, Token, OAuth

Slide 63

Slide 63 text

DRF AuthenHcaHon Classes && DRF Permissions Classes

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

{Show Code}

Slide 66

Slide 66 text

Keeping things DRY

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

def get_renderers(self): renderer_classes = self.renderer_classes if self.request.user.is_staff: renderer_classes += [BrowsableAPIRenderer] return [renderer() for renderer in renderer_classes]

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

{Show Code}

Slide 71

Slide 71 text

Token Auth

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

TesHng

Slide 74

Slide 74 text

TesHng • http://www.django-rest-framework.org/ api-guide/testing/ • https://docs.djangoproject.com/en/2.0/ topics/testing/ •“Code without tests is broken as designed.” - Jacob Kaplan-Moss

Slide 75

Slide 75 text

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)

Slide 76

Slide 76 text

Where to go from here?

Slide 77

Slide 77 text

Deploying OAuth Django Channels Working with Web Frontends

Slide 78

Slide 78 text

Thanks! Philip James [email protected] @[email protected] https://github.com/phildini/api-driven-django