在Python SQL Alchemy中创建基于字符串的序列

时间:2017-04-03 07:24:57

标签: python postgresql sqlalchemy

我正在尝试在SQLAlchemy ORM中创建基于字符串的序列。我知道最好的做法是使用基于Integer的列来管理表的ID,但在我的情况下,由于项目的一些要求,我需要将数据存储为基于字符串的ID for ID。

我知道在某些数据库中,可以使用基于字符串的序列来存储数据,如:

  • “AAA1”
  • “AAA2”
  • “AAAN”

但是在SQL炼金术中我无法通过Sequence方法实现这一点。它只生成基于整数的序列,我找不到在序列生成中“追加”前缀的选项。

实现这一目标有一个简单的解决方案吗?

3 个答案:

答案 0 :(得分:0)

你可以在PostgreSQL中这样做:

CREATE TABLE test(
   id text PRIMARY KEY,
   val text
);

CREATE SEQUENCE test_id_seq OWNED BY test.id;

ALTER TABLE test
   ALTER id SET DEFAULT to_hex(nextval('test_id_seq'));

在该示例中,我使用to_hex函数来获取十六进制表示,但您可以使用任何您喜欢的表达式。

答案 1 :(得分:0)

由于Sequence对象是基于整数的,因此似乎无法帮助您。需要将类似'AAA' + str(some_numeric_id)之类的内容显式地发布到包含主键的列中。

以下类使用itertools.product生成一系列唯一的字符串ID。

import itertools


class StringCounter:
    def __init__(self, pattern: List):
        self.digit_types = {
            'uppercase': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
            'number': '0123456789',
            'hex': '0123456789ABCDEF',
        }
        format = [self.digit_types[d] for d in pattern]
        self.sequence = (''.join(s) for s in itertools.product(*format))

    def get_n_ids(self, n):
        return [s for _, s in zip(range(n), self.sequence)]

    def next_id(self):
        return self.sequence.__next__()

    def continue_from(self, from_id):
        self.sequence = itertools.dropwhile(lambda s: s != from_id, self.sequence)

示例:

id_seq = StringCounter(['uppercase', 'uppercase', 'uppercase', 'number'])
first_batch = id_seq.get_n_ids(9)
print(f'First batch of ids: {first_batch}')
data = ['data1', 'data2', 'data3']
for d in data:
    unique_id = id_seq.next_id()
    print(f"Posting data '{d}' under unique id {unique_id}")
from_id = 'ACZ8'
id_seq.continue_from(from_id)
print(f'...continuing from {from_id}')
data = ['data4', 'data5', 'data6']
for d in data:
    unique_id = id_seq.next_id()
    print(f"Posting data '{d}' under unique id {unique_id}")

输出:

First batch of ids: ['AAA0', 'AAA1', 'AAA2', 'AAA3', 'AAA4', 'AAA5', 'AAA6', 'AAA7', 'AAA8']
Posting data 'data1' under unique id AAA9
Posting data 'data2' under unique id AAB0
Posting data 'data3' under unique id AAB1
...continuing from ACZ8
Posting data 'data4' under unique id ACZ8
Posting data 'data5' under unique id ACZ9
Posting data 'data6' under unique id ADA0

将两个StringCounter实例一起使用的示例:

product_seq = StringCounter(['uppercase', 'uppercase', 'uppercase'])
sale_seq = StringCounter(['number'] * 6)
sale_seq.continue_from('000998')
for prod in range(3):
    prod_id = product_seq.next_id()
    for line in range(3):
        print(f"Posting sale '{sale_seq.next_id()}' of product id '{prod_id}'")

输出:

Posting sale '000998' of product id 'AAA'
Posting sale '000999' of product id 'AAA'
Posting sale '001000' of product id 'AAA'
Posting sale '001001' of product id 'AAB'
Posting sale '001002' of product id 'AAB'
Posting sale '001003' of product id 'AAB'
Posting sale '001004' of product id 'AAC'
Posting sale '001005' of product id 'AAC'
Posting sale '001006' of product id 'AAC'

答案 2 :(得分:0)

这不是一个很好的答案,但是如果您需要以字符串形式访问PK,则可以使用惯用的整数PK,以确保PK是连续且唯一的,然后使用int()将其转换为字符串/ str()