Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Contrainte unique sur plusieurs colonnes d'une table

0 votes

Je débute avec django, et j'ai vraiment du mal à ne pas gérer moi-même mes requêtes sql et le modèle de mes tables.

J'ai une simple table de relations entre une table articles et une table catégories.

class ArticlesCategories(models.Model):
    cat_id = models.ForeignKey(Categories, on_delete=models.DO_NOTHING)
    art_id = models.ForeignKey(Articles, on_delete=models.DO_NOTHING)
    class Meta:
        unique_together = ('cat_id', 'art_id')

De ce modèle, je m'attendais donc à pouvoir insérer dans cette table des entiers.

artcat = ArticlesCategories()
artcat.art_id = 10
artcat.cat_id = 2

Et à récupérer une erreur de contrainte d'unicité au cas où cette entrée existe déjà dans la table.

Sauf que django râle parce qu'il veut des objets.

Cannot assign "2": "ArticlesCategories.cat_id" must be a "Categories"
instance.

De plus si je visionne le schéma de la table générée, il n'y a aucun unique sur les 2 colonnes, wtf !

On va quand même se prendre la tête à construire des models uniquement pour faire de simples insertions parce qu'en fait le schéma des tables générées est tellement à chier qu'on se demande à quoi ça sert que django s'en mêle.

Y a-t-il une autre façon de faire en utilisant de simples entiers ?
Pourquoi django ne créé pas un unique dans la table sql plutôt que si j'ai bien compris de faire cette vérification en interne ?

demandé 20-Dec-2019 par anonyme

3 Réponses

0 votes

Pour que l'orm fasse son boulot il te demande d'instancié une Categorie, pour qu'il récupère son id et la propage, ce serait la meilleur méthode car tu serais sûr que l'id existe.

tu peux effectivement passé un id sans instancié Categorie, mais l'attribut de champ en base générer par django sur le modèle ArticlesCategories n'est pas catid mais de mémoire catid_id du coup.
vérifie en base le nom dans la table, et la cela passera sans problème.
tu peux même faire un dir(artcat)..

j'espère t'avoir aidé.

répondu 7-Fev par bidochon (210 points)
0 votes

Bonsoir,

Pour moi il y a une erreur de compréhension dans la conception aux vues des noms de variables données dans le modèle ArticlesCategories.

À ce que j'ai cru comprendre, pour créer des catégories d'articles, j'ai besoin de connaître la catégorie et l'article, donc les noms de variables categorie et article me semblent plus adaptées que cat_id et art_id qui eux représentent des identifiants donc des entiers.

Après une autre chose m'embête du point de vue conception, c'est l'écrasement des valeurs des identifiants... En vrai ce genre de chose ne se fait pas. On peut modifier un nom de catégorie, un prix d'un article, mais surtout pas un identifiant qui garde l'unicité de l'objet créé et si c'est un identifiant créé par ses soins, alors il faut bien réfléchir à la manière dont le créer pour éviter les conflits.

Après si on veut créer un objet ArticlesCategories en connaissant l'id d'un article et l'id de sa catégorie, alors pourquoi pas, ça fonctionne bien normalement,

articles_categories = ArticlesCategories.objects.create(
    category=Category.objects.get(id=10),
    article=Article.objects.get(id=2)
)

Je ne sais pas si c'est cela qui te convient

répondu 15-Fev par fred1599 (108 points)
0 votes

(1) La contrainte unique_together est une liste de liste https://docs.djangoproject.com/fr/3.0/ref/models/options/#unique-together
Toi tu as défini une simple liste, donc django interprete ca comme deux contraintes unique séparées (à vrai dire je suis même surpris qu'il ne t'affiche pas d'erreur)

Ton code devrait donc être

class Meta:
    unique_together = [('cat_id', 'art_id')]

(2) Le concept des foreign key, c'est que Django va chercher les objects pour toi quand tu accèdes artid et catid. A l'inverse, il s'attend aussi que tu leur assignes des objects. Si tu inspecte une instance de ArticlesCategories, tu verras que sous le capot, Django garde la clé primaire de l'object cartid dans une valeur catidid. Donc si tu veux assigner directement catid par sa clé primaire, tu peux faire

artice_category = ArticlesCategories()
artice_category.cat_id_id = 2
artice_category.art_id_id = 10
répondu 17-Mar par benjamin (402 points)
...