Archivio

Posts Tagged ‘django’

Django channels: chat

15 Gennaio 2019 Commenti chiusi

Django-channels è stato creato per gestire, con django, i protocolli di comunicazione asincrona come i websockets.
Oltre alla gestione dei websockets, channels permette di eseguire processi asincroni in background.

In ogni tipica applicazione Django, il traffico HTTP è gestito in maniera Sincrona, ovvero, quando un browser manda una request,
aspetta che questa venga instradata a Django, il quale dopo le opportune elaborazioni rispedirà al mittente una response.

Invece la cosa è molto più versatile con i websockets, perchè la comunicazione è di tipo bidirezionale.
Quando una connessione ad un websocket viene stabilita, il browser può spedire e ricevere messaggi e la visualizzazione avviene automaticamente senza bisogno di “ricaricare” ogni volta il browser.

Vediamo un esempio chiarificatore.
Creiamo una chat (è il solito esempio che troverete sul web in migliaia di forme simili) con django e django-channels.

Per prima cosa creiamo un virtual-environment:

C:\tmp\django-dev>python -m venv venv

lo attiviamo:

C:\tmp\django-dev>venv\Scripts\activate

ed installiamo le librerie necessarie:

(venv) C:\tmp\django-dev>pip install django

Prima di installare channels, installiamo Twisted da wheel per non incorrere in un fastidioso errore.
Da questo sito scaricare la versione corretta, nel mio caso quella per python 3.7 a 32 bit e posizionare il file scaricato, all’interno del venv creato in precedenza. Installare il file:

(venv) C:\tmp\django-dev>pip install venv\Twisted-18.9.0-cp37-cp37m-win32.whl
Processing c:\tmp\django-dev\venv\twisted-18.9.0-cp37-cp37m-win32.whl
...
Installing collected packages: Twisted
Successfully installed Twisted-18.9.0

Installato twisted procedere con i pacchetti channels ed asgi_redis:

(venv) C:\tmp\django-dev>pip install channels channels_redis pywin32

Pywin32 è necessario per poter utilizzare Redis-server su windows.

La fase successiva è quella di creare il progetto django e l’applicazione chat:

(venv) C:\tmp\django-dev>django-admin startproject djangosite
(venv) C:\tmp\django-dev>cd djangosite
(venv) C:\tmp\django-dev\djangosite>python manage.py startapp chat

Ora andremo a modificare il codice per creare la nostra chat.
Nel file djangosite\setiings.py aggiungere channels e chat:

...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat',
    'channels',
]

...

Ora puntiamo al nostro server REDIS, sempre nel file djangosite\setiings.py:

...

ASGI_APPLICATION = "djangosite.routing.application"

CHANNEL_LAYERS = {
    'default': {'BACKEND': 'channels_redis.core.RedisChannelLayer',
                'CONFIG': {"hosts": [('127.0.0.1', 6379)], }, },
                 }

Come si nota nella prima riga di codice inserita, puntiamo ad un file routing, che ora andiamo a creare:

# djangosite/routing.py

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing


application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AuthMiddlewareStack(
        URLRouter(chat.routing.websocket_urlpatterns)
    ),
}
)

Qui viene definito il ProtocolTypeRouter e si richiamano le “url” riferite ai websockets dell’applicazione chat.
Dobbiamo quindi creare anche il file routing.py dell’applicazione chat:

# chat/routing.py

from django.conf.urls import url

from . import consumers

websocket_urlpatterns = [
    url(r'^ws/chat/$', consumers.ChatConsumer),
]

Come si nota, qui importiamo e facciamo riferimento ad un Consumer (ChatConsumer).
I consumers sono l’equivalente delle views in una app tradizionale di Django.
Per approfondimenti sui Consumers fare riferimento alla doc ufficiale.

Creiamo il file consumers quindi:

# chat/consumers.py

from asgiref.sync import AsyncToSync
from channels.generic.websocket import WebsocketConsumer
import json


