All Projects → crudlio → Crudl Example Django

crudlio / Crudl Example Django

Licence: other
CRUDL with Django, DRF/Graphene and SQLite

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Crudl Example Django

Django Api Domains
A pragmatic styleguide for Django API Projects
Stars: ✭ 365 (+223.01%)
Mutual labels:  graphql, rest, django, django-rest-framework
Drf Yasg
Automated generation of real Swagger/OpenAPI 2.0 schemas from Django REST Framework code.
Stars: ✭ 2,523 (+2132.74%)
Mutual labels:  rest, django, django-rest-framework
Django Rest Auth
This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST (JSON)
Stars: ✭ 2,289 (+1925.66%)
Mutual labels:  rest, django, django-rest-framework
Crudl Example Express
CRUDL with Node/Express and MongoDB
Stars: ✭ 197 (+74.34%)
Mutual labels:  graphql, rest, admin
Django Antd Tyadmin
类似 xadmin 的基于Model 快速生成前后台管理增删改查,筛选,搜索的后台管理自动化工具。Antd 界面好看现代化!前后端分离!无损二次开发!由Django Restful Framework 和 Ant Design Pro V4 驱动
Stars: ✭ 171 (+51.33%)
Mutual labels:  django, django-rest-framework, admin
Presentations
Collection of presentations for advanced Python topics
Stars: ✭ 264 (+133.63%)
Mutual labels:  rest, django, django-rest-framework
Blogbackendproject
Backend code for my blogs, develop with Django Rest framework.
Stars: ✭ 204 (+80.53%)
Mutual labels:  rest, django, django-rest-framework
Best Of Web Python
🏆 A ranked list of awesome python libraries for web development. Updated weekly.
Stars: ✭ 1,118 (+889.38%)
Mutual labels:  graphql, django, django-rest-framework
Admin On Rest
A frontend framework for building admin SPAs on top of REST services, using React and Material Design.
Stars: ✭ 392 (+246.9%)
Mutual labels:  graphql, rest, admin
Crudl
CRUDL is a backend agnostic REST and GraphQL based admin interface
Stars: ✭ 438 (+287.61%)
Mutual labels:  graphql, rest, admin
Drf Nested Routers
Nested Routers for Django Rest Framework
Stars: ✭ 1,098 (+871.68%)
Mutual labels:  rest, django, django-rest-framework
The Complete Guide To Drf And Vuejs
📢 Source Code from my Web Dev Course *The Complete Guide To Django REST Framework and Vue JS* (Lang: English & Italian)
Stars: ✭ 78 (-30.97%)
Mutual labels:  rest, django, django-rest-framework
Grand Challenge.org
A platform for end-to-end development of machine learning solutions in biomedical imaging
Stars: ✭ 89 (-21.24%)
Mutual labels:  django, django-rest-framework
Cride Platzi
REST API project used to teach Django on Platzi
Stars: ✭ 88 (-22.12%)
Mutual labels:  django, django-rest-framework
Django Graphql Social Auth
Python Social Auth support for Graphene Django
Stars: ✭ 90 (-20.35%)
Mutual labels:  graphql, django
Tutorialdb
A search 🔎 engine for programming/dev tutorials, See it in action 👉
Stars: ✭ 93 (-17.7%)
Mutual labels:  django, django-rest-framework
Ariadne
Ariadne is a Python library for implementing GraphQL servers using schema-first approach.
Stars: ✭ 1,274 (+1027.43%)
Mutual labels:  graphql, django
Django Rest Framework Api Key
An extra layer of authentication for Web APIs made with Django REST Framework
Stars: ✭ 92 (-18.58%)
Mutual labels:  django, django-rest-framework
Django rest Vuejs Auth
An Authentication project using JWT Tokens, Vuejs(frontend) and Django-Rest(backend).
Stars: ✭ 92 (-18.58%)
Mutual labels:  django, django-rest-framework
Iotdashboard
Fast Django server for IOT Devices
Stars: ✭ 95 (-15.93%)
Mutual labels:  django, django-rest-framework

CRUDL django example

This is a CRUDL example with Django, DRF (REST), Graphene (GraphQL) and SQLite.

  • CRUDL is still under development and the syntax might change (esp. with connectors and views).
  • The relevant part for your admin interface is within the folder crudl-admin-rest/admin/ (resp. crudl-admin-graphql/admin/). All other files and folders are usually given when using CRUDL.
  • The views are intentionally verbose in order to illustrate the possibilites with CRUDL.

Contents

Requirements

  • Node.js 5+
  • Python 2.7
  • virtualenv
  • SQLite

Installation

In order to use this example, you need to setup the API and serve the CRUDL admin interface (either REST or GraphQL or both).

Installation (REST)

  1. Create and activate a python virtual environment.

    $ virtualenv crudlexample
    $ source crudlexample/bin/activate
    
  2. Clone this repository and cd into the new folder:

    (crudlexample) $ git clone https://github.com/crudlio/crudl-example-django.git
    (crudlexample) $ cd crudl-example-django
    
  3. Install the python requirements:

    (crudlexample) crudl-example-django $ pip install -r conf/requirements.txt
    
  4. Setup the database (SQLite) and add contents:

    (crudlexample) crudl-example-django $ python manage.py migrate
    (crudlexample) crudl-example-django $ python manage.py loaddata apps/blog/fixtures/blog.json
    
  5. Start the django development server:

    (crudlexample) crudl-example-django $ python manage.py runserver
    
  6. Open a new terminal window/tab and build the CRUDL admin file. Go to /crudl-admin-rest/ and type:

    (crudlexample) crudl-admin-rest $ npm install --no-optional
    (crudlexample) crudl-admin-rest $ npm run watchify
    
  7. Open your browser, go to http://localhost:8000/crudl-rest/ and login with the demo user (demo/demo).

Installation (GraphQL)

Steps 1 to 5 are equal to Installation (REST).

  1. Open a new terminal window/tab and build the CRUDL admin file. Go to /crudl-admin-graphql/ and type:

    (crudlexample) crudl-admin-graphql $ npm install --no-optional
    (crudlexample) crudl-admin-graphql $ npm run watchify
    
  2. Open your browser, go to http://localhost:8000/crudl-graphql/ and login with the demo user (demo/demo).

CRUDL documentation

https://github.com/crudlio/crudl

Interface

What you get with CRUDL is an administration interface which consists of these elements:

Dashboard

  • The main entry page (currently just contains a description).

listView (per ressource)

  • A sortable table with all objects per ressource.
  • The objects are usually paginated (either numbered or continuous).
  • Includes a sidebar with search and filters.

change/addView (per object)

  • The form (fields and fieldsets) for adding/updating an object.
  • Optionally with tabs for complex relations (e.g. links with entries).

Moreover, you'll have a Menu/Navigation (on the left hand side), a Login/Logout page and Messages.

Notes

While this example is simple, there's still a couple of more advanced features in order to represent a real-world scenario.

Connectors & Views

In order for CRUDL to work, you mainly need to define connectors and views.

Connectors

The connectors provide the views with a unified access to different APIs like REST or GraphQL. Each connector usually represents a single API endpoint (or query) and implements the CRUD methods (create, read, update, delete). Moreover, the connector handles pagination and transforms the request/response.

There is an npm package implementing general connectors that can be extended (using middleware) to fit your particular needs. We also provide Django Rest Framework connectors that do most of the heavy lifting for you when you're using DRF at the backend.

Usage examples of DRF connectors:

import { createDRFConnector, defaults } from '@crudlio/crudl-connectors-drf'
import { numberedPagination } from '@crudlio/crudl-connectors-drf/lib/middleware'

defaults.baseURL = '/rest-api/'

const list = createDRFConnector(':collection/').use(numberedPagination())
const entries = list('entries') // URL resolves to '/rest-api/entries/'
const users = list('users') // URL resolves to '/rest-api/users/'

const request = crudl.createRequest() // Creates an authenticated request (alias crudl.req())

entries.read(request) // list all entries (results are paginated)
users.read(request.filter('is_staff', true)) // list all staff users (results are paginated)

Views

With views, you create the visual representation by defining the listView, changeView and addView options:

var listView = {
    // Required
    path: "api/path/to/collection",
    title: "Collection Name",
    actions: {
        list: listConnector.read,
    }
    fields: [],
    // Optional
    filters: [],
    normalize: (data) => { },
}

var changeView = {
    // Required
    path: "api/path/to/collection/:id",
    title: "Detail Name",
    actions: {
        get: req => detailConnector(crudl.path.id).read(req),
        save: req => detailConnector(crudl.path.id).update(req),
        delete: req => detailConnector(crudl.path.id).delete(req),
    },
    // Either fields or fieldsets
    fields: [],
    fieldsets: [],
    // Optional
    tabs: [],
    normalize: (data) => { },
    denormalize: (data) => { },
    validate: function (values) { },
}

Authentication

Both the REST and GraphQL API is only accessible for logged-in users based on TokenAuthentication. Besides the Token, we also return an attribute info in order to subsequently have access to the currently logged-in user (e.g. for filtering). The info is exposed in the global variable crudl.auth.

The REST login connector looks like this:

const login = createDRFConnector('login/')
.use(transformData('create',
    data => ({
        requestHeaders: { "Authorization": `Token ${data.token}` },
        info: data,
    })
))

And is used like this:

Field dependency

With Entries, the Categories depend on the selected Section. If you change the field Section, the options of field Category are populated based on the chosen Section due to the onChange method.

{
    name: 'category',
    field: 'Autocomplete',
    onChange: [
        {
            in: 'section',
            setProps: (section) => {
                if (!section.value) {
                    return {
                        readOnly: true,
                        helpText: 'In order to select a category, you have to select a section first',
                    }
                }
                return options('catogories', 'id', 'name')
                .read(crudl.req().filter('section', section.value))
                .then(({ options }) => {
                    if (options.length > 0) {
                        return {
                            readOnly: false,
                            helpText: 'Select a category',
                            options,
                        }
                    } else {
                        return {
                            readOnly: true,
                            helpText: 'No categories available for the selected section.'
                        }
                    }
                })
            }
        }
    ],
}

You can use the same syntax with list filters (see entries.js for example).

Foreign Key, Many-to-Many

There are a couple of foreign keys being used (e.g. Section or Category with Entry) and one many-to-many field (Tags with Entry).

{
    name: 'section',
    label: 'Section',
    field: 'Select',
    lazy: () => options('sections', 'id', 'name').read(crudl.req()),
},
{
    name: 'category',
    label: 'Category',
    field: 'Autocomplete',
    actions: {
        select: req => options('categories', 'id', 'name')
            .read(req.filter('id_in', req.data.selection.map(item => item.value).toString()))
            .then(({ options }) => options),
        search: (req) => {
            return options('categories', 'id', 'name')
            .read(req.filter('name', req.data.query).filter('section', crudl.context('section')))
            .then(({ options }) => options)
        },
    },
},
{
    name: 'tags',
    label: 'Tags',
    field: 'AutocompleteMultiple',
    required: false,
    showAll: false,
    helpText: 'Select a tag',
    actions: {
        search: (req) => {
            return options('tags', 'id', 'name')
            .read(req.filter('name', req.data.query.toLowerCase()))
            .then(({ options }) => options)
        },
        select: (req) => {
            return options('tags', 'id', 'name')
            .read(req.filter('id_in', req.data.selection.map(item => item.value).toString()))
            .then(({ options }) => options)
        },
    },
}

Relation with different endpoint

The descriptor Links is an example of related objects which are assigned through an intermediary table with additional fields.

changeView.tabs = [
    {
        title: 'Links', // Required
        actions: {      
            list: req => links.read(req.filter('entry', crudl.path.id)), // Requi
            add: req => links.create(req),
            save: req => link(req.data.id).update(req),
            delete: req => link(req.data.id).delete(req),
        },
        getItemTitle: (data) => `${data.url} (${data.title})`,
        fields: [
            {
                name: 'url',
                label: 'URL',
                field: 'URL',
                link: true,
            },
            {
                name: 'title',
                label: 'Title',
                field: 'String',
            },
            {
                name: 'id',
                hidden: true,
            },
            {
                name: 'entry',
                hidden: true,
                initialValue: () => crudl.context('id'),
            },
        ],
    },
]
  • The actions list, add, save and delete follow the same logic as the corresponding actions of list, change and add views.
  • getItemTitle: (data) => <string> defines the displayed title of the item form. If it is not provided, then the value of the first field is used (in this case it would be the URL value).
  • It's typical for the tab views to make use of hidden fields to include the related object's id in the form data.

Normalize/denormalize

With Entries, we set the owner to the currently logged-in user with denormalize:

var addView = {
    denormalize: (data) => {
        /* set owner on add. alternatively, we could manipulate the data
        with the connector by using createRequestData */
        if (crudl.auth.user) data.owner = crudl.auth.user
        return data
    }
}

With Users, we add a custom column full_name with the listView:

var listView = {
    normalize: (list) => list.map(item => {
        item.full_name = <span><b>{item.last_name}</b>, {item.first_name}</span>
        return item
    })
}

Custom field components

We have added a custom component SplitDateTimeField.jsx (see admin/fields) in order to show how you're able to implement fields which are not part of the core package. Usage example:

// See users.js (in both examples)
{
    name: 'date_joined',
    label: 'Date joined',
    field: SplitDateTimeField,  // Custom component
    getTime: (date) => {...},   // getTime is a required prop of SplitDateTimeField
    getDate: (date) => {...},   // getDate is a required prop of SplitDateTimeField
},

Initial values

You can set initial values with every field (based on context, if needed). The initial value is relevant mostly for add views.

{
    name: 'date',
    label: 'Date',
    field: 'Date',
    initialValue: () => formatDate(new Date())
},
{
    name: 'entry',
    hidden: true,
    initialValue: () => crudl.context('id'),
},

Validate fields and form

Validation should usually be handled with the API. That said, it sometimes makes sense to use frontend validation as well.

{
    name: 'date_gt',
    label: 'Published after',
    field: 'Date',
    /* simple date validation */
    validate: (value, allValues) => {
        const dateReg = /^\d{4}-\d{2}-\d{2}$/
        if (value && !value.match(dateReg)) {
            return 'Please enter a date (YYYY-MM-DD).'
        }
    }
},
{
    name: 'summary',
    label: 'Summary',
    field: 'Textarea',
    validate: (value, allValues) => {
        if (!value && allValues.status == '1') {
            return 'The summary is required with status "Online".'
        }
    }
},

In order to validate the complete form, you define a function validate with the changeView or addView:

var changeView = {
    path: 'entries/:id',
    title: 'Blog Entry',
    actions: { ... },
    validate: function (values) {
        if (!values.category && !values.tags) {
            return { _error: 'Either `Category` or `Tags` is required.' }
        }
    }
}

Custom column with listView

With Entries, we added a custom column to the listView based on the currently logged-in user.

var listView = {
    path: 'entries',
    title: 'Blog Entries',
    actions: {
        /* here we add a custom column based on the currently logged-in user */
        list: req => entries.read(req).then(data => data.map((item) => {
            item.is_owner = crudl.auth.user === item.owner
            return item
        })),
    },
}

listView.fields = [
    { ... }
    {
        name: 'is_owner',
        label: 'Owner',
        render: 'boolean',
    },
]

Multiple sort with listView

The listView supports ordering by multiple columns (see entries.js).

Filtering with listView

Filtering is done by defining fields with listView.filters (see entries.js). You have all the options available with the changeView (e.g. initial values, field dependency, autocompletes, ...).

Change password

You can only change the password of the currently logged-in User (see views/users.js)

Limitations

Credits & Links

CRUDL and crudl-example-django is written and maintained by vonautomatisch (Patrick Kranzlmüller, Axel Swoboda, Václav Pfeifer-Mikolášek).

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].