I think I wrote a

tool

By Emmanuelle Delescolle

Was it a mistake?

Rapid web-Application Development

Who am I?

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

Disclaimer

Once upon a time...

Django admin

Django admin

  • Full featured CRUD
  • With filters and searches
  • With permissions
  • With translatable fields
  • Themable

The QUEST

The QUEST

  • Basic CRUD functionality ✔️
  • List + Item actions 🔌
  • Search + Filters ✔️
  • Easily themable 🔌
  • Dynamic forms ❌
  • Multi-level nesting 🔌
  • Wizards ❌
  • Translatable fields 🔌
  • Granular permissions ✔️
  • Minimal code for each extra model ✔️
  • Template-based PDF or Office reports ❌
  • Orderable lists 🔌
  • Orderable inlines 🔌

Template-based reports

Relatorio

levit-report

Making everyone be nice to each-other

  • List + Item actions

Easily themable
(django-admin-bootstrap)

  • Multi-level nesting
  • Wizards
  • Translatable fields
  • Orderable lists
  • Orderable inlines

Dynamic forms...

Image source: BuzzFeed

The QUEST with DRF and 🐹

  • Basic CRUD functionality ✔️
  • List + Item actions ✔️
  • Search + Filters ✔️🔌
  • Easily themable 🐹
  • Dynamic forms 🐹
  • Multi-level nesting ✔️
  • Wizards ✔️
  • Translatable fields 🔌️
  • Granular permissions ✔️
  • Minimal code for each extra model ❌
  • Orderable lists 🐹
  • Orderable inlines 🐹

Minimal code for each extra model

# my_app/serializers.py

from .models import MyModel


class MyModelSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = MyModel
        fields = '__all__'


# my_app/views.py

from .models import MyModel
from .serializers import MyModelSerializer


class MyModelViewSet(viewsets.ModelViewSet):

    serializer_class = MyModelSerializer
    queryset = MyModel.objects.all()


# urls.py

from my_app.views import MyModelViewSet

router.register(r'my_app/my_models', MyModelViewSet)
# my_app/admin.py

from .models import MyModel


admin.site.register(MyModel)
# my_app/endpoints.py

from .models import MyModel


router.register(MyModel)

Minimal code for each extra model

# my_app/models.py

class Category(models.Model):
    name = models.Charield(max_length=100)


class Product(models.Model):
    name = models.CharField(max_length=100)
    category = models.ForeignKey(Category,
                                 related_name='products',
                                 on_delete=models.CASCADE)
    description = models.TextField(blank=True,
                                   null=True)
    image = models.ImageField(null=True)
    price = models.DecimalField(max_digits=5,
                                decimal_places=2)
/* app/models/myapp/category.js */

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


/* app/models/myapp/product.js */

export default Model.extend({
  name: attr('string'),
  description: attr('string'),
  image: attr('string'),
  price: attr('number'),
  category: belongsTo('myapp/category', {
    async: true,
    inverse: 'products',
  }),
});

Minimal code for each extra model

/* app/models/myapp/product.js */

export default Model.extend({
  name: attr('string'),
  description: attr('string'),
  image: attr('string'),
  price: attr('number'),
  category: belongsTo('myapp/category', {
    async: true,
    inverse: 'products',
  }),
});
{
  "fields": [
    {"required": false, "label": "Id", "widget": "number", "name": "id", "readonly": true},
    {"required": true, "label": "Name", "widget": "text", "name": "name", "readonly": false},
    {"required": true, "extra": {"related_model": "myapp/category"}, "label": "Category",
     "widget": "foreignkey", "name": "category", "readonly": false},
    {"required": true, "label": "Price", "widget": "number", "name": "price", "readonly": false},
    {"required": false, "label": "Picture", "widget": "image", "name": "picture", "readonly": false},
    {"required": false, "label": "Description", "widget": "text", "name": "description", "readonly": false},
    {"required": false, "label": "Product", "widget": "text", "name": "__str__", "readonly": true}
  ]
}

Just an export library and a form builder?

Maybe a bit more...

drf-schema-adapter provides information about and ember-cli-crudities consumes:

  • All the fields in a serializer
  • Fields to display in list mode
  • Fields that are editable in list mode
  • Whether or not an endpoint is searchable
  • Fields that can be used to sort and/or filter a list
  • Actions that can be performed on a single item, a list of items or the whole endpoint
  • Display, readonly and required conditions for fields and actions 🐹
  • Form layout 🐹
  • Easy theming (based on bootstrap) 🐹

I regret nothing!

Demo

Questions

Links

I think I wrote a Rapid Web-Application Development tool

By Emma

I think I wrote a Rapid Web-Application Development tool

PyCon UK 2017 talk

  • 3,131