class ChatConsumer(WebsocketConsumer):
    @property
    def _topic_name(self):
        return "topic_DjangoWebsocketTest"

    @property
    def _chat_channel_name(self):
        return "DjangoWebsocketTest"

    def connect(self):
        AsyncToSync(self.channel_layer.group_add)(
            self._chat_channel_name,
            self.channel_name)
        self.accept()

    def disconnect(self, close_code):
        AsyncToSync(self.channel_layer.group_discard)(
            self._chat_channel_name,
            self.channel_name)

    def receive(self, text_data):

        json_text_data = '{"message": "%s"}' % text_data
        text_data_json = json.loads(json_text_data)
        message = text_data_json['message']

        AsyncToSync(self.channel_layer.group_send)(
            self._chat_channel_name,
            {
                'type': 'chat_message',
                'message': message
            })

    # Handles the "chat_message" event when it's sent to our group
    def chat_message(self, event):
        message = event['message']
        self.send(text_data=message)

Ora dobbiamo modificare i vari file urls, cominciando con quello principale:

# djangosite\urls.py

from django.contrib import admin
from django.conf.urls import include, url

urlpatterns = [
    url(r'^', include('chat.urls')),
    url(r'^admin/', admin.site.urls),
]

Poi, richiamandolo tramite include, creiamo quello specifico della app chat:

# chat\urls.py

from django.contrib import admin
from django.conf.urls import include, url
from chat import views

urlpatterns = [
    url(r'^chat$', views.chat, name='chat'),
]
Creiamo la views per effettuare la chat:

# chat/views.py

from django.shortcuts import render

# Create your views here.


def chat(request):
    return render(request, 'chat/chat.html', {})

Ed infine creiamo le templates, partendo da quella base. Creare quindi la solita directory sotto chat e
dentro quest ultima, creaiamo un’altra sottodirectory chat. Creeremo quindi due file:

chat\templates\chat\base.html
chat\templates\chat\chat.html

Il file base dal quale erediteremo sarà:

{% load staticfiles %}
{% load static %}
{# Load the tag library #}
{# Load CSS and JavaScript #}

{% block head %}
{% endblock %}

<title>{% block title %}Channels Chat{% endblock %}</title>

{% block navbar %}
{% endblock %}

<html>

  <head>
      {% block script %}{% endblock script %}
  </head>

  <body>
      {% block content %}{% endblock %}
  </body>

</html>

il file specifico invece che erediterà, sarà:

{% extends 'chat/base.html' %}

{% block title %}<title>Channels Chat</title>{% endblock %}

{% block content %}
  <html>
   <script>
    window.onload = function() {

      // Get references to elements on the page.
      var form = document.getElementById('message-form');
      var messageField = document.getElementById('message');
      var messagesList = document.getElementById('messages');
      var socketStatus = document.getElementById('status');
      var closeBtn = document.getElementById('close');
      var wsurl = "ws://127.0.0.1:8000/ws/chat/";

      // create a new socket
      var socket = new WebSocket(wsurl);

      // event listener all'apertura del socket
      socket.onopen = function open(event) {
            socketStatus.innerHTML = 'Connected to: ' + event.currentTarget.url;
            socketStatus.className = 'open';
       };

    // Send a message when the form is submitted.
    form.onsubmit = function(e) {
      e.preventDefault();

      // Retrieve the message from the textarea.
      var message = messageField.value;

      // Send the message through the WebSocket.
      socket.send(message);

      // Add the message to the messages list.
      messagesList.innerHTML += '<li class="sent"><span>Sent:</span>' + message +
                                '</li>';

      // Clear out the message field.
      messageField.value = '';

      return false;
    };

    // Handle messages sent by the server.
    socket.onmessage = function(event) {
      var message = event.data;
      messagesList.innerHTML += '<li class="received"><span>Received:</span>' +
                                 message + '</li>';
    };

    };
    </script>

   <body>
   <b><font color="green">Django Chat</font></b>
    <div id="page-wrapper">
        <h1>WebSockets with Django and Channels</h1>

        <div id="status">Connecting...</div>

        <ul id="messages"></ul>

        <form id="message-form" action="#" method="post">
          <textarea id="message" placeholder="Write your message here..." required></textarea>
          <button type="submit">Send Message</button>
          <button type="button" id="close">Close Connection</button>
        </form>
      </div>
    </body>
  </html>
{% endblock %}

Ora vediamo che tutto funzioni a dovere.
Da un altro prompt dei comandi, lanciamo il server redis con l’eseguibile redis-server:

(venv) C:\tmp\redis>redis-server.exe
...
[1780] 15 Jan 12:11:24.518 # Server started, Redis version 3.2.100
[1780] 15 Jan 12:11:24.518 * DB loaded from disk: 0.000 seconds
[1780] 15 Jan 12:11:24.518 * The server is now ready to accept connections on port 6379

e lanciamo il nostro server Django:

(venv) C:\tmp\django-dev\djangosite>python manage.py runserver
Django version 2.1.5, using settings 'djangosite.settings'
Starting ASGI/Channels version 2.1.6 development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

Channels è attivo come si può notare:

Starting ASGI/Channels version 2.1.6 development server at http://127.0.0.1:8000/

Ora proviamo a raggiungere la pagina chat all’indirizzo http://127.0.0.1:8000/chat:


La console di django con debug attivo, mi direbbe che tutto funziona a dovere:

HTTP GET /chat 200 [0.05, 127.0.0.1:52135]
WebSocket HANDSHAKING /ws/chat/ [127.0.0.1:52139]
WebSocket CONNECT /ws/chat/ [127.0.0.1:52139]

Proviamo a scrivere un messaggio, otterremmo ciò che segue.

Il messaggio che spediamo, viene anche ricevuto inquanto per come abbiamo strutturato il consumer, facendo parte del gruppo su cui scriviamo, riceviamo lo stesso messaggio spedito.


Se ora aprissimo un secondo browser e ci collegassimo allo stesso indirizzo, la chat funzionerebbe.
Spediamo pertanto un paio di messaggi da entrambi i browser e verifichiamo che tutto funzioni.
Scrivo dal Secondo client:


Ricevo dal Primo client:


Scrivo dal Primo Client:


Ricevo dal Secondo Client:


Non resta che chiudere la connessione con l’apposito pulsante, disconnessione che verrà mostrata correttamente anche nella console di django:

WebSocket DISCONNECT /ws/chat/ [127.0.0.1:52200]
WebSocket DISCONNECT /ws/chat/ [127.0.0.1:52139]

Link utili:
django
django-channels
redis
example code on github

Django: favicon

13 Aprile 2018 Commenti chiusi

Django favicon

Scaricare l’immagine django desiderata ad es.

e ridimensionarla a 16×16, rinominandola favicon.ico.
Posizionarla nella directory /static/ della propria app.

/path_to_django_site/app_name/static/favicon.ico

Nella template base.html, dalla quale ereditiamo tutte le altre, inserire nel tag il link alla favicon:

{% load staticfiles %}
...

{% block head %}
   ...
  <link rel="shortcut icon" href="{%  static 'favicon.ico' %}">
{% endblock %}

Ricordarsi assolutamente di caricare gli staticfiles come indicato con:

{% load staticfiles %}

Assicurarsi che nel file settings.py del sito sia definita la costante STATIC_URL:

STATIC_URL = '/static/'

Avviato il server, la favicon dovrebbe essere correttamente caricata e visualizzata senza così incorrere nell’errore

[...] "GET /favicon.ico HTTP/1.1" 404 -

Categorie:Django, python Tag: ,

FantaStat 2.0

22 Novembre 2017 Commenti chiusi

Introduzione

Fstat 2.0 è una semplice applicazione per visualizzare le statistiche
dei giocatori di fantacalcio.
La precedente versione serviva a visualizzare i file.txt veccio formato, quelli che indicavano anche i goal segnati, gli assist ecc.
I file più recenti indicano solo il fanta voto, voto senza bonus malus e la quotazione:

code|NAME|TEAM|fanta voto|voto|quotazione

Link utili

Il codice dell’applicazione è reperibile nel repo corrispondente di github.
I binari win con database players.db qui.
I file txt delle giornate di fantacampionato sono reperibili sul blog.

Moduli utilizzati
Python 2.7.8
grafica: wx 3.0.2.0
ORM: Django 1.10.1

L’utilizzo è molto semplice e di seguito viene descritto:

Creazione database

Qualora ci si dimenticasse di creare il database per primo, al lancio dell’applicazione si verrà avvisati da un messaggio.

Ogni baco segnalato sarà ben gradito.

Creare quindi il database con il seguente comando:

python manage.py makemigrations players

Le migrations iniziali dovrebbero già essere presenti nel repo di github, quindi potrebbero non risultare modifiche, procedere quindi con il secondo comando:

python manage.py migrate

Avviare Fstat2.0

Per avviare l’applicazione eseguire lo script main.py con il comando:

python main.py

Non avendo ancora importato nessun giocatore, verremo avvisati da un messaggio che è necessario importare il primo file:

Le prime giornata disputate sono già fornite con il repo in posizione players/days, oppure sono sempre reperibili qui.
Dal menu Import-> Import players importare il file desiderato, solitamente la giornata 1 (MCC1.txt)

Il file dei giocatori va importato solo la prima volta, dopodichè il menu non sarà più attivo.
Da qui in avanti si importeranno solo i file dei voti.
Il primo sarà nuovamente MCC1.txt. Dal menu Import -> Import Evaluations, procedere con l’importazione.

Finita un’importazione eseguire un refresh dall’apposito pulsante.
L’interfaccia si presenta come in figura:

I radio-buttons permettono di filtrare i giocatori per ruolo, mentre cliccando sulle colonne della lista,
l’ordinamento avverrà in base alla colonna cliccata. Ad esempio cliccando su real_team, i giocatori
saranno ordinati per ruolo e per squadra di appartenenza.

Cliccando invece sulla “riga” di un determinato giocatore, sarà possibile modificarlo.

Dal menu Player -> Summary si accede alla lista riassuntiva del giocatori, poichè la visualizzazione della schermata del frame principale, mostra le medie dei giocatori e non i valori iniziali.
Da qui cliccando sui giocatori si possono modificare come in precedenza.

Qualora nei file non risultasse un giocatore, sarà possibile crearlo dal menu Players -> New Player

Stesso discorso vale per le valutazioni; esiste un sommario accessibile dal menu Evaluations -> Summary

dove cliccando sulla riga di una valutazione, sarà possibile modificarla

Come per i giocatori, sarà possibile aggiungere una valutazione ad hoc, dal menu
Evaluations -> New Evaluation

Come ultima operazione, è possibile cancellare tutti i dati, dal menu Reset.

Pyinstaller: django stand alone application spec file

20 Novembre 2017 Commenti chiusi

Relativamente ad applicazioni django stand-alone, descritte in questo articolo, qualora si dovessero creare i binari per windows con pyinstaller, è utile usare questo scheletro di file spec.

# -*- mode: python -*-

block_cipher = None


a = Analysis(['main.py'],
             pathex=['<path-of-application>'],
             hiddenimports=['htmlentitydefs',
                            'HTMLParser',
                            'Cookie',
			    'django.template.defaulttags',
			    'django.template.loader_tags',
                            'django.middleware.common',
                            'django.core.handlers.wsgi',
			    'settings'],
             hookspath=None,
             runtime_hooks=None,
             excludes=None,
             cipher=block_cipher)

pyz = PYZ(a.pure,cipher=block_cipher)

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name='fstat.exe',
          debug=False,
          strip=None,
          upx=True,
          console=True )

attenzione al pathex, la directory dove dovrà risiedere il binario risultante.

Negli hidden imports, come si nota, deve comparire anche il file settings.py, file descritto nell’articolo relativo alla struttura delle applicazioni django stand-alone.

La creazione del binario si eseguirà con il comando:

pyinstaller --onefile <my-app>.spec

Fantamanager 2.2

20 Settembre 2017 Commenti chiusi

Diversamente dalla versione precedente FantaLega 2.1 e dalla versione Light, qui è possibile gestire un campionato di n.squadre, a scontri diretti (con più di 8/10 squadre).
Il giocatore non potrà più appartenere a più squadre ma è prevista un’asta.

Opzioni:
Asta
Costruzione Squadre
Generazione calendario
Modifica Squadre
Consegna formazioni
Modifica Formazioni
Importazione Giocatori e Voti Gazzetta
Modifica Giocatori
Mercato tra squadre
Calcolo punteggio formazioni
Classifica Squadre

Grafica: WX
Django per l’ORM

Utilizzare virtualenv ed installare i requirements:

pip install -r requirements.txt

Creare il database:

python manage.py makemigrations fantalega
python manage.py migrate

lanciare l’applicazione con il comando:

python main.py

Il link di github:
repo su github

Fantalega: gestore Asta

1 Settembre 2017 Commenti chiusi

Ieri è terminata ufficialmente la sessione di calciomercato.
Molti fanta-allenatori hanno rischiosamente già effettuato le loro aste,
molti altri (noi), NO.

Ecco un tool parecchio spartano che velocizzerà le annotazioni
squadra-giocatore durante l’asta.

Auction1.0 è un gestore di aste di fantacalcio scritto in python.
Per la grafica sono state utilizzate le librerie wx, mentre per la
gestione del database e l’ORM, è stato utilizzato django.

Requisiti: installare django e wxpython

Su github è presente una prima bozza di repo.

Per prima cosa creare i database con i due comandi:

python manage.py makemigrations auction
python manage.py migrate

dopo queste operazioni che creeranno il database con le relative tabelle, è
possibile avviare la app:

python main.py

per prima cosa importiamo i giocatori utilizzando i soliti file txt in
formato Gazzetta, reperibili qui:

E’ possibile importare i giocatori più e più volte in modo da aggiornare
la lista dei calciatori.
Importati i giocatori, è necessario creare le squadre.

Dopo aver creato le squadre, si comincia l’asta.

Si seleziona il giocatore per squadra reale di appartenenza,
si utilizzano i filtri di ruolo e si assegna un costo d’asta e la squadra
che si aggiudica il giocatore. Durante l’asta è possibile avere un resoconto
delle squadre, con budget rimanente e giocatori ancora da comprare

Dai menu Teams e Players è possibile editare le squadre e i giocatori
per apportare delle modifiche. E’ anche possibile aggiungere giocatori che,
in fase d’asta, non sono ancora stati ufficializzati da Gazzetta e quindi
non presenti nei file.

Terminata l’asta è possibile effettuare scambi tra fanta-allenatori con il menu
“Trades”.

Nota: la casella max trades del pannello edit-team indica le massime operazioni
di mercato concesse durante la fantalega, mutuate dall’applicazione completa.
Queste operazioni (Trades) in fase di asta, sono invece illimitate.

Come ultima operazione, è possibile esportare un file in formato csv (menu Auction).

django: standalone application

3 Aprile 2017 Commenti chiusi

Supponiamo di dover creare una piccola applicazione (‘players’)
che inserisca in un database dei dati, ad esempio i nomi
dei calciatori della serie A.
Ci piace utilizzare django per la confidenza che abbiamo con il suo ORM,
ma non abbiamo voglia di sbatterci con tutto il meraviglioso e vastissimo
mondo di django, comprensivo di pattern Model-View-Template, Admin, browser…
Vogliamo invece usare le wx, ma non SQLAlchemy, o peewee.
Non resta che strutturare la nostra mini applicazione in questo modo:

C:\TMP\STANDALONEDJANGOEXAMPLE
|   main.py
|   manage.py
|   settings.py
|   __init__.py
|   
\---players
    |   controller.py
    |   model.py
    |   models.py
    |   view.py
    |   __init__.py
    |   
    \---migrations
            __init__.py

partendo dall’alto, analizziamo i singoli file.

‘main.py’ è il cuore della nostra applicazione django STAND-ALONE e contiene
i settaggi che ci permetteranno di utilizzare solo l’ORM di django:

import os

# qui importiamo il modulo settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")


import sys
# qui aggiungiamo il percorso della nostra app al path
sys.path.append('/tmp/standalonedjangoexample')
# qui aggiungiamo i virtualenv site-packages al sys.path
sys.path.append('/tmp/venv/Lib/site-packages')


from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()


if __name__ == '__main__':
    from players.controller import Controller
    c = Controller()

questo è il contenuto di ‘manage.py’, necessario per le migrations:

import os
import sys

sys.dont_write_bytecode = True

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

poi abbiamo i ‘settings.py’:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'players.db',                      # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

INSTALLED_APPS = (
    'players',
    )

SECRET_KEY = 'mysecretkey'

Ora entriamo nel cuore della app, che prevede:
– i models che django utilizzerà con il suo ORM
– i file per uno spartano MVC

Questo spartano MVC prevede una file view.py che importa il modulo wx
per creare un’interfaccia di inserimento dati, un model.py per la gestione
dei dati e un controller che metta interagisca con essi tenendoli separati.

Il file models.py (django models)

from django.db import models


class Player(models.Model):
    name = models.CharField(max_length=32)

    def __unicode__(self):
        return self.name

il file view.py

import wx


class InfoMessage(wx.MessageDialog):
    """Simple message Dialog"""
    def __init__(self, parent, message):
        super(InfoMessage, self).__init__(parent, message, 'info', wx.OK |
                                          wx.ICON_EXCLAMATION)
        if self.ShowModal() == wx.ID_OK:
            self.Destroy()


class ViewNewPlayer(wx.Frame):
    def __init__(self, parent, controller, title):
        self.title = title
        super(ViewNewPlayer, self).__init__(parent=parent, title=title,)
                                         #style=STYLE)
        self.controller = controller
        self._build()
        self._bind_widgets()
        self.Centre()
        self.Show()

    def _build(self):
        self.panel = PanelNewPlayer(parent=self)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel, 1, wx.EXPAND)
        self.SetSizer(sizer)
        self.SetSize((250, 100))

    def _bind_widgets(self):
        self.Bind(wx.EVT_BUTTON, self.on_quit, self.panel.btn_quit)
        self.Bind(wx.EVT_BUTTON, self.on_save, self.panel.btn_save)
        self.Bind(wx.EVT_TEXT, self.on_text_entry, self.panel.name)

    def show_message(self, message):
        InfoMessage(parent=self, message=message)

    def on_text_entry(self, event):
        entry = self.panel.name.GetValue()
        if len(entry) > 2:
            self.panel.btn_save.Enable()
        else:
            self.panel.btn_save.Disable()

    def on_save(self, event):
        name = self.panel.name.GetValue()
        self.controller.save_player(name)

    def on_quit(self, event):
        self.Destroy()


class PanelNewPlayer(wx.Panel):
    def __init__(self, parent):
        super(PanelNewPlayer, self).__init__(parent)
        # Attributes
        self.name = wx.TextCtrl(self)
        text_sizer = wx.FlexGridSizer(rows=2, cols=2, hgap=5, vgap=5)
        text_sizer.Add(wx.StaticText(self, label=''),
                       0, wx.ALIGN_CENTER_VERTICAL)
        text_sizer.AddGrowableCol(1)
        button_sizer = wx.StdDialogButtonSizer()
        self.btn_save = wx.Button(self, wx.ID_SAVE)
        self.btn_quit = wx.Button(self, wx.ID_CANCEL)
        # Buttons initialization
        self.btn_quit.SetDefault()
        self.btn_save.Disable()
        # Sizers
        button_sizer.AddButton(self.btn_save)
        button_sizer.AddButton(self.btn_quit)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(text_sizer, 1, wx.EXPAND | wx.ALL, 5)
        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        button_sizer.Realize()
        self.SetBackgroundColour('LightGray')
        self.SetSizer(sizer)

il file model.py (MVC) che si interfaccerà a sua volta con i models di django:

from models import Player


class Model(object):

    @staticmethod
    def new_player(name):
        """new_player(name) -> player object"""
        player = Player.objects.create(name=name.upper())
        return player

    @staticmethod
    def get_player(name):
        """get_player(name) -> player object"""
        return Player.objects.filter(name=name.upper()).first()

    @staticmethod
    def get_players():
        """get_players() -> iterable"""
        return Player.objects.all()

il file controller.py

import wx
from model import Model
from view import ViewNewPlayer


class Controller(object):
    def __init__(self):
        app = wx.App()
        self.model = Model()
        self.view = ViewNewPlayer(parent=None, controller=self,
                                  title='New Player')
        app.MainLoop()

    def get_player(self, name):
        return self.model.get_player(name)

    def save_player(self, name):
        player_object = self.get_player(name)
        if player_object:
            self.view.show_message("Player already exist")
        else:
            new_player = self.model.new_player(name)
            self.view.show_message("New Player %s saved" % new_player.name)

Questo è quanto.
Ovviamente non esistono ancora le tabelle del database e provvediamo con i
comandi:

(venv) C:\tmp\standalonedjangoexample>python manage.py makemigrations
Migrations for 'players':
  players\migrations\0001_initial.py:
    - Create model Player

e successivamente:

(venv) C:\tmp\standalonedjangoexample>python manage.py migrate
Operations to perform:
  Apply all migrations: players
Running migrations:
  Applying players.0001_initial... OK

poi è possibile eseguire la nostra applicazione utilizzando il file
main.py.

Ovviamente la struttura dell’applicazione può essere fatta in mille modi
diversi, come pure il raggruppamento dei codici.
Lo stesso main.py può fungere da controller riducendo i livelli di
separazione, soprattutto qualora non si volesse utilizzare un
pattern simil-MVC.

Categorie:Django, python Tag: ,

django – RuntimeWarning: DateTimeField received a naive datetime

2 Febbraio 2017 Commenti chiusi

Abbiamo un form

class ChangeForm(forms.Form):
    date = forms.DateField(label='date', initial=datetime.date.today)
    ...

e la view di competenza

def edit(request, item_id):
    myitem = get_object_or_404(AItem, pk=item_id)
    if request.method == "POST":
        form = ChangeForm(request.POST)
        if form.is_valid():
            myitem.date = form.cleaned_data['date']
            myitem.save()
            return redirect(...)
    else:
        form = ChangeForm(initial={'date': myitem.date})
    return render(request, 'atemplate.html',
                  {'form': form, 'myitem': myitem})

nel momento del salvataggio dei dati (POST) ottengo il warning in oggetto.

per risolvere il problema, tramite timezone di django, trasformiamo le date
da formato ‘naive’ a formato ‘aware’.
Nel mio caso la data è di tipo datetime.date per tento prima,
la trasformerò in formato datetime.datetime.

>>> import datetime
>>> from django.utils import timezone
>>> a_date = datetime.date.today()
>>> a_datetime = datetime.datetime.combine(a_date, datetime.time())
>>> a_datetime
datetime.datetime(2017, 2, 2, 0, 0)
>>> aware_date = timezone.make_aware(a_datetime, timezone.get_default_timezone())
>>> aware_date
datetime.datetime(2017, 2, 2, 0, 0, tzinfo=<UTC>)

oppure tramite il modulo pytz (installabile tramite pip):

>>> import pytz
>>> naive = datetime.datetime(2017, 2, 2, 12, 0, 0)
>>> aware = datetime.datetime(2017, 2, 2, 12, 0, 0, 0, pytz.UTC)
>>> aware == naive
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> aware == pytz.utc.localize(naive)
True

ora basta modificare la view con la gestione corretta delle date:

def edit(request, item_id):
    myitem = get_object_or_404(AItem, pk=item_id)
    if request.method == "POST":
        form = ChangeForm(request.POST)
        if form.is_valid():
            naive_date = form.cleaned_data['date']
            naive_datetime = datetime.datetime.combine(naive_date, datetime.time())
            myitem.date = timezone.make_aware(naive_datetime, 
                                              timezone.get_default_timezone())
            myitem.save()
            return redirect(...)
    else:
        form = ChangeForm(initial={'date': myitem.date})
    return render(request, 'atemplate.html',
                  {'form': form, 'myitem': myitem})
Categorie:Django, python Tag: ,

Django: in_memory_zip di più in_memory_files

22 Novembre 2016 Commenti chiusi

Il concetto base è di non creare file su disco,
ma utilizzare file in_memory.
Dovranno essere “volanti” quindi, sia i file
da zippare, sia il file zip stesso, che verrà
in seguito servito da django.

Semplifichiamo il tutto:

ho una funzione che genera lo zip:

from zipfile import ZipFile
from StringIO import StringIO


def zip_files():
    zip_sio = StringIO()
    zip_file = ZipFile(zip_sio, 'w')

    for line in ['a', 'b', 'c', 'd']:
        name = 'file_{}.txt'.format(line)
        outfile = StringIO()
        outfile.write(line)
        zip_file.writestr(name, outfile.getvalue())
        outfile.close()

    zip_file.close()
    zip_sio_data = zip_sio.getvalue()
    zip_sio.close()
    return zip_file, zip_sio_data

chiaramente potrebbe essere più complesso, ad esempio una
funzione che prenda in ingresso un file, lo splitti in
più parti le quali vengano restituite sotto forma di zip…

from zipfile import ZipFile
from StringIO import StringIO


def zip_files(file_in):
    for line in file_in.readlines():
        ...

In pratica utilizziamo degli oggetti StringIO, ovvero oggetti File-like,
all’interno dei quali possiamo scrivere delle stringhe, con il metodo ‘write’,
proprio come gli oggetti file.
Con il metodo ‘getvalue’, otteniamo tutto il contenuto dell’oggetto, che una
volta chiuso con ‘close’, non è più accessibile. Per questo motivo lo inseriamo
all’interno del file-like object zip prima di chiuderlo.
Gli stessi dati del file zip, li memorizziamo prima di chiudere lo stesso flie-like
zip, in modo da poterli avere disponibili ad esempio nella view di django
prima di costruire il ‘response’…

comunque nel nostro esempio avremo:

>>> z, data = zip_files()
>>> [o.filename for o in z.filelist]
['file_a.txt', 'file_b.txt', 'file_c.txt', 'file_d.txt']
>>> len(data)
410

Come già detto, ‘data’ tornerà utile quando con django andremo a servire il file zip
per il download, ad es.:

...
def a_view(request):
    if request.method == "POST":
        form = a_form(request.POST, request.FILES)
        if form.is_valid():
            file_in = request.FILES['a_form_file_field']
            zip_file, data = zip_file()  # or zip_file(file_in) if you process a file
            response = HttpResponse(data,
                                    content_type="application/x-zip-compressed")
            response['Content-Disposition'] = 'attachment; filename=a_file_zip.zip'
            return response
    else:
        form = a_form()
    return render(request, 'a_template.html', {'form': form})

Con questo sistema, non scriveremo nessun file sul server.

Categorie:Django, python Tag: ,

djangofantalega 1.0

15 Novembre 2016 Commenti chiusi

Djangofantalega

Djangofantalega è un progetto realizzato con
python e django.

Si tratta di un gestore di fantalega, dalla A alla Z.
Si creano: stagione, lega, squadre.
Si effettua l’asta sui giocatori e composte le squadre,
si crea il calendario e si dà inizio al campionato.
Ad ogni giornata si consegnano le formazioni e, dopo
la pubblicazione dei voti ufficiali, si procede
con i conteggi.

INDICE

1 – Virtualenv e Git
2 – Models: Season
3 – Admin: Login e Logout
4 – Models: League
5 – Models: Team
6 – Models: Match
7 – Models: Player
8 – Asta
9 – Models: Lineup
10 – Models: Trade
11 – Asta di riparazione
12 – Classifica

Categorie:Django Tag: