• PostgreSQL 的字段类型和表操作笔记


    PostgreSQL 的 Table 相关笔记

    字段类型

    数值类型

    Name Storage Size Description Range
    smallint 2 bytes small-range integer -32768 to +32767
    integer 4 bytes typical choice for integer -2147483648 to +2147483647
    bigint 8 bytes large-range integer -9223372036854775808 to +9223372036854775807
    decimal variable user-specified precision, exact up to 131072 digits before the decimal point; up to 16383 digits after the decimal point
    numeric variable user-specified precision, exact up to 131072 digits before the decimal point; up to 16383 digits after the decimal point
    real 4 bytes variable-precision, inexact 6 decimal digits precision
    double precision 8 bytes variable-precision, inexact 15 decimal digits precision
    smallserial 2 bytes small autoincrementing integer 1 to 32767
    serial 4 bytes autoincrementing integer 1 to 2147483647
    bigserial 8 bytes large autoincrementing integer 1 to 9223372036854775807

    金额类型

    Name Storage Size Description Range
    money 8 bytes currency amount -92233720368547758.08 to +92233720368547758.07

    numeric, int, 和 bigint 类型可以转为 money. 从 real 和 double precision 则需要先转为 numeric first, 例如

    SELECT '12.34'::float8::numeric::money;
    

    money 可以无损转换为 numeric, 转换为其他类型则会有精度损失, 例如

    SELECT '52093.89'::money::numeric::float8;
    

    字符串类型

    Name Description
    character varying(n), varchar(n) variable-length with limit
    character(n), char(n) fixed-length, blank padded
    text variable unlimited length

    二进制类型

    Name Storage Size Description
    bytea 1 or 4 bytes plus the actual binary string variable-length binary string

    二进制表示, 使用 \x sequence

    SELECT '\xDEADBEEF';
    

    时间类型

    Name Storage Size Description Low Value High Value Resolution
    timestamp [ (p) ] 8 bytes both date and time (no time zone) 4713 BC 294276 AD 1 microsecond
    timestamp [ (p) ] with time zone 8 bytes both date and time, with time zone 4713 BC 294276 AD 1 microsecond
    date 4 bytes date (no time of day) 4713 BC 5874897 AD 1 day
    time [ (p) ] 8 bytes time of day (no date) 00:00:00 24:00:00 1 microsecond
    time [ (p) ] with time zone 12 bytes time of day (no date), with time zone 00:00:00+1559 24:00:00-1559 1 microsecond
    interval [ fields ] [ (p) ] 16 bytes time interval -178000000 years 178000000 years 1 microsecond

    其中, interval类型可以为以下值

    YEAR
    MONTH
    DAY
    HOUR
    MINUTE
    SECOND
    YEAR TO MONTH
    DAY TO HOUR
    DAY TO MINUTE
    DAY TO SECOND
    HOUR TO MINUTE
    HOUR TO SECOND
    MINUTE TO SECOND
    

    布尔类型

    Name Storage Size Description
    boolean 1 byte state of true or false

    枚举类型

    声明

    CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
    

    使用

    CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
    CREATE TABLE person (
        name text,
        current_mood mood
    );
    INSERT INTO person VALUES ('Moe', 'happy');
    SELECT * FROM person WHERE current_mood = 'happy';
     name | current_mood 
    ------+--------------
     Moe  | happy
    (1 row)
    

    排序和比较

    INSERT INTO person VALUES ('Larry', 'sad');
    INSERT INTO person VALUES ('Curly', 'ok');
    SELECT * FROM person WHERE current_mood > 'sad';
     name  | current_mood 
    -------+--------------
     Moe   | happy
     Curly | ok
    (2 rows)
    
    SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood;
     name  | current_mood 
    -------+--------------
     Curly | ok
     Moe   | happy
    (2 rows)
    
    SELECT name
    FROM person
    WHERE current_mood = (SELECT MIN(current_mood) FROM person);
     name  
    -------
     Larry
    (1 row)
    

    地理位置类型

    Name Storage Size Description Representation
    point 16 bytes Point on a plane (x,y)
    line 32 bytes Infinite line {A,B,C}
    lseg 32 bytes Finite line segment ((x1,y1),(x2,y2))
    box 32 bytes Rectangular box ((x1,y1),(x2,y2))
    path 16+16n bytes Closed path (similar to polygon) ((x1,y1),...)
    path 16+16n bytes Open path [(x1,y1),...]
    polygon 40+16n bytes Polygon (similar to closed path) ((x1,y1),...)
    circle 24 bytes Circle <(x,y),r> (center point and radius)

    网络地址类型

    Name Storage Size Description
    cidr 7 or 19 bytes IPv4 and IPv6 networks
    inet 7 or 19 bytes IPv4 and IPv6 hosts and networks
    macaddr 6 bytes MAC addresses
    macaddr8 8 bytes MAC addresses (EUI-64 format)

    inet 和 cidr 的区别
    二者最关键的区别在于, inet 允许IP地址在掩码区域外有非零值, 例如 "192.168.0.1/24", 这个值对于 cidr 是不允许的.

    如果不喜欢 inet 或 cidr 输出的格式, 可以使用 host, text 和 abbrev 这些函数进行处理.

    二进制串类型

    使用0和1表示的字符串, sql示例

    CREATE TABLE test (a BIT(3), b BIT VARYING(5));
    INSERT INTO test VALUES (B'101', B'00');
    INSERT INTO test VALUES (B'10', B'101');
    
    ERROR:  bit string length 2 does not match type bit(3)
    
    INSERT INTO test VALUES (B'10'::bit(3), B'101');
    SELECT * FROM test;
    
      a  |  b
    -----+-----
     101 | 00
     100 | 101
    

    文本搜索类型

    PostgreSQL provides two data types that are designed to support full text search, which is the activity of searching through a collection of natural-language documents to locate those that best match a query. The tsvector type represents a document in a form optimized for text search; the tsquery type similarly represents a text query. Chapter 12 provides a detailed explanation of this facility, and Section 9.13 summarizes the related functions and operators.

    tsvector, tsquery

    UUID类型

    字段长16 byte(128-bit), 用于分布式系统可以提供更好的唯一性保证(相对于自增序列). 一个 UUID 是一组短横线分隔的十六进制小写数字,
    格式为: 一组8位, 三组4位, 最后是一组12位, 一共32位组成128bit. 例如

    a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
    

    XML类型

    XMLPARSE ( { DOCUMENT | CONTENT } value)
    XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter></book>')
    XMLPARSE (CONTENT 'abc<foo>bar</foo><bar>foo</bar>')
    
    XMLSERIALIZE ( { DOCUMENT | CONTENT } value AS type )
    

    JSON类型

    数组类型

    CREATE TABLE sal_emp (
        name            text,
        pay_by_quarter  integer[],
        schedule        text[][]
    );
    
    CREATE TABLE tictactoe (
        squares   integer[3][3]
    );
    

    增和查

    INSERT INTO sal_emp
        VALUES ('Bill',
        '{10000, 10000, 10000, 10000}',
        '{{"meeting", "lunch"}, {"training", "presentation"}}');
    
    INSERT INTO sal_emp
        VALUES ('Carol',
        '{20000, 25000, 25000, 25000}',
        '{{"breakfast", "consulting"}, {"meeting", "lunch"}}');
    The result of the previous two inserts looks like this:
    
    SELECT * FROM sal_emp;
     name  |      pay_by_quarter       |                 schedule
    -------+---------------------------+-------------------------------------------
     Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}}
     Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}}
    (2 rows)
    
    -- 使用 ARRAY
    INSERT INTO sal_emp
        VALUES ('Bill',
        ARRAY[10000, 10000, 10000, 10000],
        ARRAY[['meeting', 'lunch'], ['training', 'presentation']]);
    
    INSERT INTO sal_emp
        VALUES ('Carol',
        ARRAY[20000, 25000, 25000, 25000],
        ARRAY[['breakfast', 'consulting'], ['meeting', 'lunch']]);
    
    
    SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];
    
     name
    -------
     Carol
    (1 row)
    
    
    SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';
    
            schedule
    ------------------------
     {{meeting},{training}}
    (1 row)
    
    
    SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
    
                     schedule
    -------------------------------------------
     {{meeting,lunch},{training,presentation}}
    (1 row)
    

    UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
        WHERE name = 'Carol';
    
    -- 使用 ARRAY
    UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
        WHERE name = 'Carol';
    

    自定义类型, 组合类型

    CREATE TYPE complex AS (
        r       double precision,
        i       double precision
    );
    
    CREATE TYPE inventory_item AS (
        name            text,
        supplier_id     integer,
        price           numeric
    );
    
    CREATE TABLE on_hand (
        item      inventory_item,
        count     integer
    );
    
    INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);
    
    CREATE FUNCTION price_extension(inventory_item, integer) RETURNS numeric
    AS 'SELECT $1.price * $2' LANGUAGE SQL;
    
    SELECT price_extension(item, 10) FROM on_hand;
    
    CREATE TABLE inventory_item (
        name            text,
        supplier_id     integer REFERENCES suppliers,
        price           numeric CHECK (price > 0)
    );
    

    Table相关SQL

    建表 CREATE TABLE

    建表说明: https://www.postgresql.org/docs/14/sql-createtable.html

    完整的建表语法

    CREATE [ { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
      { column_name data_type [ COMPRESSION compression_method ] [ COLLATE collation ] [ column_constraint [ ... ] ]
        | table_constraint
        | LIKE source_table [ like_option ... ] }
        [, ... ]
    ] )
    [ INHERITS ( parent_table [, ... ] ) ]
    [ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
    [ USING method ]
    [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
    [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
    [ TABLESPACE tablespace_name ]
    
    -- column_constraint 字段约束的格式
    [ CONSTRAINT constraint_name ]
    { NOT NULL |
      NULL |
      CHECK ( expression ) [ NO INHERIT ] |
      DEFAULT default_expr |
      GENERATED ALWAYS AS ( generation_expr ) STORED |
      GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
      UNIQUE index_parameters |
      PRIMARY KEY index_parameters |
      REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
        [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
    [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
    
    -- table_constraint 表约束的格式
    [ CONSTRAINT constraint_name ]
    { CHECK ( expression ) [ NO INHERIT ] |
      UNIQUE ( column_name [, ... ] ) index_parameters |
      PRIMARY KEY ( column_name [, ... ] ) index_parameters |
      EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
      FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
        [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
    [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
    

    还有OF type_namePARTITION OF parent_table两种, 比较少用.

    TEMPORARY | TEMP

    临时表, 在session结束后自动drop

    UNLOGGED

    对UNLOGGED表的写入不记入 write-ahead 日志, 所以比普通表快. 如果数据库崩溃(crash)或非常关机, UNLOGGED表会被自动truncate. UNLOGGED表不能replicated, 基于UNLOGGED表的索引也会是UNLOGGED的.

    COMPRESSION

    压缩仅用于变长字段类型, and is used only when the column's storage mode is main or extended

    PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ opclass ] [, ...] )

    用于对表进行分区. The table thus created is called a partitioned table. The parenthesized list of columns or expressions forms the partition key for the table. When using range or hash partitioning, the partition key can include multiple columns or expressions (up to 32, but this limit can be altered when building PostgreSQL), but for list partitioning, the partition key must consist of a single column or expression.

    Range and list partitioning require a btree operator class, while hash partitioning requires a hash operator class. If no operator class is specified explicitly, the default operator class of the appropriate type will be used; if no default operator class exists, an error will be raised. When hash partitioning is used, the operator class used must implement support function 2 (see Section 38.16.3 for details).

    表分区后, 会变成一系列子表, 原表本身变成空表. 向原表的写入, 会路由到对应的子表, 如果对应的分区不存在就会报错. 分区表不支持 EXCLUDE 约束, 但是在子表中可以定义.

    NOT NULL, NULL, DEFAULT, UNIQUE, PRIMARY KEY

    和MySQL用法一样

    GENERATED ALWAYS AS ( generation_expr ) STORED

    类似于view, 这种字段由其他字段(非generated)生成, 不能写只能读

    GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]

    表示此字段为ID字段, 使用一个绑定的sequence自动赋值, 并且这个字段一定是NOT NULL. This clause creates the column as an identity column. It will have an implicit sequence attached to it and the column in new rows will automatically have values from the sequence assigned to it. Such a column is implicitly NOT NULL.

    The clauses ALWAYS and BY DEFAULT determine how explicitly user-specified values are handled in INSERT and UPDATE commands.

    TABLESPACE tablespace_name

    表空间, 未指定则使用 default_tablespace, 如果是临时表, 则使用 temp_tablespaces.

    建表示例

    设置主键

    CREATE TABLE films (
        code        char(5) CONSTRAINT firstkey PRIMARY KEY,
        title       varchar(40) NOT NULL,
        did         integer NOT NULL,
        date_prod   date,
        kind        varchar(10),
        len         interval hour to minute
    );
    
    CREATE TABLE distributors (
         did    integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
         name   varchar(40) NOT NULL CHECK (name <> '')
    );
    
    CREATE TABLE films (
        code        char(5),
        title       varchar(40),
        did         integer,
        date_prod   date,
        kind        varchar(10),
        len         interval hour to minute,
        CONSTRAINT code_title PRIMARY KEY(code,title)
    );
    
    -- 下面两个是等价的
    CREATE TABLE distributors (
        did     integer,
        name    varchar(40),
        PRIMARY KEY(did)
    );
    
    CREATE TABLE distributors (
        did     integer PRIMARY KEY,
        name    varchar(40)
    );
    
    

    二维数组字段

    CREATE TABLE array_int (
        vector  int[][]
    );
    

    唯一约束字段

    CREATE TABLE films (
        code        char(5),
        title       varchar(40),
        did         integer,
        date_prod   date,
        kind        varchar(10),
        len         interval hour to minute,
        CONSTRAINT production UNIQUE(date_prod)
    );
    
    CREATE TABLE distributors (
        did     integer,
        name    varchar(40) UNIQUE
    );
    

    表达式约束字段

    CREATE TABLE distributors (
        did     integer,
        name    varchar(40),
        CONSTRAINT con1 CHECK (did > 100 AND name <> '')
    );
    

    设置字段默认值

    CREATE TABLE distributors (
        name      varchar(40) DEFAULT 'Luso Films',
        did       integer DEFAULT nextval('distributors_serial'),
        modtime   timestamp DEFAULT current_timestamp
    );
    

    非空约束

    CREATE TABLE distributors (
        did     integer CONSTRAINT no_null NOT NULL,
        name    varchar(40) NOT NULL
    );
    

    对表进行分区

    CREATE TABLE measurement (
        logdate         date not null,
        peaktemp        int,
        unitsales       int
    ) PARTITION BY RANGE (logdate);
    
    -- 分区依据多个字段
    CREATE TABLE measurement_year_month (
        logdate         date not null,
        peaktemp        int,
        unitsales       int
    ) PARTITION BY RANGE (EXTRACT(YEAR FROM logdate), EXTRACT(MONTH FROM logdate));
    
    -- 使用list分区
    CREATE TABLE cities (
        city_id      bigserial not null,
        name         text not null,
        population   bigint
    ) PARTITION BY LIST (left(lower(name), 1));
    
    -- 使用hash分区
    CREATE TABLE orders (
        order_id     bigint not null,
        cust_id      bigint not null,
        status       text
    ) PARTITION BY HASH (order_id);
    
    -- 使用区间分区
    CREATE TABLE measurement_y2016m07
        PARTITION OF measurement (
        unitsales DEFAULT 0
    ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
    

    分别创建 表measurement_year_month 的各个分区子表

    CREATE TABLE measurement_ym_older
        PARTITION OF measurement_year_month
        FOR VALUES FROM (MINVALUE, MINVALUE) TO (2016, 11);
    
    CREATE TABLE measurement_ym_y2016m11
        PARTITION OF measurement_year_month
        FOR VALUES FROM (2016, 11) TO (2016, 12);
    
    CREATE TABLE measurement_ym_y2016m12
        PARTITION OF measurement_year_month
        FOR VALUES FROM (2016, 12) TO (2017, 01);
    
    CREATE TABLE measurement_ym_y2017m01
        PARTITION OF measurement_year_month
        FOR VALUES FROM (2017, 01) TO (2017, 02);
    

    或者

    CREATE TABLE orders_p1 PARTITION OF orders
        FOR VALUES WITH (MODULUS 4, REMAINDER 0);
    CREATE TABLE orders_p2 PARTITION OF orders
        FOR VALUES WITH (MODULUS 4, REMAINDER 1);
    CREATE TABLE orders_p3 PARTITION OF orders
        FOR VALUES WITH (MODULUS 4, REMAINDER 2);
    CREATE TABLE orders_p4 PARTITION OF orders
        FOR VALUES WITH (MODULUS 4, REMAINDER 3);
    

    对于以上的分区方式, 可以设置一个默认子分区

    CREATE TABLE cities_partdef
        PARTITION OF cities DEFAULT;
    

    一个完整的建表sql例子

    CREATE TABLE "public"."test_account" (
    	"id" bigserial primary key,
    	"name" varchar(64),
    	"val" int4 NOT NULL DEFAULT 0,
    	"amount" numeric(20,2) NOT NULL CHECK (amount > 0),
    	"flag" bool DEFAULT false,
    	"status" int2 NOT NULL DEFAULT 0,
    	"created_at" timestamp(6) NOT NULL,
    	"updated_at" timestamp(6),
    	"version" int8 NOT NULL DEFAULT 0
    );
    
    CREATE INDEX ON "public"."test_account" ("name");
    CREATE INDEX ON "public"."test_account" ("flag");
    CREATE INDEX ON "public"."test_account" ("created_at");
    
    COMMENT ON COLUMN "public"."test_account"."id" IS 'ID';
    COMMENT ON COLUMN "public"."test_account"."name" IS 'Account Name';
    COMMENT ON COLUMN "public"."test_account"."val" IS 'Value';
    COMMENT ON COLUMN "public"."test_account"."amount" IS 'Amount';
    COMMENT ON COLUMN "public"."test_account"."flag" IS 'Boolean Flag';
    COMMENT ON COLUMN "public"."test_account"."status" IS 'Status';
    COMMENT ON COLUMN "public"."test_account"."created_at" IS 'Create Time';
    COMMENT ON COLUMN "public"."test_account"."updated_at" IS 'Update Time';
    COMMENT ON COLUMN "public"."test_account"."version" IS 'Version';
    

    修改表 ALTER TABLE

    添加字段

    ALTER TABLE distributors ADD COLUMN address varchar(30);
    

    删除字段

    ALTER TABLE distributors DROP COLUMN address RESTRICT;
    

    修改字段类型

    ALTER TABLE distributors
        ALTER COLUMN address TYPE varchar(80),
        ALTER COLUMN name TYPE varchar(100);
    
    ALTER TABLE foo
        ALTER COLUMN foo_timestamp SET DATA TYPE timestamp with time zone
        USING
            timestamp with time zone 'epoch' + foo_timestamp * interval '1 second';
    
    ALTER TABLE foo
        ALTER COLUMN foo_timestamp DROP DEFAULT,
        ALTER COLUMN foo_timestamp TYPE timestamp with time zone
        USING
            timestamp with time zone 'epoch' + foo_timestamp * interval '1 second',
        ALTER COLUMN foo_timestamp SET DEFAULT now();
    

    字段更名

    ALTER TABLE distributors RENAME COLUMN address TO city;
    

    表更名

    ALTER TABLE distributors RENAME TO suppliers;
    

    字段添加非空限制, 删除非空限制

    ALTER TABLE distributors ALTER COLUMN street SET NOT NULL;
    ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;
    

    对表和子表添加和删除check限制

    ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
    ALTER TABLE distributors DROP CONSTRAINT zipchk;
    

    仅删除一个表的check限制

    ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;
    

    自增序列 SEQUENCE

    查看数据库中的sequence

    select * from pg_sequences order by sequencename asc
    

    列出sequence与table的关联

    select sn.nspname as seq_schema,
           s.relname as seqname,
           st.nspname as tableschema,
           t.relname as tablename,
           at.attname as columname
      from pg_class s
      join pg_namespace sn on sn.oid = s.relnamespace
      join pg_depend d on d.refobjid = s.oid 
      join pg_attrdef a on d.objid = a.oid
      join pg_attribute at on at.attrelid = a.adrelid and at.attnum = a.adnum
      join pg_class t on t.oid = a.adrelid
      join pg_namespace st on st.oid = t.relnamespace
     where s.relkind = 'S'
       and d.classid = 'pg_attrdef'::regclass
       and d.refclassid = 'pg_class'::regclass
     order by s.relname asc
    

    如果字段类型为serial, 会隐含创建sequence, 例如下面的语句会产生三个sequence

    create table foo(id serial, v integer);
    create table boo(id_boo serial, v integer);
    create sequence omega;
    create table bubu(id integer default nextval('omega'), v integer);
    
    ┌────────────┬────────────────┬─────────────┬───────────┬───────────┐
    │ seq_schema │    seqname     │ tableschema │ tablename │ columname │
    ╞════════════╪════════════════╪═════════════╪═══════════╪═══════════╡
    │ public     │ foo_id_seq     │ public      │ foo       │ id        │
    │ public     │ boo_id_boo_seq │ public      │ boo       │ id_boo    │
    │ public     │ omega          │ public      │ bubu      │ id        │
    └────────────┴────────────────┴─────────────┴───────────┴───────────┘
    

    如果执行

    create table foo(
        id serial primary key, 
        val integer
    );
    

    实际看到的表结构为

    CREATE TABLE "public"."foo" (
      "id" int4 NOT NULL DEFAULT nextval('foo_id_seq'::regclass),
      "val" int4,
      CONSTRAINT "foo_pkey" PRIMARY KEY ("id")
    )
    ;
    
    ALTER TABLE "public"."foo" 
      OWNER TO "dbuser";
    

    参考

  • 相关阅读:
    MySQL 简单查询(实验回顾)
    PicGo + Gitee 创建图床,Typora竟还有这种功能
    SQL 条件判断
    django ORM中的复选MultiSelectField的使用
    with的用法
    django update-or-create的用法
    获取异常文件
    支付宝支付
    单例模式创建
    数据类模型
  • 原文地址:https://www.cnblogs.com/milton/p/15969920.html
Copyright © 2020-2023  润新知