Artigo
· 23 hr atrás 10min de leitura

Um portal para administrar memória feito com Django - Parte 1

Nosso objetivo

No último artigo, falamos sobre alguns fatores iniciais do Django. Aprendemos como começar um projeto, garantir que temos todos os requisitos e fazer um CRUD. No entanto, hoje vamos um pouco mais além.

Às vezes precisamos acessar métodos mais complexos, então hoje vamos conectar o IRIS a um ambiente Python, construir algumas funções e exibir seus resultados numa página web. Será similar à última discussão, mas mais adiante para que você possa fazer algo novo, mas não demais para que você se sinta perdido. Nesse projeto, vamos buscar informações sobre as globais em IRIS para rastrear seus tamanhos e entender o custo de cada namespace e tabela em MBs.

 

O Projeto

Cada passo percorrido aqui está disponível no meu Repositório GitHub, no histórico de commits;

Passos iniciais

Vamos repetir alguns passos do último artigo, então essa parte deve soar familiar.

  • Vá ao diretório desejado e digite o comando abaixo.

django-admin startproject globalSize

  • Adicione o arquivo requirements.txt com o texto abaixo e use o comando

pip install -r requirements.txt

para assegurar que seu ambiente tem todos os requisitos.

# Django itself
django>=4.2.1
# InterSystems IRIS driver for Django
django-iris==0.2.2
  • Em globalSize/settings.py, adicione IRIS nas configurações de DATABASES:
DATABASES = {
	‘default’: {
		‘ENGINE’: ‘django_iris’,
		‘NAME’: ‘USER’,
		‘USER’: ‘_system’,
		‘PASSWORD’: ‘SYS’,
		‘HOST’: ‘localhost’,
		‘PORT’: 1972,
	}
}
  • Não se esqueça de adicionar um .girignore e um .editorconfig. Também é conveniente ter um linter de sua preferência, mas está além do escopo desse artigo discutir esse tópico.

 

Criando o app e o modelo

Nós criamos um app e um modelo no último artigo, então essa seção também deve ser familiar, mesmo se tratando de um app e modelo diferentes

  • Para criar o app, digite

python manage.py startapp globals

  • E, globals/models.py, crie um modelo com a informação que deseja exibir sobre suas globais:
class irisGlobal(models.Model):
	database = models.CharField(max_length=40)
	name = models.CharField(max_length=40)
	allocatedsize = models.FloatField()
	size = models.FloatField()


	def __str__(self):
		return self.name
  • Em settings.py, adicione o novo app a INSTALLED_APPS:
INSTALLED_APPS = [
	…,
	‘globals’,
]

 

Definindo URLs e a página de início

Novamente vamos fazer alguns passos muito similares aos do último artigo.

  • Em globalSize/urls.py, importe a função include de django.urls e adicione um novo caminho a globals.urls em urlpatterns.
from django.urls import path, include
urlpatterns = [
    …,
    path(‘globals/’, include(‘globals.urls’)),
]
  • Crie as URLs para o app, adicionando o arquivo globals/urls.py com o texto abaixo.
from django.urls import path
from .views import home
urlpatterns = [
	path(‘’, home),
]
  • Crie a view que importamos no último passo. Em view.py, adicione a função a seguir.
def home(request):
	return render(request, “index.html”)
  • Finalmente, adicione o arquivo globals/templates/index.py e crie a página de início como desejado. Observe o exemplo que segue:
<!DOCTYPE html>
<html>
  <body>
    hello world!
  </body>
</html>

Se você utilizar so comandos abaixo e em seguida entrar no link http://127.0.0.1:8000/globals/ você já deve ter uma página exibindo a frase "hello world".

python manage.py makemigrations
python manage.py migrate
python manage.py runserver


Exibindo as globais nas páginas de admin e inicial

  • Em admin.py, importe o modelo e o registre.
from .models import irisGlobal
admin.site.register(irisGlobal)
  • Importe o modelo em views.py e retorne-o na função.
from .models import irisGlobal
def home(request):
	globals = irisGlobal.objects.all()
	return render(request, “index.html”, {“globals”: globals})
  • Agora podemos acessar as globais no index.html conforme preferência. Veja o exemplo a seguir.
<h3>ID  -  DATABASE          /       GLOBAL       - Size / Allocated</h3>
<ul>
  {% for global in globals %}
  <li>
    {{ global.id }} - {{ global.database }}    {{ global.name  }}  -  {{ global.size  }}  /  {{ global.allocatedsize }}
  </li>
  {% endfor %}
</ul>

 

Recuperando dados

Nesse ponto já temos o projeto pronto para ser carregado com informações. Tem uma boa quantidade de maneiras que isso pode tomar forma, mas eu vou usar a abordagem de Python para aprendermos um nova solução possível para integrar Django e IRIS.
Precisamos de alguns métodos para recuperar todos os dados. Podemos usar o InterSystems IRIS Cloud SQL com o driver DB-API para conectar a instância que queremos analisar, que não precisa ser a mesma a que conectamos o Django.
É uma boa prática organizar essa parte numa nova pasta para podermos tratá-la como um módulo. Para assegurar isso, crie a pasta api em globals, adicione um arquivo vazio __init__.py para que o Python a reconheça como um módulo e comece a escrever o arquivo que contém os métodos. Podemos chamá-lo de methods.py.


 

Crie a conexão

Para conectar nosso ambiente Python ao InterSystems IRIS, devemos seguir alguns passos descritos na seção "ObjectScript em ambiente Python" do artigo anterior Python e IRIS na prática - com exemplos!.

Daqui em diante é simples; importamos iris, passamos o endereço da conexão (a instância IRIS que queremos analisar no seguinte formato: host:port/namespace), um usuário e uma senha ao método iris.connect e criamos um IRIS do Python. Dê uma olhada no código abaixo

import intersystems_iris as iris
from django.db import connection as djangoconnection

# connection by iris
conn_params = djangoconnection.get_connection_params()
conn_params[“namespace”] = “%SYS”
connection = iris.connect(**conn_params)
irisPy = iris.createIRIS(connection)

 

Buscando os diretórios das bases de dados

Já que queremos retornar as dimensões das globais, precisamos (é claro) de suas dimensões, seus nomes e seus endereços, conhecidos como bases de dados.
Vou mostrar uma versão simplificada da função, mas lembre-se que é uma boa prática verificar cada passo e a conexão, disparando uma Exception se algo não corre como esperado.
Exatamente como faríamos em ObjectScript, adicionamos uma declaração SQL para então prepará-la, executá-la e retornar uma lista contendo todos os diretórios das bases de dados no seu conjunto de resultados. Podemos fazer tudo isso de maneira simples com as funções "irisPy.classMethodAlgo()", onde o Algo significa o tipo de elemento que o método deve retornar, e a irisObject.invoke(), com a qual podemos acessar tudo que o irisObject referencia. Analise o exemplo que segue.

def getAllDatabaseDirectories():
    try:
	   # check the connection made in irisPy, and if it is set to %SYS namespace
	   databaseDirectoriesList = []
	   with connection.cursor() as cursor:
		cursor.execute(“SELECT DISTINCT %EXACT(Directory) FROM Config.Databases WHERE SectionHeader = ?”, [“Databases”,],)
		databaseDirectoriesList = [row[0] for row in cursor]

    except Exception as error:
        return str(error)

    return databaseDirectoriesList

A variável statement (declaração) é definida como um objeto gerado pelo método %New da classe %SQL.Statement do IRIS. Então, é possível invocar o método %Prepare do objeto instanciado, com uma string de consulta como um argumento. Em seguida, podemos invocar os métodos %Execute e %Next para fazer a consulta e iterar pelo conjunto de resultados, acrescentando a informação desejada numa lista Python para acessá-la facilmente.
É fácil achar cada diretório das bases de dados na tabela Config.Databases, localizada somente no namespace %SYS de toda instância IRIS. Se quiser dar uma olhada nela pelo Portal de Administração, vai encontrar algumas informações interessantes lá.

 

Buscando todas as globais de uma base de dados

Essa função é bastante similar à anterior. Entretanto, temos uma consulta de classe pronta para usar agora. Outra vez, precisamos de uma declaração SQÇ, para prepararmos a consulta DirectoryList (lista de diretórios) da classe %SYS.GlobalQuery. Então, executamos com um diretório como argumento e retornar uma lista contendo todas as globais daquela base de dados.

def getGlobalsList(databaseDirectory: str):
    try:
        statement = irisPy.classMethodObject("%SQL.Statement", "%New")
        status = statement.invoke("%PrepareClassQuery", "%SYS.GlobalQuery","DirectoryList")

        result = statement.invoke("%Execute", databaseDirectory)

        globalList = []
        while (result.invoke("%Next")!=0):
            globalList.append(result.invoke("%Get", "Name"))

    except Exception as error:
        return str(error)

    return globalList


Buscando as dimensões das globais e seus espaços alocados

Finalmente podemos acessar a informação que queremos. Por sorte, o IRIS tem um método pronto para retornar as dimensões e o espaço alocado se você fornecer os nomes da base de dados e da global.

def getGlobalSize(databaseDirectory: str, globalName: str):
    try:
        globalUsed = iris.IRISReference(0)
        globalAllocated = iris.IRISReference(0)
        status = irisPy.classMethodObject("%GlobalEdit", "GetGlobalSize", databaseDirectory, globalName, globalAllocated, globalUsed, 0)

    except Exception as error:
        return str(error)

    return (globalUsed.getValue(), globalAllocated.getValue())

Dessa vez, precisaremos da função IRISRference(0) do módulo iris para receber os tamanhos da função "GetGlobalSize" por referência. Então, podemos acessar o valor com o método getValue().

 

Mostrando tudo na página inicial

Finalmente podemos utilizar essas funções para exibir os dados na página de início. Já temos um caminho pela informação e a tabela, agora precisamos preenchê-la. Quero criar um botão de atualizar para isso.

Primeiro, adicionamos o link ao index.html.

<body>
  <a href = "{% url 'update' %}">update</a></body>

Adicionamos o link à lista urlpatterns, em urls.py.

Add the link to the urlpatterns list, in urls.py.
from .views import home, update
urlpatterns = [
    path('', home),
    path('update', update, name="update"),
]

Então criamos a view em views.py.

from django.shortcuts import render, redirect
from .api.methods import *
def update(request):
    irisGlobal.objects.all().delete()
    databaseList = getAllDatabaseDirectories()

    for database in databaseList:
        globalList = getGlobalsList(database)

        for glob in globalList:
            used, allocated = getGlobalSize(database, glob)
            irisGlobal.objects.create(database=database, name=glob, size=used, allocatedsize=allocated)

    return redirect(home)

Para essa view, primeiro devemos importar a função redirect do django.shortcuts e os métodos que acabamos de construir. É uma boa ideia deletar qualquer dado remanescente na tabela para que eventualmente as globais deletadas sejam limpas. Já que a contagem de globais provavelmente não é gigante, é melhor fazer dessa maneira do que checar cada registro para ver se a global referenciada foi deletada ou precisa de uma atualização.

Então, buscamos todos os diretórios das bases de dados para que possamos, para cada base, checar todas as globais que existem dentro e, para cda global, teremos seu espaço utilizado e alocado.

Nesse ponto, já temos o modelo Django populado e pronto para retornar dados, então redirecionamos para a view home.
Se você acessar http://127.0.0.1:8000/globals/ e clicar no link de atualização que adicionamos, a página deve recarregar e dentro de alguns segundos exibir a lista e todas as globais, com suas bases de dados, espaços usados e alocados, conforme a imagem que segue.


 

Adicionando agregação

Você ficaria surpreso em saber o quanto é simples adicionar algumas opções rápidas de análise, como soma ou contagem. Não é necessário ser um mestre em Django para criar alguns painéis nessa página e, depois dessa seção, você estará num bom lugar para começar.

Já sabemos que a view home é responsável por renderizar o index. Até agora, geramos a variável "globals", contendo todos os dados e passamos para o index.html. Faremos algo similar, mas com funções de agregação. Criaremos uma variável para cada soma, usaremos os métodos aggregate() e Sum() e os adicionaremos à lista de argumentos no contexto da função de renderização. E, é claro, não podemos esquecer de importar a Sum() do django.db.models. Observe a função abaixo.

def home(request):
	globals = irisGlobal.objects.all()
	sumSize = globals.aggregate(Sum("size"))
	sumAllocated = globals.aggregate(Sum("allocatedsize"))
	return render(request, "index.html", {"globals": globals, "sumSize": sumSize, "sumAllocated":sumAllocated})

Agora podemos adicioná-la ao arquivo index.html e adicionar alguns parágrafos abaixo da lista (elemento <ul>). Dentro desses parágrafos, podemos acessar a conta de todas as globais e as somas, como mostrado abaixo.

<p>showing results for {{globals.count}} globals</p>
  <p>total size: {{sumSize.size__sum}}</p>
  <p>total allocated size: {{sumAllocated.allocatedsize__sum}}</p>
 </body>
</html>

Recarregue a página e você deve ter o seguinte:


 

O fim... quase

Nesse artigo, aprendemos sobre o armazenamento de dados de memória do InterSystems IRIS, como acessá-los pelo Python, construir uma API  e, usando IRIS como um sistema Cloud, rastreá-los e analisá-los facilmente. Podemos ver no horizonte consultas mais complexas, criação de painéis, automatização das atualizações e adicionar um sistema de notificação.

No próximo artigo, darei um passo mais perto desse horizonte, mostrando como filtrar e ordenar os dados antes de exibi-los, adicionar algumas opções de edição do lado do cliente e, por cima de tudo isso, uma pitada de CSS para deixar o projeto charmoso.

Você gostaria de ver algo que ainda não mostrei? Por favor, entre em contato se tiver alguma ideia ou necessidade que gostaria que eu escrevesse sobre.
 

Discussão (0)1
Entre ou crie uma conta para continuar