Blog-Layout

PDVM System wie geht es weiter

Norbert Peters • 24. Juli 2019

Ein Anfang bis zum Start

Nun ist es so, dass ich mit solchen Dingen wie ein Blog erst gerade anfange und wirklich ein Neuling bin. Dieses tut nichts zur Sache. Ich habe mir vorgenommen, ein flexibles System zu schaffen, mit dem so die meisten Anforderungen im Bereich eines Unternehmens verwaltet werden können.

So habe ich mich mit unterschiedlichen Dingen beschäftigt und bin zu der Erkenntnis gelangt, dass es am sinnvollsten wäre es mit Phython/Django und das Frontend mit React/Hooks zu machen. Wie die Anforderungen und die Verteilung dann werden kommt zu einem späteren Zeitunkt.

So und nun zu der Website PDVM.de. Diese habe ich vom Grundsatz her mal erstellt und entsprechend meinen Vorstellungen gegliedert. Dieses bedeutet aber nicht, dass im Laufe der Zeit noch Änderungen stattfinden. Die echten Inhalte schaffe ich im Laufe der Zeit. Dieses kann daran erkannt werden, dass dann in den Zusammenfassungen ein Button oder Link ist, der auf den Inhalt verzweigt.  Begleiten werde ich die einzelnen Bereiche hier im Blog und auf neue Inhalte hinweisen.

Sobald meine Seite veröffentlicht ist und damit auch dieser Blog habe ich schon einige Grundlagen zusammengetragen. Wie zügig es sich entwickelt kann ich heute noch nicht sagen. Vielleicht gibt es im Laufe der Zeit ein paar Interessierte an diesem Thema und werden sich beteiligen.

Zum Start an Technik habe ich einen Laptop (damit ich überall arbeiten kann) mit VS Code, da sich dieser aus meiner Sicht als sehr praktisch erwiesen hat.

Damit es von vorn herein in die richtige Richtung geht werde ich die Anforderungen als erstes in der Grundform theoretisch beschreiben und damit dann starten.

Anmerkung:
Diese Anmerkung habe ich zum Start der Web-Site eingefügt. Ich bin so weit gekommen, dass das Datum/Zeitformat (pdvm_datetime) geschaffen wurde und zur Verfügung steht. Zuerst wollte ich das Projekt auf der Seite von Django weit bringen, da aber viele Dinge Auwirkungen auf das Frontend haben, werde ich micht erst mal mit dem Frontend unter React beschäftigen und dann das Projekt technisch neu aufsetzten. Die grundlegende Vorgehensweise bleibt, wie nachfolgend beschreiben. Mit Neubeginn, gibt es einen neuen Blog.

Wenn einer Anmerkungen oder Fragen zu meinen Ideen hat, können diese in Rahmen dieses Blogs oder unter Kontakt angebracht und diskutiert werden.

22.12.2019  Norbert Peters

So nun fange ich mal an

Nachdem ich nun einfach mal grob meine Ideen in www.pdvm.de beschrieben habe fange ich mal an um zu sehen wie ich weiter komme, ob es so geht wie ich mir gedacht habe oder ob mir die Technik einen Strich durch die Rechnung macht.

Der 1. Schritt den ich mache ist so, dass ich mir ein Django Projekt schaffe, für die Authentifizierung mit ein KontoObjekt account mache und versuche die Grundstruktur für die historische Datenhaltung gleich einbeziehe. Meine Erläuterungen werden keine Schritt für Schritt Anweisungen sein. Ich setzte voraus, dass ihr Euch an anderer Seite zu Beginn mit dem Thema Django, DjangoProjekt, DjangoApp befasst habt.

Grundsätzlich versuche ich mit den Neuesten Versionen der genutzten Funktionalitäten zu arbeiten und ggf. auch darauf umzusteigen, sofern es logisch Sinn macht. Beim Backend mit Python/Django bin ich für den ERP Bereich sowieso bis heute ein Exote. Hat den Vorteil, dass man mit der modernen Technik aufzeigen kann, dass diese auch in diesem Bereich Vorteile hat. Wenn ich dann im näcshten Schritt ein Frontend mit React und Hooks mache, dann ist es sowieso für Entwickler im ERP Bereich von heute ein Unding, da wir dann die gelernten Strukturen verlassen. Aber dieses ist gerade das Interessante an diesem Thema mit der Technik.

