Home > Django, python > django: standalone application

django: standalone application

3 Aprile 2017

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: ,
I commenti sono chiusi.