Datenbank aufräumen und eine kleine Änderung

Django, Flask, Bottle, WSGI, CGI…
Antworten
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Meine Tabelle "kategorien" umfasst zurzeit die ersten 14 Aufgabentypen, es werden irgendwann hoffentlich 35 werden. Die hätte ich gerne "durchnummeriert". Jetzt hatte ich aus Versehen den letzten Eintrag gelöscht und neu angelegt. Dabei ist der PK mit der Nummer 14 "verlorengegangen" ich musste also eine neues Feld "zeile" (es heißt aus historischen Gründen so) anlegen um auf die einzelnen Einträge mit fortlaufenden Nummern zugreifen zu können. Amliebsten wäre es mir, wenn ich aber darauf verzichten könnte und dem letzten Eintrag mit der PK 15, wieder die 14 zuweisen könnte. Ich weiß, dass das nicht so einfach geht, da die Links zu den Forign Keys dann nicht mehr stimmen. Diese sind aber übersichtlich und es wäre zur zeit auch vollkommen egal, wenn diese auf einen falschen Eintrag verweisen würden. Ich habe das auch mit DB Viewer mal angeschaut und könnte dort den Key auch ändern - aber, wie erwartet, nicht speichern. Habt ihr eine Idee? Datenbank kopieren und neu aufsetzen? Die bisherigen nötigen Einträge sind ebenfalls überschaubar. Ich nehme an, ich könnte alle Tabellen löschen und neu anlegen, aber erstens ist das etwas Arbeit und zweitens gibt es vielleicht ja auch eine einfachere Lösung?
Und nochwas: Mein Grundgerüst ist ja noch von @whitie. Im DB Viewer sehe ich, dass da noch Einträge (Tabellen) angezeigt werden, die ich dachte gelöscht zu haben, die werden also eindeutig nicht mehr gebraucht. Auf diese kann ich auch als Admin nicht zugreifen. Wie werde ich die los?
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Im DB Viewer sehe ich, dass da noch Einträge (Tabellen) angezeigt werden, die ich dachte gelöscht zu haben, die werden also eindeutig nicht mehr gebraucht.
Du verwendest das Migrationstool, was Django an Bord hat, oder? Dann wird AFAIK der Status vor jeder Migration gespeichert - das ist ja auch ein Grund, mit Migrationen zu arbeiten. Du könntest ja auch zu einer älteren Version zurückgekehren wollen, siehe https://docs.djangoproject.com/en/4.1/t ... migrations
Meine Tabelle "kategorien" umfasst zurzeit die ersten 14 Aufgabentypen, es werden irgendwann hoffentlich 35 werden. Die hätte ich gerne "durchnummeriert".
Wäre es hier nicht ggf. sinnvoll(er), den Aufgabentype als Primary Key zu definieren, statt eine nummerischen zu nehmen?

Gruß, noisefloor
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

noisefloor hat geschrieben: Dienstag 17. Januar 2023, 17:29 Du verwendest das Migrationstool, was Django an Bord hat ...
ja, tue ich. Lasse ich die also drin, oder erledigt sich das Problem, wenn ich (hoffentlich irgendwann mal dieses Projekt veröffentlich?
noisefloor hat geschrieben: Dienstag 17. Januar 2023, 17:29 Wäre es hier nicht ggf. sinnvoll(er), den Aufgabentype als Primary Key zu definieren, statt eine nummerischen zu nehmen?
nun ja, ich hatte mich daran gewöhnt, dass ich auch "nummerisch" auf meine Kategorien zugreifen kann. Das brauche ich auch zurzeit, weil ich mithilfe dieser Zahl per Dict auf meine entsprechenden Funktionen zugreife (ist das klar, wie ich das meine?).
nezzcarth
User
Beiträge: 1633
Registriert: Samstag 16. April 2011, 12:47

Die Datenbankstruktur ist durch die Migrationen abgebildet. Nutzdaten, die regulär zum Betrieb der Software benötigt werden können, in Data Migrations oder Fixtures abgelegt und von dort auch wieder eingelesen werden. Sind diese beiden Voraussetzungen erfüllt, kannst du die Datenbank (während der Entwicklung) zum Beispiel aufräumen, indem du sie löschst, neu anlegst und dann aus den Migrationen/Fixtures neu aufbaust. Data Migrations und Fixtures ersetzen jedoch keinen Export/Importmechanismus für Daten, die im laufenden Betrieb anfallen. Falls es also darüber hinausgehende Nutzdaten gibt, die erhalten werden müssen, muss man etwas anders vorgehen; das sollte während der Entwicklung aber ja eigentlich nicht der Fall sein.
Zuletzt geändert von nezzcarth am Dienstag 17. Januar 2023, 18:40, insgesamt 1-mal geändert.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Lasse ich die also drin, oder erledigt sich das Problem, wenn ich (hoffentlich irgendwann mal dieses Projekt veröffentlich?
Also ich mache es immer so, dass ich die Datenbank neu anlegen, wenn ich ein Projekt veröffentliche. Dann muss man ja nicht die ganzen Migrationen mitschleppen, die man beim Entwickeln so generiert hat.
nun ja, ich hatte mich daran gewöhnt, dass ich auch "nummerisch" auf meine Kategorien zugreifen kann. Das brauche ich auch zurzeit, weil ich mithilfe dieser Zahl
Es gibt da keine reines "richtig" und "falsch", aber im gegebenen Fall halte ich es - basierend auf dem Ausschnitt, den ich über dein Projekt weißt - für ungünstig, sich auf einen nummerischen Primärschlüssel zu verlassen, den die DB automatisch vergibt. Wenn du DB neu baust, z.B. wenn das Projekt produktiv geht, dann müsstest du dich 100% drauf verlassen, dass die DB die gleichen Primärschlüssel vergibt, die du auch in der Testversion hast. Kannst du aber nicht, weil es dafür keine Garantie gibt (auch, wenn es bei deiner vergleichsweise kleinen DB zu wahrscheinlich 99,9% funktionieren wird. Wenn du eine garantiert unveränderliche Referenz benötigst, dann wäre ein händischer Primärschüssel oder eine Referenz auf ein unique Feld sicherer.

Gruß, noisefloor
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

noisefloor hat geschrieben: Dienstag 17. Januar 2023, 18:34 Also ich mache es immer so, dass ich die Datenbank neu anlegen, wenn ich ein Projekt veröffentliche. Dann muss man ja nicht die ganzen Migrationen mitschleppen, die man beim Entwickeln so generiert hat.
Heißt das, dass ich die ganzen Einträge in meinen Tabellen neu eintragen muss? Ich könnte nahezu alle Tabellen (models) gefahrlos löschen und neu anlegen, aber bei zweien müssten die Einträg erhalten bleiben - die muss ich dann doch nicht händisch neu eintippen?
noisefloor hat geschrieben: Dienstag 17. Januar 2023, 18:34 Wenn du eine garantiert unveränderliche Referenz benötigst, dann wäre ein händischer Primärschüssel oder eine Referenz auf ein unique Feld sicherer.
Das heißt, ich weise einfach der "zeile" meinen PK zu?
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
aber bei zweien müssten die Einträg erhalten bleiben - die muss ich dann doch nicht händisch neu eintippen?
Musst du nicht. Du kannst dir doch ein Skript schreiben, dass die relevanten Daten aus der alten DB in die neue einfügt. Oder du nutzt die zuvor genannten Fixtures. Was auch immer einfacher / praktikabler für dich ist.

Gruß, noisefloor
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Dann müsste ich das mit den Fixtures nur noch verstehen. Also ich lösche alle Einträge, die ich nicht mehr brauche (für jede einzelne Aufgabe wird ja ein Eintrag erstellt - die PK ist zwischenzeitlich auf über 8000 angestiegen) oder lösche gleich die ganze Tabelle (model)?
... dann führe ich "datadump" aus und dann? ... und die "Karteileichen" vom Anfang sind dann weg?
Ich brauche ja eigentlich nur die (zurzeit) 14 Einträge in Kategorien und etwa 20 Einträge mit Hilfetexten in "Hilfe". Aber das hatten wir ja schon geklärt, dass ich anstelle von Änderungen an der automatischen PK diese auf "zeile" ändere.
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

noisefloor hat geschrieben: Dienstag 17. Januar 2023, 17:29 Wäre es hier nicht ggf. sinnvoll(er), den Aufgabentype als Primary Key zu definieren, statt eine nummerischen zu nehmen?
Uii, ich hoffe, ich habe das nicht verkehrt verstanden. Ich habe jetzt mein Feld "zeile" in "unique" geändert und dieses Feld (Die Kategorien sind hier von 1 bis 14 durchnummeriert) als "primary_key=True" geändert. Bei "makemigration" wird also die id gelöscht. Aber "migrate" meldet jetzt einen Fehler, da die ids in den anderen Tabellen nicht mehr stimmen. Wie komme ich da raus? Rückgängig geht auch nicht, da das Feld id ja weg ist.
Hilfe!
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

kannst du vielleicht mal die zugehörige(n) Klasse(n) aus der models.py zeigen, also vor und nach der Änderung?

Gruß, noisefloor
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Na klar:

Code: Alles auswählen

class Kategorie(models.Model):
    zeile = models.PositiveSmallIntegerField(default=0, unique=True, primary_key=True)       # entspricht der Aufgabengruppe (1 bis 35)
    name = models.CharField(max_length=30)
    farbe= models.CharField(max_length=25, choices=wahl_farbe.choices)

    start_jg = models.PositiveSmallIntegerField(default=5, verbose_name="Start in Jahrgang")
    start_sw = models.PositiveSmallIntegerField(default=1, verbose_name="Start in Schulwoche")

    eof = models.PositiveSmallIntegerField(default=25, verbose_name="Eingaben ohne Fehler")  # Aufgaben die an einem Stück richtig beantwortet werden müssen damit der Fehlerzähler zurückgesetzt wird

    slug=models.SlugField(default="", null=False)

    def save(self, *args, **kwargs):
        self.slug=slugify(self.name)
        super().save(*args, **kwargs)

    def __str__(self):
        return self.slug

    class Meta:
        verbose_name = 'Kategorie'
        verbose_name_plural = 'Kategorien'

class Auswahl(models.Model):
    kategorie = models.ForeignKey(Kategorie, null=True, on_delete=models.CASCADE)
    text = models.CharField(max_length=80, verbose_name="Text")
    bis_stufe = models.IntegerField(default=0, verbose_name="bis einschl. Stufe:")
    bis_jg = models.IntegerField(default=0, verbose_name="bis einschl. Jahrgang:")
    update = models.BooleanField(default = True)

    def __str__(self):
        return self.text 

    class Meta:
        verbose_name = 'Auswahl'
        verbose_name_plural = 'Auswahl'

class Hilfe(models.Model):
    kategorie = models.ForeignKey(Kategorie, on_delete=models.CASCADE, related_name="hilfe")
    lfd_nr = models.SmallIntegerField(default=0)
    text = models.TextField(blank=True)
    def __str__(self):
        return f"({self.kategorie}: {self.lfd_nr}: {self.text})"

    class Meta:
        verbose_name_plural = 'Hilfen'

class Protokoll(models.Model):
    user = models.ForeignKey(Profil, verbose_name='Benutzer', related_name='protokolle', on_delete=models.CASCADE)
    #gewertet werden nur die Aufgaben des jeweiligen Schuljabjahres, im Januar, Juni und August, kann der user aber auch schon festlegen, dass die Aufgaben für das nächste Schulhalbjahr gelten:
    sj = models.SmallIntegerField(default=0)
    hj = models.SmallIntegerField(default=0)

    kategorie = models.ForeignKey(Kategorie, related_name='protokolle', on_delete=models.CASCADE)
    titel = models.CharField(max_length=20, blank=True)
    typ = models.SmallIntegerField(default=0) 
    typ2 = models.SmallIntegerField(default=0) 
     
    aufgnr = models.PositiveSmallIntegerField(default=0) 
    
    #der Aufgabentext:
    text = models.TextField(blank=True)
    pro_text = models.CharField(max_length=100, blank=True)
    parameter = models.JSONField()
    frage = models.CharField(max_length=20, blank=True)
    einheit = models.CharField(max_length=20, blank=True)
    anmerkung = models.CharField(max_length=100, blank=True)
   
    grafik = models.JSONField()
 
    #hier speichere ich die Lösung, wahlweise als zahl, u.U. auch (mehrere) Lösungen als String:
    value = models.DecimalField('Wert', null=True, max_digits=20, decimal_places=7)
    loesung = models.JSONField()                                                    #hier können mehrere Werte eingegeben werden, der erste wird angezeigt wenn "Lösung anzeigen" angeklickt wird. Steht hier auch "indiv" so wird die Eingabe in der jeweiligen Funktion überprüft
    individuell = models.JSONField()                                                #hier können, wenn oben "inivi" eingetragen wird, individeuelle Werte eingegeben werden, wie z.B. Nenner und Zähler

    #hilfe = models.TextField(blank=True)
    hilfe = models.SmallIntegerField(default=0)
    
    #die Eingabe des users:
    eingabe = models.CharField(max_length=20, blank=True)

    tries = models.PositiveSmallIntegerField('Versuche', default=0)
    #Eintrag richtig, falsch, Extrapunkte, Lösung anzeigen, Abbruch:
    wertung = models.CharField(max_length=10, blank=True)

    start = models.DateTimeField('Start', auto_now_add=True)
    end = models.DateTimeField('Ende', blank=True, null=True, default=None)
    szeit=models.FloatField(default=0)

    @property
    def dauer(self):
        if not self.end:
            return 0
        return (self.end - self.start).total_seconds()

    class Meta:
        verbose_name = 'Protokoll'
        verbose_name_plural = 'Protokoll'

class Zaehler(models.Model):
    user = models.ForeignKey(Profil, verbose_name='Benutzer', related_name='zaehler', on_delete=models.CASCADE)    
    kategorie = models.ForeignKey(Kategorie, on_delete=models.CASCADE, related_name="zaehler")
    
    optionen_text=models.CharField(max_length=40, blank=True, default="", verbose_name="Optionen")
    
    typ_anf = models.SmallIntegerField(default=0)        
    typ_end = models.SmallIntegerField(default=0)    

    aufgnr = models.PositiveSmallIntegerField(default=0)  

    richtig = models.DecimalField(max_digits=6, decimal_places=1, default=0)
    #extrapunkte = models.DecimalField(max_digits=5, decimal_places=1, default=0)
    richtig_of = models.PositiveSmallIntegerField(default=0)   
    zeit = models.PositiveSmallIntegerField(default=0)
    zeit_summe = models.FloatField(default=0)
    schnellste = models.FloatField(default=0)

    falsch = models.PositiveSmallIntegerField(default=0)    
    loesung = models.PositiveSmallIntegerField(default=0)    
    abbrechen = models.PositiveSmallIntegerField(default=0)    
    hilfe = models.PositiveSmallIntegerField(default=0) 
    
    hinweis = models.CharField(max_length=100, blank=True)

    def __str__(self):
        return f"({self.user}, {self.kategorie}, {self.aufgnr})"
    
    def quote(self):
        gesamt = self.richtig + self.falsch
        return self.falsch / gesamt *100 if gesamt else 0

    @property
    def dauer(self):
        h, min = divmod(self.zeit, 3600)
        min, sec = divmod(min, 60) 
        dauer = f'{int(h)}:{int(min):02d}:{int(sec):02d}'
        return dauer

    @property
    def pro_aufg(self):
        return (f'{self.schnellste/10:.1f}').replace(".",",")

    class Meta:
        verbose_name = 'Zähler'
        verbose_name_plural = 'Zähler'  
Die einzige Änderung war:

Code: Alles auswählen

class Kategorie(models.Model):
    zeile = models.PositiveSmallIntegerField(default=0, unique=True)
zu:

Code: Alles auswählen

class Kategorie(models.Model):
    zeile = models.PositiveSmallIntegerField(default=0, unique=True, primary_key=True)
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Vielleicht interesseirt es ja, was es hier Neues gibt.
Ich habe zuerst die Inhalte von "Protokoll" und "Zaehler" gelöscht. Letztere Tabelle hat den Fehler wohl verursacht, diese Inhalte musste ich mithilfe der shell löschen. Das hat nicht geholfen.
Dann habe ich die migrations rückgängig gemacht - hat auch nicht geholfen.
Ich habe mit dumpdata die Datenbank gesichert, das UTF-8 Problem gelöst (in meinem Alter) und die Daten wieder eingespielt - nix.
Ich habe die komplette Tabelle "Zaehler" gelöscht und alle Links darauf entfernt - auch nix.
In meiner Verzweiflung habe ich zuletzt eine alte Version, die ich sicherheitshalber vorgestern kopiert habe (obwohl mir mein Sohn das verboten hat - "man macht das nicht so"), aufgemacht und den ganzen geänderten Code wieder eingefügt. Jetzt geht es wohl wieder.
Jetzt lasse ich aber besser die Finger von PK-Änderungen!
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

gut, das es wieder klappt. Für so große Projekte sollte man auch ein Versionskontrolle wie z.B. git benutzen.

Ich habe glaube ich noch nie den PK bei einer DB geändert. Was ich auch eigentlich meinte ist, dass du die Relation auf ein `unique` Feld setzt statt auf den PK. Man kann Django ja mitgeben, wohin die Relation geht, siehe https://docs.djangoproject.com/en/4.1/r ... y.to_field.
Dann kann dir egal sein, wenn du Lücken in der PK Sequenz hast, weil das Feld, das referenziert wird, kannst du ja frei vergeben.

Gruß, noisefloor
Pitwheazle
User
Beiträge: 871
Registriert: Sonntag 19. September 2021, 09:40

Git habe ich schon installiert und Github auch. Allerdings kann ich damit möglicherweise nicht richtig umgehen. Zumindest in Github wird meine Datenbankja nicht hochgeladen - und da lag ja das Problem.
Mein Feld "zeile" hatte ich schon auf "unique" gesetzt.
Was mir aber jetzt aufgefallen ist: Nachdem ich nahezu alle Einträge in meinen datenbanken gelöscht habe, ist in dem Verzeichnis, das "dumpdata" anlegt ja immer noch jede Menge drin. Die Daten, die ich brauche ("Kategorien", "Hilfe" und "Auswahl" habe ich getrennt gespeichert. Wie werde ich den jetzt den Rest los? db.sqlite3 löschen und neu anlegen? Und bei der Wiederherstellung müsste ich ja eigentlich den einen PK händisch ändern können, bevor ich ihn wieder einspiele (könnte man natürlich aber auch lassen). Und noch was: Muss/sollte ich, wenn ich die Datenbank neu anlege, die ganzen Einträge in "migrations" löschen?
Benutzeravatar
grubenfox
User
Beiträge: 426
Registriert: Freitag 2. Dezember 2022, 15:49

Pitwheazle hat geschrieben: Sonntag 22. Januar 2023, 16:32 Zumindest in Github wird meine Datenbankja nicht hochgeladen - und da lag ja das Problem.
Also für mich ist das im ersten Ansatz etwas gutes. Mit git die Versionsverwaltung lokal im eigenen Netz bzw. ganz ohne Netz auf dem lokalen Entwicklungsrechner zu lassen ist für mich ein guter Anfang und solange es keine guten Gründe gibt, dass noch irgendwo in der Welt andere Personen darauf Zugriff haben müssen (z.b. weil sie mit daran entwickeln) solange kann es für mich lokal bleiben.

Es wäre ja nicht das erste Mal dass auch Dateien mit irgendwelchen geheimen Zugangsdaten aus Versehen im Netz (z.b. in einer Versionsverwaltung wie git) landen und dort nach einem Hack dann öffentlich verbreitet werden.
Antworten