ProgrammingError:列“product”的类型为product [],但表达式的类型为text [] enum postgres

时间:2017-03-02 21:11:14

标签: python django database postgresql enums

我想保存枚举数组。

我有以下内容:

CREATE TABLE public.campaign
(
  id integer NOT NULL,
  product product[]
)

产品是enum

在Django中,我这样定义:

PRODUCT = (
    ('car', 'car'),
    ('truck', 'truck')
)
class Campaign(models.Model):
    product = ArrayField(models.CharField(null=True, choices=PRODUCT))

但是,当我写下以下内容时:

campaign = Campaign(id=5, product=["car", "truck"])
campaign.save()

我收到以下错误:

ProgrammingError: column "product" is of type product[] but expression is of type text[]
LINE 1: ..."product" = ARRAY['car...

注意 我看到this回答了,但我不使用sqlalchemy,如果不需要,我宁愿不使用它。

EDITED 我尝试下面的@Roman Konoval建议:

class PRODUCT(Enum):
    CAR = 'car'
    TRUCK = 'truck'

class Campaign(models.Model):
        product = ArrayField(EnumField(PRODUCT, max_length=10))

和:

campaign = Campaign(id=5, product=[CAR, TRUCK])
campaign.save()

然而,我仍然得到同样的错误,

我看到django正在将它翻译成字符串列表。 如果我直接在psql控制台上编写以下内容:

INSERT INTO campaign ("product") VALUES ('{car,truck}'::product[]) 

它运作得很好

4 个答案:

答案 0 :(得分:10)

这里有两个基本问题。

不要使用枚举

如果继续使用枚举,那么Stackoverflow上的下一个问题将是"如何在枚举中添加新条目?"。 Django不支持开箱即用的枚举类型(谢天谢地)。所以你必须使用第三方库。您的里程数将随图书馆的完整程度而变化。

  

枚举值占用磁盘上的四个字节。枚举的长度   value的文本标签受编译的NAMEDATALEN设置的限制   进入PostgreSQL;在标准版本中,这意味着最多63个字节。

如果您认为使用枚举节省磁盘空间,manual的上述引用表明它是一种错觉。

有关枚举的优点和缺点的更多信息,请参阅此Q&A。但通常缺点超过了优点。

不要使用数组

  

提示:数组不是集合;搜索特定的数组元素即可   数据库错误设计的标志。考虑使用单独的表格   每个项目的行都是一个数组元素。这会更容易   搜索,并可能为大量的更好地扩展   元件。

来源:https://www.postgresql.org/docs/9.6/static/arrays.html

如果您要搜索涉及汽车或卡车的广告系列,您将需要做很多艰苦的工作。数据库也是如此。

正确的设计

正确的设计是postgresql数组文档页面中建议的设计。创建一个相关的表。这也是标准的django方式。

class Campaign(models.Model):
    name = models.CharField(max_length=20)


class Product(Models.model):
    name = models.CharField(max_length=20)
    campaign = models.ForeignKey(Campaign)

这使您的代码更简单。不需要任何额外的存储空间。不需要第三方库。最重要的是django related models的广泛api可供您使用。

答案 1 :(得分:2)

var app = angular.module('plunker', []); app.controller('MainCtrl', function($scope) { $scope.data = { amount: ''}; }); app.directive('amountInputCurrency', function () { var isAllowedKey = function (k, v) { return ( k === 8 || k === 9 || k === 46 || (k > 47 && k < 58) || (k > 95 && k < 106) || (k > 36 && k < 41) || (k === 188 && (!v || v.search(/(\.|\,)/)<0)) || ((k === 190 || k === 110) && (!v || v.search(/(\.|\,)/)<0)) ); }; return { restrict: 'E', // require: 'ngModel', templateUrl: 'amountInputCurrency.tmpl.html', scope: { model: '=', }, link: function (scope, elem, attrs) { // scope.model2 = ngModelCtrl; console.log("I am in the directive!"); var myAmountCurrencyType = elem.find('.cb-amount-input-currency'); myAmountCurrencyType.on('keydown', function (e) { //if (!isAllowedKey(e.which, scope.model)) { if (!isAllowedKey(e.which, scope.model)) { e.preventDefault(); } }); scope.onFocus = function() { removeThousandSeparator(); }; scope.onBlur = function() { renderValue(); // ngModelCtrl.$render(); // scope.model = ngModelCtrl.$viewValue; }; // //format text going to user (model to view) // ngModelCtrl.$formatters.push(function(value) { // return parseValue(value); // }); // //format text from the user (view to model) // ngModelCtrl.$parsers.push(function(value) { // var num = Number(value); // if(isNumeric(num)) { // var decimal = 2; // return formatAmount(Number(num).toFixed(decimal), decimal, ',', '.'); // } else { // return value; // } // }); function isNumeric(val) { return Number(parseFloat(val))==val; } function renderValue() { var value = String(scope.model || ''); var decimal = attrs.cbAmountDecimal || 2; if (value != undefined && value !="") { scope.model = formatAmount(value, decimal, ',', '.'); // ngModelCtrl.$render(); } } function formatAmount(amount, c, d, t) { if (amount.indexOf(',') !== -1) { if (amount.indexOf('.') !== -1) { amount = amount.replace(/\./g,''); //remove thousand separator } amount = amount.replace(/\,/g,'.'); } c = isNaN(c = Math.abs(c)) ? 2 : c; d = d === undefined ? "." : d; t = t === undefined ? "," : t; var n = amount, s = n < 0 ? "-" : "", i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); } function removeThousandSeparator() { if(scope.model != undefined && scope.model !="") { scope.model = scope.model.replace(/\./g,''); // ngModelCtrl.$render(); // scope.model = ngModelCtrl.$viewValue; } } function parseValue(viewValue) { var num = 0; if(isNumeric(viewValue)) { num = viewValue; } else { num = viewValue ? viewValue.replace(/,/g,'.') : viewValue; } return num; } } } }); 字段的定义不正确,因为它指定它是product s的数组,但它实际上是枚举数组。 Django does not现在支持枚举类型,因此您可以尝试this extension来正确定义类型:

CharField

答案 2 :(得分:2)

试试这个:

def django2psql(s):
    return '{'+','.join(s) + '}

campaign = Campaign(id=5, product=django2psql(["car", "truck"]))  

答案 3 :(得分:-1)

我认为您可能需要将CharField子类化,以使其报告正确的db_type。可能会出现更多问题,但您可以尝试一下:

class Product(models.CharField):
    def db_type(self, connection):
        return 'product'

PRODUCT = (
    ('car', 'car'),
    ('truck', 'truck')
)

class Campaign(models.Model):
    product = ArrayField(Product(null=True, choices=PRODUCT))