Django db错误:尝试用jsonfield注释模型时,无法为类型json标识相等运算符

2024-04-20 07:58:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我在django1.5.4和postgresql9.3中工作,对JSONField使用django-jsonfield。在

以下查询引发db错误(无法标识json类型的相等运算符):

ModelWithJsonField.objects.annotate(count=Count('field_to_count_by'))

field_to_count_by不是JSONField,普通int字段。在

有什么想法我可以解决这个问题,仍然使用注释?在

引擎盖后面的注解是什么?在


Tags: todjangojson类型fielddbbyobjects
3条回答

我经历了同样的问题,然后我尝试了joachimjablon的代码,虽然它看起来很好用,但仍然存在问题。我要说到这里,最长的版本is on my blog。在

  • SELECT '{"a":1,"b":2}'::json = '{"b":2,"a":1}'::json返回false,因为它基于字符串表示。在
  • 不允许使用

然后,我在PL/V8中创建了一个json_cmp()函数,该函数可用于增强btree所需的运算符。在

下面是完整的SQL脚本

CREATE OR REPLACE FUNCTION json_cmp(left json, right json)
RETURNS integer AS $$
    function cleverType(obj) {
        var type = typeof obj;

        if (type === 'object') {
            if (obj === null) {
                type = 'null';
            } else if (obj instanceof Array) {
                type = 'array';
            }
        }

        return type;
    }

    function cmp(left, right) {
        var leftType = cleverType(left),
            rightType = cleverType(right),
            i,
            buf,
            leftKeys,
            rightKeys,
            output = 0;

        if (leftType !== rightType) {
            output = leftType.localeCompare(rightType);
        } else if (leftType === 'number'
                || leftType === 'boolean'
                || leftType === 'string') {
            if (left < right) {
                output = -1;
            } else if (left > right) {
                output = 1;
            } else {
                output = 0;
            }
        } else if (leftType === 'array') {
            if (left.length !== right.length) {
                output = cmp(left.length, right.length);
            } else {
                for (i = 0; i < left.length; i += 1) {
                    buf = cmp(left[i], right[i]);

                    if (buf !== 0) {
                        output = buf;
                        break;
                    }
                }
            }
        } else if (leftType === 'object') {
            leftKeys = Object.keys(left);
            rightKeys = Object.keys(right);

            if (leftKeys.length !== rightKeys.length) {
                leftKeys.sort();
                rightKeys.sort();
                buf = cmp(leftKeys, rightKeys);
            } else {
                buf = cmp(leftKeys.length, rightKeys.length);
            }

            if (buf !== 0) {
                output = buf;
            } else {
                for (i = 0; i < leftKeys.length; i += 1) {
                    buf = cmp(left[leftKeys[i]], right[leftKeys[i]]);

                    if (buf !== 0) {
                        output = buf;
                        break;
                    }
                }
            }
        }

        return output;
    }

    return cmp(left, right);
$$ LANGUAGE plv8 IMMUTABLE STRICT;

CREATE OR REPLACE FUNCTION json_eq(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
    SELECT json_cmp($1, $2) = 0;
$$;

CREATE OR REPLACE FUNCTION json_lt(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
    SELECT json_cmp($1, $2) < 0;
$$;

CREATE OR REPLACE FUNCTION json_lte(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
    SELECT json_cmp($1, $2) <= 0;
$$;

CREATE OR REPLACE FUNCTION json_gt(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
    SELECT json_cmp($1, $2) > 0;
$$;

CREATE OR REPLACE FUNCTION json_gte(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
    SELECT json_cmp($1, $2) >= 0;
$$;

CREATE OPERATOR =  (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_eq);
CREATE OPERATOR <  (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_lt);
CREATE OPERATOR <= (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_lte);
CREATE OPERATOR >  (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_gt);
CREATE OPERATOR >= (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_gte);

CREATE OPERATOR CLASS json_ops
   DEFAULT FOR TYPE json USING btree AS
   OPERATOR 1 <,
   OPERATOR 2 <=,
   OPERATOR 3 =,
   OPERATOR 4 >=,
   OPERATOR 5 >,
   FUNCTION 1 json_cmp(json, json);

当然,这比简单的字符串比较慢得多,但是它的优点是可以产生更可靠的结果。在

请注意,如果您使用South进行迁移,则可以创建一个空迁移并从forwards()方法执行SQL。这将在您迁移应用程序时自动安装这些功能。在

我遇到了同样的问题,最后(今天)在psql控制台中以admin的身份运行了一个伪操作符:

  This creates a function named hashjson that transforms the
  json to texts and generates a hash
CREATE OR REPLACE FUNCTION hashjson(            
    json          
) RETURNS INTEGER LANGUAGE SQL STRICT IMMUTABLE AS $$ 
    SELECT hashtext($1::text); 
$$; 

  This creates a function named json_eq that checks equality (as text)
CREATE OR REPLACE FUNCTION json_eq(            
    json, 
    json              
) RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$ 
    SELECT bttextcmp($1::text, $2::text) = 0; 
$$; 

  This creates an operator from the equality function
CREATE OPERATOR = (            
    LEFTARG   = json, 
    RIGHTARG  = json, 
    PROCEDURE = json_eq 
); 

  Finaly, this defines a new default JSON operator family with the
  operators and functions we just defined.
CREATE OPERATOR CLASS json_ops
   DEFAULT FOR TYPE json USING hash AS
   OPERATOR 1  =,
   FUNCTION 1  hashjson(json);

(深受this线程的启发)

我在django jsonfield GitHubissue中也提到了你的问题。在

注意:

  • 我对这将产生的影响知之甚少。也许这不是个好主意。这些实现很幼稚,但它们应该足够了。或许不是。在
  • 特别是,equality操作符检查文本相等,而不是语义json相等。但就djangojsonfield而言,我认为我们几乎不可能真正需要结果是正确的(可能一个selectfalse甚至可以做到这一点)。在

我的解决方案使用PL/Python,它解析并重新转储排序键的json,然后生成结果的fnf1a散列:https://github.com/ifad/chronomodel/blob/master/sql/json_ops.sql。在

我没有使用hashtext(),因为它仅供内部使用:http://www.postgresql.org/message-id/24463.1329854466@sss.pgh.pa.us。在

这不是一颗银弹,只是一个粗糙的黑客。真正的解决办法是等待Postgres的全面支持。在

相关问题 更多 >