Ich habe mal Python 3.7.3 hier auf meinem Rechner, damit werde ich in nächster Zeit unter Windows arbeiten.

  • Ich habe mit für das Projekt ein Verzeichnis pdvm-master angelegt.
  • dann eine virtuelle Umgebung
==>  ./pdvm-master> virtualenv venv
Hiermit legen wir eine virtuelle Umgebung im Verzeichnis venv an. Damit ich diese bequem aktivieren und deaktivieren kann, habe ich mir zwei kleine Batchdateien gemacht.

v_a.bat
venv\\Scripts\\activate

zum aktivieren der virtuellen Umgebung und

v_d.bat
venv\\Scripts\\deactivate

zum Deaktiviren der virtuellen Umgebnung.

Dann muss man im Terminal nur noch v_a eingeben um die vitruelle Umgebung zu aktivieren und v_d zum Deaktivieren.

Die aktivierte virtuelle Umgebung wird ist an dem (venv) am Anfang der Komandozeile im Terminal sichtbar.

So weit so gut nun fangen wir an Django einzurichten. Ich benutze hier vorerst nun Django 2.2.4.

Nun starten wir das DjangoProjekt pdvm_system
==> (venv) C:\Python\pdvm-master>django-admin startproject pdvm_system

Mit diesem Startprojekt haben wir im Verzeichnis pdvm_system die Basisapplikation im Verzeichnis pdvm_system
Diese Verzeichnisstruktur müsste nun vorhanden sein:

>node_modules
pdvm_system
pdvm_system
__init__.py
settings.py
urls.py
wsgi.py
manage.py
>venv
package-lock.json
v_a.bat
v_d.bat

Für unsere eigenen Funktionalitäten machen wir einfach eine DjangoApp bzw. ein Modul 'pdvm_basis'. Wie wir es dann genau handhaben werden wir sehen.

==> (venv) C:\Python\pdvm-master\pdvm_system>django-admin startapp pdvm_basis

Für unser Konto account in der Berechtigung brauchen wir auch eine DjangoApp bzw. ein Modul 'authentication'.

==> (venv) C:\Python\pdvm-master\pdvm_system>django-admin startapp authentication

So nun werden wir mal etwas parametrisieren, damit wir für die Anmeldung nicht auth sondern unseren account in authentication verwenden.

So nun mal in pdvm_system die settings.py bearbeiten.
Wir verwenden die intern eingestellt SQLITE Datenbank. Damit wir einfach in der Datenbank schauen können installieren wir uns einfach mal das SQLiteStudio ich habe es in der Version 3.2.1. Nun geben wir in den Settings und geben der Datenbank einen Namen. Wir nennen sie mal pdvmbase1.db.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'pdvmbase1.sqlite3'),
    }
}


Bei den Applikationen fügen wir nun unsere authentication hinzu.

# Application definition

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


Am Ende fügen wir

# UserModell von Django wird auf 'authentication.Account' umgestellt.
AUTH_USER_MODEL = 'authentication.Account'

hinzu.

Da wir hier ja mit der deutschen Sprache entwickeln und unsere Tests machen, sollten hier folgende Parameter in den Settings vorhanden sein.

TIME_CODE = 'DEU'
LANGUAGE_CODE = 'de-de'
TIME_ZONE = 'Europa / Berlin'

Wir werden diese später auch von dieser Stelle aus verwenden und uns anschauen wir unser System hier auf Veränderungen reagiert.

Was uns jetzt fehlt ist das Model zu authentication. Etwas gibt es noch vorher zu tun. Wr wollen ja als id nicht eine einfache Zahl sondern eine GUID haben. Hier werden wir immer wieder eine neue GUID benötigen, also machen wir hierfür eine Funktion. Außerdem werden wir nach meinen Vorstellungen für bestimmte allgemeine Dinge und später vielleicht einen Root-Mandanten statische GUIDs verwenden. Dieses sind GUIDs, die sich aus einem zugelassenen Zeichen zsammensetzen. Dieser String muss den HEX Konventionen entsprechen.

In unserem Modul pdvm_basis habe ich nun eine Funktionslibrary erstellt die in der Datei pdvm_util.py gespeichert wird.

Noch einen Hinweis, alle Python Dateien, die gekapselte Funktionalitäten enthalten werden so gestaltet, dass dies bei direkter Ausführung immer einen Test durchlaufen und das Ergebnis ausgeben. Dieses in der Art zu machen hat Vorteile. Bei der Entwicklung von solchen Funktionalitäten können diese schon während der Entwicklung getestet und sauber fertig gestellt werden.

# pdvm_util.py
# ------------------------------------------------------------------
# 01.07.2018 Norbert Peters
# ------------------------------------------------------------------
""" Eine Sammlung von viel verwendeter Funktionen
--- getNewId --> liefert eine neue UUID/GUID im Format string zurück
--- getStaticId --> liefert eine statische UUID/GUID aus einem Zeichen zurück
--- __checkHexValue --> prüft ob Zeichen für einen Hex String zugelassen sind
"""
# ------------------------------------------------------------------
import uuid                                 # import Modul uuid

def getNewId():                             # Definition der Funktion
    return str(uuid.uuid4())                # neue UUID/GUID

def getStaticId(zahl):                      # Definition der Funktion
    if __checkHexValue(zahl):
        t=""
        z = 8
        for i in range(36):                 # statik UUID/GUID aus zahl
            if i == z:
                if z < 23:
                    z += 5
                t+="-"
            else:
                t+=str(zahl)
        return t
    else:
        r = "Eingabezeichen "+str(zahl)+" ist nicht zugelassen!"
        return r

def __checkHexValue(zeich):                 # Prüfung zugelassener Zeichen
    zeichen = [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f']
    if zeich in zeichen:
        return True
    else:
        return False
   
           

# ------------------------------------------------------------------
# Hauptprogramm - Testumgebung
# ------------------------------------------------------------------
if __name__=='__main__':                    # Test der Funktionen, wenn
    # Test der Funktion getNewId()          # die Funktionsbibliothek
    b=getNewId()                            # direkt aufgerufen wird
    print("T e s t  -- getNewId() -- Funktion öffentlich")
    print("=============================================")
    print("Neue UUID/GUID: "+b)
print("Neue UUID/GUID: "+getNewId())

    # Test __checkHexValue
    print("\nT e s t  -- __checkHexValue() -- Funktion privat")
    print("================================================")
    print("Zeichen 0 "+str(__checkHexValue(0)))
    print("Zeichen 9 "+str(__checkHexValue(9)))
    print("Zeichen a "+str(__checkHexValue('a')))
    print("Zeichen f "+str(__checkHexValue('f')))
    print("Zeichen g "+str(__checkHexValue('g')))
    print("Zeichen z "+str(__checkHexValue('z')))
   
    # Test der Funktion staticId(x)  (x = 0-9 oder a-f)
    # dieses sind die ggf. zugelassenen statischen Ids
    print("\nT e s t  -- staticId() -- Funktion öffentlich")
    print("=============================================")
    for i in range(10):
        print("Statische UUID: "+getStaticId(i))
    zeichen = ['a','b','c','d','e','f']
    for i in zeichen:
        print("Statische UUID: "+getStaticId(i))
    # Fehlerprüfung Test
    print("gibt es dieses? "+getStaticId('x'))
    print("gibt es dieses? "+getStaticId('12'))
    print("gibt es dieses? "+getStaticId('aa'))
    print("gibt es dieses? "+getStaticId('b1'))

So nun zu den Einzelheiten ist das Meiste für sich schon klar. Ich versuche, immer allse so zu machen, dass auch ein nicht Fachkundiger schnell erkennen kann was hier geschieht. Zumal ja am Ende im Testbereich schon die Beispiele sind.
Nun können wir in authentication unsere models.py setzten. Diese hat ein paar Besonderheiten, da hier ja die Tabelle auth in der Datenbank durch die Tabelle account erstetzen soll. Danneben noch die id als UUID und noch ein paar Felder dazu. Die Datumsfelder sind hierbei noch mit dem Timestamp aus Python sersehen. Das machen wir erst in einem weiteren Schritt.

authenication.models.py
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from pdvm_basis.pdvm_util import getNewId

# Klasse für das Management des Account
class AccountManager(BaseUserManager):
    def create_user(self, username, email, password, **kwargs):
        if not email:
            raise ValueError('Das Benutzerkonto benötigt eine valide Email-Adresse!')

        if not username:
            raise ValueError('Das Benutzerkonto muss eine validen Namen haben!')

        account = Account(
            id = getNewId(),
            email = email,
            username = username,
            first_name = kwargs.get('first_name'),
            last_name = kwargs.get('last_name')
        )

        account.set_password(password)
        account.save()

        return account

    def create_superuser(self, username, email, password, **kwargs):
        account = self.create_user( username, email, password, **kwargs)

        account.is_admin = True
        account.save()

        return account

# Django Modell für den 'Account' - erzeugt Kommunikation mit der Datenbank
class Account(AbstractBaseUser):
    # id wird mit einer UUID belegt
    id = models.UUIDField(primary_key=True, default=getNewId ,unique=True)
    # Email Adresse zwingend erforderlich
    email = models.EmailField(unique=True)
    # Benutzername zwingend erforderlich - maximal 50 Zeichen lang
    username = models.CharField(max_length=50, blank=False)
    # Vorname nicht zwingend - maximal 50 Zeichen lang
    first_name = models.CharField(max_length=50, blank=True)
    # Nachname nicht zwingend - maximal 50 Zeichen lang
    last_name = models.CharField(max_length=50, blank=True)
    # tagline nicht zwingend - maximal 150 Zeichen lang
    tagline = models.CharField(max_length=150, blank=True)
    # Kurzkommentar - maximal 300 Zeichen lang
    comment = models.CharField(max_length=300, blank= True)

    # Kennzeichen für Adminberechtigung - default nein
    is_admin = models.BooleanField(default=False)

    # Erstellungsdatum - DateTime Feld
    created_at = models.DateTimeField(auto_now_add=True)
    # Änderungsdatum - DateTime Feld
    updated_at = models.DateTimeField(auto_now=True)

    objects = AccountManager()
    # Werte von Django User setzen
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username', 'first_name', 'last_name']

    def __unicode__(self):
        return self.email
   
    def get_full_name(self):
        return ' '.join([self.first_name, self.last_name])

    def get_short_name(self):
        return self.first_name
    @property
    def is_superuser(self):
        return self.is_admin

    @property
    def is_staff(self):
       return self.is_admin

    @property
    def is_activ(self):
       return True

    def has_perm(self, perm, obj=None):
       return self.is_admin

    def has_module_perms(self, app_label):
       return self.is_admin

    @is_staff.setter
    def is_staff(self, value):
        self._is_staff = value

Durch die Kommentare müsste weitgehend klar sein, was hier passiert. Die Properties zum Schluss werden benötigt, damit wir es dann auch im DjangoAdmin anzeigen können.

Als erste machen wir nun einen makemigrations

--> (venv) C:\Python\pdvm-master\pdvm_system>python manage.py makemigrations
--> Migrations for 'authentication':
  authentication\migrations\0001_initial.py
    - Create model Account

Der nächste Schritt ist es nun, die Modelle auf die Datenbank zu migrieren

--> (venv) C:\Python\pdvm-master\pdvm_system>python manage.py migrate
--> Operations to perform:
Apply all migrations: admin, auth, authentication, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying authentication.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK

Wenn wir uns nun die Datenbank mit dem SQLiteStudio ansehen, dann sehen wir keinen auth.user sondern einen authentication_account. Der unsere Felder hat. Um zu sehen ob dieses in Django funktioniert und ob bestimmte Dinge auch korrekt ankommen legen wir einen Superuser an.

--> (venv) C:\Python\pdvm-master\pdvm_system>python manage.py createsuperuser
-->Email: norbert.peters@pdvm.de
Username: norbert.peters
First name: Norbert
Last name: Peters
Password:
Password (again):
Superuser created successfully.

Hier könnt ihr eure eigenen Daten eingeben. Mit dem SQLite Studio können wir dann sehen wie der Superuser in der Datenbank abgelegt wird. Die id ist eine UUID, das Passwort ist ein Hashcode.

Wenn es interessiert kannst du auch einen weiteren Superuser anlegen und sehen wie der in der Datenbank ankommt.

Mit dem Superuser kannst du dich nun auch beim DjangoAdmin anmelden.

--> (venv) C:\Python\pdvm-master\pdvm_system>python manage.py runserver

im Browser  http://localhost:8000/admin

Unsere authentication.account können wir hier aber noch nicht sehen. Dazu brauchen wir noch ein paar Anpassungen.

Wir müssen diese im admin.py registrieren:

admin.py
from django.contrib import admin
from authentication.models import Account

admin.site.register(Account)

Hinweis:
Wenn Sie im Admin ein neues Konto anlegen, wird einen neue UUID vergeben, die DAten können wir pflegen nur ein Passwort vorgeben geht hier nicht, da es nicht umgerechnet wird. Ich habe nicht weiter danach gesucht, da ich ja vorhabe, die Konten in einem React Frontend zu pflegen. Wir nehmen den Admin nur jetzt in der Phase in der wir unserer Strukturen aufbauen, damit wir einfach auch Daten anlegen können.


Nun, ab zum Zeitpunkt

Wir haben nun ein Konto geschaffen, mit dem wir später unsere Login machen können. Wir werden uns nun mit dem Datum-, Zeitformat beschäftigen um dieses dann als erstes in unseres Konto einzubeziehen und auch dieses Objekt mit den entsprechenden Eigenschaften zu versehen. Es wird nach meiner Logik ein Objekt sein, dass in die einheitliche historische Struktur passt, aber ohne Historie gepflegt wird.

Das Datum- / Zeitformat habe ich auf meiner Seite grob beschrieben, nun wollen wir es umsetzen. In Django (Phyton) haben wir einen Timestamp, der uns heute ausgegeben wird. Ich denke mal, dass wir diesen mal in ein Datum und Zeit umsetzen.

Dazu schaffen wir uns einen neue Datei pdvm_datetime.py in unserem Modul pdvm_basis. Ja was wollen wir eigentlich machen? Es ist so, dass wir ein datetime objekt machen, dass den Inhalt über verschiedene Formen erhalten kann:
  • Python datetime
  • Jahr, Monat, Tag, Stunden, Minuten, Sekunden, Milli- bzw. Microsekunden
  • pdvm_datetime
  • pdvm_date
  • pdvm_time
Das Objekt hat dann in der Endausbauphase viele einzelne Parameter wie:
  • Python datetime
  • Jahr, Monat, Tag, Stunden, Minuten, Sekunden, Milli- bzw. Microsekunden // auch einzeln
  • pdvm_datetime
  • pdvm_date
  • pdvm_time
  • Tag im Jahr
  • Wochentag
  • Kalenderwoche
  • Kalendermonat (als Wort)
  • Schaltjahr
Rechenfunktionen:
  • addYears
  • addMonths
  • addDays
  • addHours
  • addMinutes
  • add Secunds
  • addDateTime(JJJJ, MM, DD, hh, mm, sec, msec)
  • diffDateTime
  • diffDate
  • diffTime
Also etwas Arbeit bis wir hier durch sind:

Wir erarbeiten dieses uns direkt in der Dokumentation meiner Website zum PDVM_DateTime Format.

Mit der Entwicklung von pdvm_datetime.py wurde die pdvm_util.py erweitert. Siehe dazu die Dokumentation. Dazu kam dann noch die pdvm_langtext.py. Auch hier gehts zur Dokumentation.

So nun ist erst man das Ganze um die Pdvm_DateTime soweit fertig. Ich werde es erst mal in meinem System einsetzten und damit prüfen, ob noch Änderungen erforderlich sind. Wenn es auf Dauer stabil läuft werden ich dann ein eigenes selbständiges Modul daraus machen.





Share by: