MYSQL Fehler bei Model Erweiterung in Django

Django, Flask, Bottle, WSGI, CGI…
Antworten
gomez72
User
Beiträge: 71
Registriert: Sonntag 28. März 2021, 09:57

Hallo,

ich habe eine Django App entwickelt. Meine Datenbank besitzt eine Tabelle bzw. ein Model, dass sich "Plant" nennt. Es geht also um Pflanzen.
Jetzt wollte ich noch eine ManyToMany Beziehung hinzufügen, um den gespeicherten Pflanzen auch gute Pflanzennachbarn zu zuordnen. Also die Beziehung soll so funktionieren: Alle Pflanzen der Tabelle haben keine oder mehrere Beziehungen zu Pflanzen aus der gleichen Tabelle.
Ich habe also ein ManyToManyField hinzugefügt und dann makemigrations und migrate ausgeführt.
Es kam dann zu folgendem Fehler:

Code: Alles auswählen

(venv) D:\dev\projects\web_saat_kalender>py manage.py makemigrations
Migrations for 'plant':
  plant\migrations\0002_plant_good_neighbours.py
    - Add field good_neighbours to plant

(venv) D:\dev\projects\web_saat_kalender>py manage.py migrate        
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, plant, seed, sessions
Running migrations:
  Applying plant.0002_plant_good_neighbours...Traceback (most recent call last):
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\mysql\base.py", line 75, in execute
    return self.cursor.execute(query, args)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\MySQLdb\cursors.py", line 206, in execute
    res = self._query(query)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\MySQLdb\cursors.py", line 319, in _query
    db.query(q)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\MySQLdb\connections.py", line 254, in query
    _mysql.connection.query(self, query)
MySQLdb._exceptions.OperationalError: (3780, "Referencing column 'from_plant_id' and referenced column 'id' in foreign key constraint 'plant_good_neighbours_from_plant_id_40ca9d12_fk_plant_id' are incompatible.")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\dev\projects\web_saat_kalender\manage.py", line 22, in <module>
    main()
  File "D:\dev\projects\web_saat_kalender\manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\core\management\__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\core\management\__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\core\management\base.py", line 414, in run_from_argv
    self.execute(*args, **cmd_options)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\core\management\base.py", line 460, in execute
    output = self.handle(*args, **options)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\core\management\base.py", line 98, in wrapped
    res = handle_func(*args, **kwargs)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\core\management\commands\migrate.py", line 290, in handle
    post_migrate_state = executor.migrate(
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\migrations\executor.py", line 131, in migrate
    state = self._migrate_all_forwards(
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\migrations\executor.py", line 163, in _migrate_all_forwards
    state = self.apply_migration(
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\migrations\executor.py", line 245, in apply_migration
    with self.connection.schema_editor(
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\base\schema.py", line 157, in __exit__
    self.execute(sql)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\base\schema.py", line 192, in execute
    cursor.execute(sql, params)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\utils.py", line 103, in execute
    return super().execute(sql, params)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\utils.py", line 67, in execute
    return self._execute_with_wrappers(
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    with self.db.wrap_database_errors:
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\django\db\backends\mysql\base.py", line 75, in execute
    return self.cursor.execute(query, args)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\MySQLdb\cursors.py", line 206, in execute
    res = self._query(query)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\MySQLdb\cursors.py", line 319, in _query
    db.query(q)
  File "D:\dev\projects\web_saat_kalender\venv\lib\site-packages\MySQLdb\connections.py", line 254, in query
    _mysql.connection.query(self, query)
django.db.utils.OperationalError: (3780, "Referencing column 'from_plant_id' and referenced column 'id' in foreign key constraint 'plant_good_neighbours_from_plant_id_40ca9d12_fk_plant_id' are incompatible.")


Mein Model sah vor der Änderung so aus:

Code: Alles auswählen

class Plant(models.Model):
    species = models.CharField(max_length=100)
    species_variety = models.CharField(unique=True, max_length=100)
    genus = models.CharField(max_length=100, blank=True, null=True)
    family = models.CharField(max_length=100, blank=True, null=True)
    species_scientific = models.CharField(max_length=100, blank=True, null=True)
    
    general_information = models.CharField(max_length=500, blank=True, null=True)
    tip = models.CharField(max_length=500, blank=True, null=True)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        if self.seeding_planting_begin_day > 27:
            self.seeding_planting_begin_day = \
                plant_methods.calculate_last_day_in_month(self.seeding_planting_begin_month)

        if self.seeding_planting_end_day > 27:
            self.seeding_planting_end_day = \
                plant_methods.calculate_last_day_in_month(self.seeding_planting_end_month)

        if not self.planting_out_begin_day:
            pass
        elif self.planting_out_begin_day > 27:
            self.planting_out_begin_day = \
                plant_methods.calculate_last_day_in_month(self.planting_out_begin_month)

        if not self.planting_out_end_day:
            pass
        elif self.planting_out_end_day > 27:
            self.planting_out_end_day = \
                plant_methods.calculate_last_day_in_month(self.planting_out_end_month)

        self.culture_duration = \
            plant_methods.calculate_culture_duration(
                self.harvest_begin_day, self.harvest_begin_month,
                self.seeding_planting_begin_day, self.seeding_planting_begin_month)

        super().save(*args, **kwargs)

    def __str__(self):
        print_name = str(self.species) + " | " + str(self.species_variety)
        return print_name

    def get_absolute_url(self):
        return reverse('plant:plant_detail', args=[str(self.id)])

    class Meta:
        managed = True
        db_table = 'plant'
Dann habe ich also die guten NAchbarn dem Model hinzugefügt:

Code: Alles auswählen

class Plant(models.Model):
    species = models.CharField(max_length=100)
    species_variety = models.CharField(unique=True, max_length=100)
    genus = models.CharField(max_length=100, blank=True, null=True)
    family = models.CharField(max_length=100, blank=True, null=True)
    species_scientific = models.CharField(max_length=100, blank=True, null=True)
   
  
    good_neighbours = models.ManyToManyField("Plant", blank=True)
  
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        if self.seeding_planting_begin_day > 27:
            self.seeding_planting_begin_day = \
                plant_methods.calculate_last_day_in_month(self.seeding_planting_begin_month)

        if self.seeding_planting_end_day > 27:
            self.seeding_planting_end_day = \
                plant_methods.calculate_last_day_in_month(self.seeding_planting_end_month)

        if not self.planting_out_begin_day:
            pass
        elif self.planting_out_begin_day > 27:
            self.planting_out_begin_day = \
                plant_methods.calculate_last_day_in_month(self.planting_out_begin_month)

        if not self.planting_out_end_day:
            pass
        elif self.planting_out_end_day > 27:
            self.planting_out_end_day = \
                plant_methods.calculate_last_day_in_month(self.planting_out_end_month)

        self.culture_duration = \
            plant_methods.calculate_culture_duration(
                self.harvest_begin_day, self.harvest_begin_month,
                self.seeding_planting_begin_day, self.seeding_planting_begin_month)

        super().save(*args, **kwargs)

    def __str__(self):
        print_name = str(self.species) + " | " + str(self.species_variety)
        return print_name

    def get_absolute_url(self):
        return reverse('plant:plant_detail', args=[str(self.id)])

    class Meta:
        managed = True
        db_table = 'plant'

Komischerweise wurde die Tabelle

Code: Alles auswählen

plant_good_neighbours
angelegt und scheint auch zu funktionieren:
Ich weiss leider nicht, ob ich jetzt weitermachen kann, den Fehler habe ich bisher noch nicht verstanden und die Ursache eliminieren können.
https://prnt.sc/Bee_I7NRX7hL
Bild


vielen Dank und viele Grüße
gomez
Benutzeravatar
sparrow
User
Beiträge: 4165
Registriert: Freitag 17. April 2009, 10:28

Versuch mal

Code: Alles auswählen

good_neighbours = models.ManyToManyField("self", blank=True)
gomez72
User
Beiträge: 71
Registriert: Sonntag 28. März 2021, 09:57

sparrow hat geschrieben: Sonntag 8. Mai 2022, 04:43 Versuch mal

Code: Alles auswählen

good_neighbours = models.ManyToManyField("self", blank=True)
Danke hatte ich auch schonmal so. Kam die gleiche Fehlermeldung.
VG
gomez72
User
Beiträge: 71
Registriert: Sonntag 28. März 2021, 09:57

Hat jemand noch eine andere Idee ?
gomez72
User
Beiträge: 71
Registriert: Sonntag 28. März 2021, 09:57

Ich glaube ich habe die Lösung jetzt selbst gefunden. Ich schreibe meinen Lösungsweg hier rein für den Fall, dass jemand mal die gleiche Fragestellung hat.

Also die Fehlermeldung beschreibt inkompatibilitäten. Deswegen habe ich mir mal die von Django neu erstellte ManytoMany-Tabelle angesehen. Dort gibt es 3 Spalten. Eine ID Spalte und in den Spalten "from_plant_id" und "to_plant_id" wird die Beziehung der Pflanzen zueinander gespeichert. Genauer gesagt werden die IDs der jeweiligen Pflanzen darin gespeichert. Und das in einem "bigint" Feld Typen. Die Pflanze selbst um deren Id es hier geht, hat aber scheinbar selber in einem früheren Prozess ein "unsignend int" von mir definiert bekommen. Da diese Feldtypen (also die Plant id und die zwei Spalten in der MAnyToMany Tabelle) nun in direkter Verbindung stehen, aber unterschiedliche Feld Typen besitzen, dachte ich mir, ich setze den Prozess nochmal zurück und ändere ersteinmal die Plant id Spalte auch in ein "bigint". Siehe da, der folgende makemigrations und migrate Prozess spuckte nun keinen MYSQL Fehler mehr raus. Ich hoffe, dass ich dem Fehler richtig auf die Schliche gekommen bin und dass auch die richtige Lösung für dieses Prolem ist. Mir hat es jedenfalls geholfen. Für Meinungen wäre ich sehr gespannt.
LG gomez
Benutzeravatar
sparrow
User
Beiträge: 4165
Registriert: Freitag 17. April 2009, 10:28

Das wird der Fehler gewesen sein. Aber dann hast du deine Tabellen nicht über Django-Migrationen angelegt.
Antworten