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
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 venvHiermit 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.