/django-chartjs

Django and ChartJS experiments.

Primary LanguageHTML

django-chartjs

Django and ChartJS experiments.

This project was done with:

  • Python 3.8.2
  • Django 2.2.12
  • Bootstrap 4.0
  • ChartJS 2.9.3

How to run project?

  • Clone this repository.
  • Create virtualenv with Python 3.
  • Active the virtualenv.
  • Install dependences.
  • Run the migrations.
git clone https://github.com/rg3915/django-chartjs.git
cd django-chartjs
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python contrib/env_gen.py
python manage.py migrate

Este projeto foi feito com:

  • Python 3.8.2
  • Django 2.2.12
  • Bootstrap 4.0
  • ChartJS 2.9.3

Como rodar o projeto?

  • Clone esse repositório.
  • Crie um virtualenv com Python 3.
  • Ative o virtualenv.
  • Instale as dependências.
  • Rode as migrações.
git clone https://github.com/rg3915/django-chartjs.git
cd django-chartjs
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python contrib/env_gen.py
python manage.py migrate

Criando o projeto do zero

Baixe este boilerplate.

wget https://gist.githubusercontent.com/rg3915/b363f5c4a998f42901705b23ccf4b8e8/raw/39caa9f63aa693ed651cebd4bb503cebffc51f6d/boilerplatesimple.sh

Ele serve pra criar um projeto simples em Django com o settings pré-configurado.

source boilerplatesimple.sh

Após terminar o processo de instalação você pode deletar isso.

rm boilerplatesimple.sh

Chart.js

O Chart.js é uma biblioteca Javascript para gerar gráficos.

Clicando em Get Started temos o seguinte exemplo:

<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
    type: 'bar',
    data: {
        labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
        datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
                'rgba(255, 99, 132, 0.2)',
                'rgba(54, 162, 235, 0.2)',
                'rgba(255, 206, 86, 0.2)',
                'rgba(75, 192, 192, 0.2)',
                'rgba(153, 102, 255, 0.2)',
                'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
                'rgba(255, 99, 132, 1)',
                'rgba(54, 162, 235, 1)',
                'rgba(255, 206, 86, 1)',
                'rgba(75, 192, 192, 1)',
                'rgba(153, 102, 255, 1)',
                'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
        }]
    },
    options: {
        scales: {
            yAxes: [{
                ticks: {
                    beginAtZero: true
                }
            }]
        }
    }
});
</script>

Criando os templates

Para ver este exemplo funcionando precisamos criar um template:

cd myproject/core
mkdir -p templates/includes

Vamos um template base.

cd templates
wget https://gist.githubusercontent.com/rg3915/0144a2408ec54c4e8008999631c64a30/raw/be05f2f8a483c6ddab3705c13bff0ac5fc2223be/base.html
wget https://gist.githubusercontent.com/rg3915/0144a2408ec54c4e8008999631c64a30/raw/ecbd782e23b3886ce64b318ef5ea194ed89b8ab3/index.html#

# Vá para a subpasta includes
wget https://gist.githubusercontent.com/rg3915/0144a2408ec54c4e8008999631c64a30/raw/ee78c798ed1fd57e2ba7328342ac3f4ab20268be/nav.html

Criando views e url.

Vamos criar a url

# core/urls.py
from django.urls import path
from myproject.core import views as v


app_name = 'core'


urlpatterns = [
    path('', v.index, name='index'),
]

E a views.py

from django.shortcuts import render


def index(request):
    template_name = 'index.html'
    return render(request, template_name)

Rodando a aplicação

Antes de rodar, faça

# Vá para a pasta onde está o manage.py
cd ../..
python contrib/env_gen.py
python manage.py migrate

E por fim, rode a aplicação.

python manage.py runserver

Inserindo o gráfico

Vamos copiar o exemplo de Get Started para dentro de index.html.

Veja o resultado em index.html.

E em base.html acrescente, no final do arquivo

    ...
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>

Parte 2

Trabalhando com dados reais

Vamos trabalhar com dados reais vindos do banco de dados.

Para isso vamos criar alguns modelos: product e category.

models.py

Vamos editar o arquivo models.py.

cat << EOF > models.py
from django.db import models


class Category(models.Model):
    title = models.CharField('categoria', max_length=50, unique=True)

    class Meta:
        verbose_name = 'categoria'
        verbose_name_plural = 'categorias'

    def __str__(self):
        return self.title


class Product(models.Model):
    title = models.CharField('título', max_length=100, unique=True)
    price = models.DecimalField('preço', max_digits=8, decimal_places=2)
    category = models.ForeignKey(
        Category,
        verbose_name='categoria',
        on_delete=models.SET_NULL,
        null=True,
        blank=True
    )

    class Meta:
        ordering = ('title',)
        verbose_name = 'produto'
        verbose_name_plural = 'produtos'

    def __str__(self):
        return self.title
EOF

Migrações

Vamos criar as migrações do banco.

python manage.py makemigrations
python manage.py migrate

Inserindo alguns dados

Para isso abra o shell_plus.

python manage.py shell_plus

E rode o script a seguir:

# Criando as categorias
categories = ('Bebida', 'Sobremesa', 'Lanche', 'Fruta', 'Refeição')
[Category.objects.create(title=category) for category in categories]

# Criando os produtos
import csv
import io
import urllib.request
from pprint import pprint


def csv_online_to_list(url: str) -> list:
    '''
    Lê um CSV a partir de uma url.
    '''
    url_open = urllib.request.urlopen(url)
    reader = csv.DictReader(io.StringIO(
        url_open.read().decode('utf-8')), delimiter=',')
    csv_data = [line for line in reader]
    return csv_data


products = csv_online_to_list('https://raw.githubusercontent.com/rg3915/django-chartjs/master/fix/products.csv')

pprint(products)

aux = []

for product in products:
    data = {
        'title': product['title'],
        'price': product['price']
    }
    category_title = product.get('category')
    category = Category.objects.filter(title=category_title).first()
    obj = Product(**data)
    if category:
        obj = Product(category=category, **data)
    aux.append(obj)

Product.objects.bulk_create(aux)

Visualizando os dados no Admin

Talvez você ainda não tenha criado um super usuário...

python manage.py createsuperuser --username="admin"

Edite admin.py

cat << EOF > myproject/core/admin.py
from django.contrib import admin
from .models import Category, Product


@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ('__str__', 'category',)
    search_fields = ('title',)
    list_filter = ('category',)


admin.site.register(Category)
EOF

Pegando os dados do banco para retornar no gráfico

cat << EOF > myproject/core/views.py
from django.shortcuts import render
from django.http import JsonResponse
from .models import Product


def index(request):
    template_name = 'index.html'
    return render(request, template_name)


def products(request):
    products = Product.objects.values('title', 'price')
    data = {
        'data': [
            {
                'title': item['title'],
                'value': float(item['price'])
            }
            for item in products
        ]
    }
    return JsonResponse(data)
EOF

Editando o template

TODO

Gráfico de pizza

Em inglês este gráfico é chamado de pie.

# core/urls.py
    path('api/categories/', v.categories, name='categories'),
# views.py
from django.db.models import Count
from django.http import JsonResponse
from .models import Product

# ...

def categories(request):
    categories = Product.objects\
        .values('category')\
        .annotate(value=Count('category'))\
        .order_by('category')\
        .values('category__title', 'value')
    data = {
        'data': [
            {
                'label': item['category__title'],
                'value': item['value'],
            }
            for item in categories
        ]
    }
    return JsonResponse(data)