2
votes

django.db.utils.DatabaseError: ORA-00904: identifiant invalide

J'exécute une application sur django 1.11 et en tant que moteur de base de données Oracle Database 11G XE. J'ai 3 tables producto, catalogo et catalogo_producto. La table catalogo_producto est une table intermédiaire entre producto et catalogo. Lors de la migration des tables, aucun problème ne se pose, mais lorsque vous souhaitez créer un objet, l'erreur suivante se produit django.db.utils.DatabaseError: ORA-00904: "CATALOGO_PRODUCTO". "ID": identifiant invalide.

Je sais que cette erreur fait référence au fait que je n'ai attribué aucune clé primaire dans le modèle, mais en suivant la documentation de django Champs supplémentaires sur les relations plusieurs-à-plusieurs pour ces cas, une clé primaire n'est pas affectée un domaine spécifique. Je comprends également que django dans ces cas crée un "ID" PK qui n'apparaît pas dans les modèles. Donc je ne sais pas si la bonne solution est simplement de créer un champ id dans la table catalogo_producto ou existe-t-il une autre solution?

models.py

CREATE TABLE producto (
codigo     NUMBER(6) NOT NULL,
precio     NUMBER(8) NOT NULL,
nombre     VARCHAR2(100) NOT NULL,
tipo       VARCHAR2(25) NOT NULL,
cantidad   NUMBER(6) NOT NULL
);

ALTER TABLE producto ADD CONSTRAINT producto_pk PRIMARY KEY ( codigo );

CREATE TABLE catalogo (
codigo        NUMBER(6) NOT NULL,
descripcion   VARCHAR2(250) NOT NULL,
fecha         DATE NOT NULL);

ALTER TABLE catalogo ADD CONSTRAINT catalogo_pk PRIMARY KEY ( codigo );

CREATE TABLE catalogo_producto (
catalogo_id       NUMBER(6) NOT NULL,
producto_id       NUMBER(6) NOT NULL,
posicion          NUMBER(4) NOT NULL,
precio            NUMBER(8) NOT NULL,
imagen_producto   VARCHAR2(250) NOT NULL
);

ALTER TABLE catalogo_producto ADD CONSTRAINT catálogo_producto_pk PRIMARY KEY(catalogo_id, producto_id);

ALTER TABLE catalogo_producto
ADD CONSTRAINT cat_prod_catalogo_fk FOREIGN KEY ( catalogo_id )
    REFERENCES catalogo ( codigo );

ALTER TABLE catalogo_producto
ADD CONSTRAINT cat_prod_producto_fk FOREIGN KEY ( producto_id )
REFERENCES producto ( codigo );

DDL Oracle

class Producto(models.Model):
    codigo = models.IntegerField(primary_key=True)
    precio = models.IntegerField()
    nombre = models.CharField(max_length=100)
    tipo = models.CharField(max_length=25)
    cantidad = models.IntegerField()

    class Meta:
        db_table = 'producto'

    def __str__(self):
        return str(self.codigo)

class Catalogo(models.Model):
    codigo = models.AutoField(primary_key=True)
    descripcion = models.CharField(max_length=250)
    fecha = models.DateField()
    productos = models.ManyToManyField(Producto, through='CatalogoProducto')

    class Meta:
        db_table = 'catalogo'

    def __str__(self):
        return str(self.codigo)

class CatalogoProducto(models.Model):
    catalogo_codigo = models.ForeignKey(Catalogo, models.DO_NOTHING, db_column='catalogo_codigo')
    producto_codigo = models.ForeignKey(Producto, models.DO_NOTHING, db_column='producto_codigo')
    posicion = models.IntegerField()
    precio = models.IntegerField()
    imagen_producto = models.ImageField(upload_to="images/", default='')

    class Meta:
        db_table = 'catalogo_producto'
        unique_together = (('catalogo_codigo', 'producto_codigo'),)


4 commentaires

Comment essayez-vous de créer l'enregistrement dans CatalogoProducto? Lorsque vous utilisez un modèle traversant, vous devez créer manuellement l'objet ...


Je crée d'abord un produit puis un catalogue et lorsque j'essaye de créer l'enregistrement dans CatalogoProducto, l'erreur se produit. Tout ce que je fais par l'administrateur.


Pourriez-vous fournir la description de la table de la table catalogo_producto dans Oracle (liste de toutes les colonnes et contraintes). La colonne de clé primaire doit être créée automatiquement par Django avec le nom "ID". Vous êtes autorisé à passer outre, mais ce n'est pas nécessaire. Cette table existait-elle avant? Avez-vous exécuté makemigrations / migrate sans erreurs?


@Risadinha Lorsque je fais les migrations, aucune erreur ne se produit. Seulement lorsque j'essaye d'ajouter un enregistrement dans CatalogoProducto de l'administrateur. J'ai déjà partagé l'oracle DDL


3 Réponses :


0
votes

J'ai trouvé ce problème sur le projet django, qui pourrait être la réponse:

Il n'y a pas de bug ici. Par défaut, Django veut que les noms de colonne de la table de jointure entre les modèles Foo et Bar soient foo_id et bar_id. Les colonnes de votre table intermédiaire ne sont pas appelées ainsi, l'erreur est donc déclenchée lors de la tentative d'exécution du SQL sur la table intermédiaire (jointure). Pour résoudre ce problème, construisez la table intermédiaire manuellement, en spécifiant les noms des colonnes de base de données et utilisez le nouveau paramètre through sur ManyToManyField pour spécifier le modèle intermédiaire.

Donc, je pense que vous devez changer les noms de colonnes dans le modèle intermédiaire:

https://code.djangoproject.com/ticket/8339


1 commentaires

Le problème est lorsque je crée d'abord la base de données depuis oracle via un script, puis que j'exécute les migrations depuis django. Ce que j'ai fait pour résoudre était d'obtenir les modèles avec python manage.py inspectdb> models.py, puis de faire les migrations avec cela fonctionnant déjà pour moi. Je suppose qu'une erreur s'est produite lors de la définition de certaines tables par le script.



0
votes

Vous créez les clés primaires et les clés étrangères manuellement, semble-t-il.

./manage.py makemigrations # creates the files that create the DB structure
./manage.py migrate. # actually accesses the DB and creates the tables

Vous pointez les clés étrangères vers des clés primaires non existantes. Est-ce que cela est même créé par Oracle? (Ou peut-être que j'ai mal interprété ceci.)

Peut-être que vous créez ces derniers dans vos propres scripts SQL ou qu'ils ont déjà été créés.

Par convention, dans Django, vous n'écrivez pas le Scripts SQL pour créer les tables vous-même mais vous écrivez les modèles puis exécutez:

ALTER TABLE producto ADD CONSTRAINT producto_pk PRIMARY KEY ( codigo );
ALTER TABLE catalogo ADD CONSTRAINT catalogo_pk PRIMARY KEY ( codigo );
ALTER TABLE catalogo_producto
ADD CONSTRAINT cat_prod_catalogo_fk FOREIGN KEY ( catalogo_id )
    REFERENCES catalogo ( codigo );

ALTER TABLE catalogo_producto
ADD CONSTRAINT cat_prod_producto_fk FOREIGN KEY ( producto_id )
REFERENCES producto ( codigo );

Django créera les colonnes de clé primaire, en les nommant ID . Vous avez besoin de ces colonnes pour les jointures en général (vous ne pourrez pas joindre dans Oracle avec vos contraintes actuelles, cela devrait vous donner une erreur très similaire à ce que vous obtenez de Django).

Django le ferait également définissez les contraintes ForeignKey via makemigrations et migrate , en les nommant de manière appropriée en utilisant les noms de champs dans vos modèles.

Si vous êtes autorisé à créer votre propres tables, supprimez tous les paramètres db_ * de votre code et exécutez makemigrations car vous n'aurez pas à vous soucier de vérifier les noms de table en double. Laissez Django faire le travail partout où vous le pouvez.

Les migrations Django vous aideront à garder une trace des changements que vous apportez à vos modèles au fil du temps et qui doivent être reflétés dans votre structure de base de données. L'une des raisons d'utiliser Django est précisément parce que vous ne voulez pas maintenir cela manuellement. Voir https://docs.djangoproject.com/en/2.1/topics/migrations/ .


5 commentaires

Auparavant, j'avais ces colonnes avec le nom de catalogo et producto mais je les ai modifiées en suivant la réponse de ce ticket code. djangoproject.com/ticket/8339 Aussi pour ce cas, je génère les modèles avec python manage.py inspectdb> models.py


Avez-vous vraiment besoin de nommer vous-même les tables et les colonnes DB? Ne pouvez-vous pas simplement utiliser les conventions Django? Vous aurez beaucoup de travail supplémentaire si votre application se développe. (Une fois que vous avez plus de 50 modèles avec des centaines de champs et de relations croisées, vous ne voulez plus vraiment micro-gérer cela par vous-même, si personne ne vous y pousse.)


Le problème est lorsque je crée d'abord la base de données depuis oracle via un script, puis que j'exécute les migrations depuis django. Ce que j'ai fait pour résoudre était d'obtenir les modèles avec python manage.py inspectdb> models.py, puis de faire les migrations avec cela fonctionnant déjà pour moi. Je suppose qu'une erreur s'est produite lors de la définition de certaines tables par le script. Merci de votre aide.


@alejandrodxf - si vous êtes celui qui définit les tables (et que vous n'êtes pas obligé d'utiliser les tables existantes qui sont partagées avec d'autres applications non Django), alors: dropdb , createdb , supprimez tous les migration des fichiers dans vos applications, effectuez makemigrations , puis migrez . NE créez PAS de tableaux vous-même.


Je dois être d'accord avec @Risadinha ... Si vous prévoyez d'utiliser django depuis longtemps, la meilleure idée est de s'appuyer sur la structure de la base de données pour les migrations.



0
votes

Il semble que vous créez du DDL à la main ou autre chose, car si vous fouillez dans les fichiers de migration, vous verrez que django veut créer le champ id :

python manage.py makemigrations
python manage.py migrate   #<-------- this one creates the tables.

Vérifiez deux fois que vous créez des tables à partir de migrations:

    migrations.CreateModel(
        name='CatalogoProducto',
        fields=[
            ('id', models.AutoField(auto_created=True,                 #<---- here!!!
                                    primary_key=True, serialize=False, 
                                    verbose_name='ID')),
            ('posicion', models.IntegerField()),
            ('precio', models.IntegerField()),
            ('catalogo_codigo', models.ForeignKey(db_c...


2 commentaires

Oui, le problème était qu'il créait d'abord les tables à partir d'un script dans oracle, puis exécutait les migrations.


Pues la gracia de las migraciones está en no tener que escribir el DDL, ya sabes, DRY (ne vous répétez pas)