ORMs oder Object Relational Mappings sind Systeme, die es uns beim programmieren erlauben, in der Programmiersprache unseres vertrauens zu bleiben und dennoch mit Datenbanken zu interagieren.
In Django beispielsweise werden Models definiert, die die Daten repräsentieren, die wir gerne in einer Datenbanktabelle abbilden möchten. Beispielswesie einen Kunden
class Customer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
customer_number = models.PositiveIntegerField(blank=True, null=True)
zip_code = models.CharField(max_length=20)
# additional fields...
Dann können wir im Python code so beispielsweise einen neuen Kunden anlegen:
mycustomer = Customer(
name="Screenion GmbH",
customer_number=3215,
zip_code="61440"
)
mycustomer.save()
Mit .save()
wird SQL Code generiert, der den neuen Kunden in die Datenbank schreibt.
Für unseren Kundenstamm ist jedoch wichtig, dass kein Kunde doppelt angelegt wird. Normalerweise bietet sich für diese Aufgabenstellung an, zu fordern, dass beispielswesie die Email Adresse eindeutig ist. Wir haben in diesem Beispiel jedoch kein Feld für die Email adresse. Stattdessen fordern wir, dass die Kombination aus Namen, Kundennummer und Postleitzahl eindeutig sein soll. Das lässt sich so im Model mit Constraints abbilden, zum Beispiel so:
class Customer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
customer_number = models.PositiveIntegerField(blank=True, null=True)
zip_code = models.CharField(max_length=20)
class Meta:
constraints = [
UniqueConstraint(
"name",
"customer_number",
"zip_code",
name="unique_name_customer_number_zip_code"
),
]
Hierbei haben wir jedoch einen Fehler gemacht, der nicht offensichtlich ist. Die Kundennummer ist optional. Wird sie nicht angegeben, steht in der Datenbank "null". Die Datenbank vergleicht bei Eindeutigkeitsrandbedingungen (UniqueConstraints) die einzelnen Felder die in der Bedingung angegeben worden sind. Dabei sind zwei "null" - Werte nicht gleich, sondern werden von der Datenbank als unterschiedlich betrachtet. Wir können also ohne, dass wir von der Datenbank aufgehalten werden, folgendes tun:
mycustomer = Customer(
name="Screenion GmbH",
zip_code="61440"
)
mycustomer.save()
othercustomer = Customer(
name="Screenion GmbH",
zip_code="61440"
)
othercustomer.save()
Das ist schlecht, denn intuitiv hätten wir hier erwartet, dass unser Eindeutigkeitskritierium in der Datenbank das anlegen des zweiten Kunden verhindert.
Ohne ORM gäbe es die Möglichkeit, der Datenbank zu sagen, dass null Werte nicht als unterschiedlich interpretiert werden sollten.
...
CONSTRAINT unique_name_customer_number_zip_code UNIQUE NULLS NOT DISTINCT ("name", "customer_number", "zip_code")
...
Im Django ORM gibt es diese Möglichkeit leider noch nicht, zumindest war sie nicht auffindbar.
Stattdessen lässt sich das Problem mit zwei Randbedingungen lösen. Eine für den Fall, dass keine Kundennummer vorliegt und eine für den Fall, dass eine Kundennummer vorliegt. Unsere Modeldefinition ist also:
class Customer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=200)
customer_number = models.PositiveIntegerField(blank=True, null=True)
zip_code = models.CharField(max_length=20)
class Meta:
constraints = [
UniqueConstraint(
"name",
"zip_code",
name="unique_name_zip_code",
condition=Q(customer_number__isnull=True)
),
UniqueConstraint(
"name",
"customer_number",
"zip_code",
name="unique_name_customer_number_zip_code"
),
]
Mit einem Q-Objekt haben wir hier beschrieben, dass die erste Randbedingung nur gelten soll, wenn keine Kundennummer vorhanden ist. Dann müssen Name und Postleitzahl eindeutig sein. Falls eine Kundennummer vorliegt, gilt nur noch die zweite Randbedingung: Name, Kundennummer und Postleitzahl müssen eindeutig sein.
Screenion GmbH
Büroanschrift:
Adenauerallee 21, 1. OG
61440 Oberursel
Rechnungsanschrift und Firmensitz:
Oberhöchstadter Straße 70a
61440 Oberursel
Deutschland
Fon: +49 (0)6171
9519800
Fax: 06171-9519808
post@screenion.de
Web: https://www.screenion.de
Geschäftsführer: Reto M. Kiefer
Amtsgericht: Bad Homburg HRB 13769
UmSt-Id gemäß §27a Umsatzsteuergesetz: DE273300425
Mit Ihrem Zugriff auf unsere Website werden Daten, die eine Identifizierung ermöglichen könnten (z.B.
IP-Adresse) und weitere Angaben wie Datum, Uhrzeit und aufgerufene Seite In Log-Files gespeichert.
Eine Auswertung der Daten, außer für statistische Zwecke sowie zur Optimierung unseres Internetangebots
in anonymisierter Form, findet nicht statt. Sie können unsere Website grundsätzlich ohne Offenlegung
Ihrer Identität nutzen.
Des Weiteren verwenden wir keine Cookies oder ähnliche Technologien.
Sicher haben Sie schon den Hinweis vermisst :)
Wir verwenden Fotos von unsplash sowie pixabay und danken: