web-dev-qa-db-fra.com

heroku, postgreSQL, Django, comments, tastypie: aucun opérateur ne correspond au nom et au (s) type (s) d'argument donnés. Vous devrez peut-être ajouter des conversions de types explicites

J'ai une simple requête sur le modèle de commentaires intégré de Django et l'obtention de l'erreur ci-dessous avec la base de données postgreSQL de heroku:

DatabaseError: operator does not exist: integer = text LINE 1: 
... INNER JOIN "Django_comments" ON ("pi ns_pin"."id" = "Django_...
                                                         ^
HINT:  No operator matches the given name and argument type(s). 
You might need to add explicit type casts.

Après avoir cherché sur Google, il semble que cette erreur ait déjà été corrigée à plusieurs reprises dans Django, mais je l’ai toujours (toutes les questions connexes ont été résolues il ya 3 à 5 ans). J'utilise Django version 1.4 et la dernière version de tastypie.

La requête est faite sous les filtres orm et fonctionne parfaitement avec ma base de données de développement (sqlite3):

class MyResource(ModelResource):    

    comments = fields.ToManyField('my.api.api.CmntResource', 'comments', full=True, null=True)

    def build_filters(self, filters=None):
        if filters is None:
            filters = {}

        orm_filters = super(MyResource, self).build_filters(filters)

        if 'cmnts' in filters:
            orm_filters['comments__user__id__exact'] = filters['cmnts']

class CmntResource(ModelResource):
    user = fields.ToOneField('my.api.api.UserResource', 'user', full=True)
    site_id = fields.CharField(attribute = 'site_id')
    content_object = GenericForeignKeyField({
        My: MyResource,
    }, 'content_object')
    username = fields.CharField(attribute = 'user__username', null=True)
    user_id = fields.CharField(attribute = 'user__id', null=True)

Quelqu'un a-t-il déjà essayé de contourner cette erreur sans écrire du SQL brut?

19
arctelix

S'appuyant sur la réponse d'IMSoP: il s'agit d'une limitation de la couche ORM de Django lorsqu'une clé étrangère générique utilise un champ de texte pour object_id et que le champ id de l'objet n'est pas un champ de texte. Django ne veut faire aucune supposition ou jette l'identifiant de l'objet comme quelque chose qui ne le serait pas. J'ai trouvé un excellent article à ce sujet http://charlesleifer.com/blog/working-around-Django-s-orm-to-do-interesting-things-with-gfks/ .

L'auteur de l'article, Charles Leifer, a mis au point une solution très intéressante pour les requêtes affectées par cette situation, qui sera très utile pour résoudre ce problème à l'avenir.

Alternativement, j'ai réussi à obtenir ma requête pour fonctionner comme suit:

if 'cmnts' in filters:
    comments = Comment.objects.filter(user__id=filters['cmnts'], content_type__name = 'my',   site_id=settings.SITE_ID ).values_list('object_pk', flat=True)
    comments = [int(c) for c in comments]
    orm_filters['pk__in'] = comments

A l'origine, je cherchais un moyen de modifier le code SQL de la même manière que ce que Charles a fait, mais il s'avère que tout ce que j'avais à faire était de diviser la requête en deux parties et de convertir les str (id) en int (id) ' s.

4
arctelix

PostgreSQL est "fortement typé", c’est-à-dire que chaque valeur de chaque requête a un type particulier, défini explicitement (par exemple, le type d’une colonne dans une table) ou implicitement (par exemple, les valeurs entrées dans une clause WHERE). Toutes les fonctions et tous les opérateurs, y compris =, doivent être définis comme acceptant des types spécifiques. Ainsi, il existe par exemple un opérateur pour VarChar = VarChar et un opérateur différent pour int = int.

Dans votre cas, vous avez une colonne qui est explicitement définie comme type int, mais vous la comparez à une valeur que PostgreSQL a interprétée comme type text.

SQLite, en revanche, est "faiblement typé" - les valeurs sont librement considérées comme du type qui convient le mieux à l'action exécutée. Ainsi, dans votre base de données dev SQLite, l'opération '42' = 42 peut être calculée parfaitement, dans laquelle PostgreSQL aurait besoin d'une définition spécifique de VarChar = int (ou text = int, text étant le type des chaînes non bornées dans PostgreSQL).

Maintenant, PostgreSQL va parfois être utile et "transtyper" automatiquement vos valeurs pour faire correspondre les types à un opérateur connu, mais le plus souvent, comme le dit la suggestion, vous devez le faire explicitement. Si vous écriviez vous-même le code SQL, un cas de type explicite pourrait ressembler à WHERE id = CAST('42' AS INT) (ou WHERE CAST(id AS text) = '42').

Comme vous ne l'êtes pas, vous devez vous assurer que l'entrée que vous entrez dans le générateur de requête est un entier, et non une chaîne composée de chiffres. Je suppose que cela est aussi simple que d’utiliser fields.IntegerField plutôt que fields.CharField, mais je ne connais pas vraiment Django, ni même Python, et j’ai donc pensé vous expliquer le contexte dans l’espoir que vous pourrez le reprendre.

32
IMSoP

Pour ne pas vous pirater, ORM et les logiciels externes postgres vous permettent d’enregistrer vos propres castes et de comparer les opérations. S'il vous plaît regarder exemple dans une question similaire .

0
Hubbitus