Staying DRY(er)

By Emmanuelle Delescolle

when working with Djang and frontend frameworks/libraries

Who am I?

  • An average developper
  • Who loves working with Django
  • A woman who codes
  • Someone prone to burnouts
  • Works at LevIT

Disclaimer

You know what this means

💖 DJango Admin 💖

from django.contrib import admin

from .models import Product, Category


@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    pass


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    pass

API and frontend

from restframework import viewsets, serializers

from .model import Product, Category


class ProductSerializer(serializers.ModelSerializer):

    class Meta:
        model = Product
        fields = ('id', 'name', 'category', 'in-stock')

class ProductViewSet(viewsets.ModelViewSet):

    serializer_class = ProductSerializer
    queryset = Product.objects.all()


class CategorySerializer(serializers.ModelSerializer):

    class Meta:
        model = Category
        fields = ('id', 'name', 'products')

class CategoryViewSet(viewsets.ModelViewSet):

    serializer_class = CategorySerializer
    queryset = Category.objects.all()
import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  category: DS.belongsTo('category', {
    async: true,
    inverse: 'products',
  }),
  in_stock: DS.attr('number'),
});
from catalog.api import ProductViewSet, CategoryViewSet

router.register(ProductViewSet)
router.register(CategoryViewSet)
import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  products: DS.hasMany('product', {
    async: true,
    inverse: 'category',
  }),
});



from .models import Product, Category


@register
class ProductEndpoint(Endpoint):
    model = Product


router.register(Category)
from drf_auto_endpoint.router import router, register
from drf_auto_endpoint.endpoints import Endpoint
$ magically export --all
$ ./manage.py export --all

It's not magic,
it's DRF-schema-adapter

Fast prototyping/concise code

The Endpoint class

from drf_auto_endpoint.endpoints import Endpoint

from .models import Product


class ProductEndpoint(Endpoint):
    
    model = Product
    filter_fields = ('category_id', )
    search_fields = ('name', )
    ordering_fields = ('in_stock', )
from drf_auto_endpoint import Endpoint
from rest_framework import viewsets

from .models import Product
from .serializers import ProductSerializer


class ProductViewSet(viewsets.ModelViewSet):

    def create(self, request, *args, **kwargs):
        # do something fancy
        return super(ProductViewSet, self) \
            .create(request, *args, **kwargs)


class ProductEndpoint(Endpoint):
    
    model = Product
    base_viewset = ProductViewSet
    base_serializer = ProductSerializer

Fast prototyping/concise code

Router and auto-discovery

from drf_auto_endpoint.endpoint import Endpoint
from drf_auto_endpoint.router import register, router

from .models import Product, Category


@register
class ProductEndpoint(Endpoint):
    
    model = Product
    filter_fields = ('category_id', )
    search_fields = ('name', )
    ordering_fields = ('in_stock', )


router.register(Category)

catalog/endpoints.py

from django.conf.urls import include, url

from drf_auto_endpoint.router import router


urlpatterns = [
    ...
    url(r'^api/', include(router.urls)),
]

project/urls.py

INSTALLED_APPS = (
    ...
    'drf_auto_endpoint',
)

settings.py

Demo

Ok, but what about my frontend?

INSTALLED_APPS = (
    ...
    'export_app',
)


EXPORTER_ADAPTER = 'export_app.adapters.MobxAxiosAdapter'
EXPORTER_ROUTER_PATH = 'react_ango.api_urls.router'
EXPORTER_FRONT_APPLICATION_PATH = os.path.join(BASE_DIR, '..', 'front', 'app')

settings.py

$ ./manage.py export --all
front/app/stores
├── _base.js
├── todolist.js
└── todotask.js

front/app/models
├── base
│   ├── _base.js
│   ├── todolist.js
│   └── todotask.js
├── todolist.js
└── todotask.js

What's in there?

import TodoTaskBase from './base/todotask';

class TodoTask extends TodoTaskBase {}

export default TodoTask;
import todoTaskStore from '../../stores/todotask';
import Model from './_base';   
import { observable } from 'mobx';
              
class TodoTaskBase extends Model {  
          
  store = todoTaskStore;
              
  @observable id;
  @observable description;
  @observable done;
  @observable __str__;
  @observable lst;
}

export default TodoTaskBase;
import Store from './_base';
import TodoTask from '../models/todotask';

export class TodoTaskStore extends Store {
  endpoint = 'todo/tasks';
  result = 'results';

  transform(record) {
    return new TodoTask(record);
  }

};

export default new TodoTaskStore;

front/app/models/todotask.js

front/app/models/base/todotask.js

front/app/stores/todotask.js

Demo

We are still missing information to be able to build a form

DRF_AUTO_METADATA_ADAPTER = \
    'drf_auto_endpoint.adapters.AngularFormlyAdapter'

settings.py

$ ./manage.py export --adapter_name MetadataAdapter --all
$ ./manage.py export --adapter_name MetadataES6Adapter --all

Demo

Building custom adapters

How complex can we get?

Demo

More?

Come find me in the hallways or during the sprints!

Questions

Links

Made with Slides.com