8 个版本
使用旧的 Rust 2015
0.0.8 | 2016年1月27日 |
---|---|
0.0.7 | 2015年12月27日 |
0.0.5 | 2015年11月24日 |
0.0.3 | 2015年10月21日 |
0.0.1 | 2015年7月9日 |
#29 in #database-table
24 每月下载量
260KB
1.5K SLoC
Codegenta
是一个代码生成工具,旨在与 rustorm 一起使用,以便根据数据库表元数据信息积极创建 Rust 结构体。
为什么?
有无数种方式使用各种库在生态系统中表示数据库表。可能最常见的方式(没有太多开销/学习曲线)是使用数据库 GUI 工具,如 PgAdmin。
这样做的方式是,您创建数据库表,然后在您的代码中创建相应的结构体。这是一个迭代过程,其中您将根据您开发应用程序添加/删除/编辑表定义。通常,很容易出现不一致。
示例
术语和表
- Product 表存储了我们示例应用程序中的产品列表。
- Users 是一个用户表(复数是因为用户在大多数数据库中都是一个保留关键字)。
- Category - 产品、项目等的分类。
- Photos - 如果产品/项目列表有图片,会看起来更加吸引人。
- Reviews - 对特定产品的用户评论。
- ProductAvailability - 当特定产品可用时,确定卖家的营业时间。在产品上市前宣布产品。这更适用于服务。
要查看的代码
-
查看 examples/generate_model_code.rs。它生成的源代码与您可能为项目编写的模型代码相当。
-
生成的模型代码位于
./gen
文件夹
CREATE TABLE bazaar.product
(
product_id uuid NOT NULL DEFAULT uuid_generate_v4(),
name character varying,
description character varying,
price numeric,
currency_id uuid,
unit character varying,
barcode character varying,
owner_id uuid,
currency_id uuid,
CONSTRAINT product_pkey PRIMARY KEY (product_id),
CONSTRAINT product_currency_id_fkey FOREIGN KEY (currency_id)
REFERENCES payment.currency (currency_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT product_user_id_fkey FOREIGN KEY (owner_id)
REFERENCES bazaar.users (user_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED
)
生成的模型代码
#[derive(RustcDecodable, RustcEncodable)]
#[derive(Debug, Clone)]
pub struct Product {
pub product_id:Uuid,
pub name:Option<String>,
pub description:Option<String>,
pub barcode:Option<String>,
pub currency_id:Option<Uuid>,
pub owner_id:Option<Uuid>,
pub price:Option<f64>,
pub unit:Option<String>,
pub owner: Option<Users>,
pub currency: Option<Currency>,
pub availability: Option<Box<ProductAvailability>>,
pub category: Vec<Category>,
pub photo: Vec<Photo>,
pub review: Vec<Review>,
}
-
pub product_id:Uuid,
标记列 product 为NOT NULL
因此它始终具有值。 -
pub name:Option<String>
是一个可选值,因为我们没有指定它为非空。同样,descrption
等也是如此。 -
pub owner: Option<Users>,
根据 foreign key 约束,codegenta 足够智能,可以识别出产品有一个基于owner_id
的owner,它引用
Users
表。Codegenta 然后添加一个可选字段 owner:Option,您可以在您的应用程序中使用它来存储有关特定产品卖家的额外信息。 -
pub currency: Option<Currency>
,currency_id
指定了产品使用哪种货币。您可以在控制器代码中将使用的货币添加到产品中,以包含更多有关所使用货币的详细信息,而无需创建额外的容器结构体。
更多高级功能
查看项目中提供的示例所使用的表模式。
-
pub category: Vec<Category>,
- codegenta同样能够智能地识别产品表通过product_category连接表与Category
表相关联,这提供了一个产品与类别之间的1:M关系,因为产品可以有多个类别。 -
pubphoto: Vec<Photo>
, -
pubreview: Vec<Review>
,
同样适用于Photo,通过product_photo连接表和带有product_review连接的Review表。
pub availability: Option<Box<ProductAvailability>>
,- 产品可用性是codegenta的另一个独特功能,它确定ProductAvailability表只是产品表的扩展表,具有1:1关系,因为每个产品只能有一个产品可用性。
使用codegenta时进行复杂查询
extern crate rustorm;
extern crate uuid;
extern crate chrono;
extern crate rustc_serialize;
use rustorm::query::Query;
use rustorm::query::{Filter,Equality};
use rustorm::dao::{Dao,IsDao};
use gen::bazaar::Product;
use gen::bazaar::product;
use gen::bazaar::Photo;
use gen::bazaar::photo;
use gen::bazaar::Review;
use gen::bazaar::review;
use gen::bazaar::Category;
use gen::bazaar::category;
use gen::bazaar::product_category;
use gen::bazaar::ProductCategory;
use gen::bazaar::product_photo;
use gen::bazaar::ProductPhoto;
use gen::bazaar::ProductAvailability;
use gen::bazaar::product_availability;
use rustorm::table::IsTable;
use rustorm::pool::ManagedPool;
mod gen;
fn main(){
let mut pool = ManagedPool::init("postgres://postgres:p0stgr3s@localhost/bazaar_v6",1);
let db = pool.connect().unwrap();
let mut query = Query::select_all();
query.from(&Product::table())
.left_join(&ProductCategory::table(),
product_category::product_id, product::product_id)
.left_join(&Category::table(),
category::category_id, product_category::category_id)
.left_join(&ProductPhoto::table(),
product::product_id, product_photo::product_id)
.left_join(&Photo::table(),
product_photo::photo_id, photo::photo_id)
.filter(product::name, Equality::EQ, &"GTX660 Ti videocard")
.filter(category::name, Equality::EQ, &"Electronic")
.group_by(vec![category::name])
.having("count(*)", Equality::GT, &1)
.asc(product::name)
.desc(product::created)
;
let frag = query.build(db.as_ref());
let expected = "
SELECT *
FROM bazaar.product
LEFT OUTER JOIN bazaar.product_category
ON product_category.product_id = product.product_id
LEFT OUTER JOIN bazaar.category
ON category.category_id = product_category.category_id
LEFT OUTER JOIN bazaar.product_photo
ON product.product_id = product_photo.product_id
LEFT OUTER JOIN bazaar.photo
ON product_photo.photo_id = photo.photo_id
WHERE product.name = $1
AND category.name = $2
GROUP BY category.name
HAVING count(*) > $3
ORDER BY product.name ASC, product.created DESC
".to_string();
println!("actual: {{{}}} [{}]", frag.sql, frag.sql.len());
println!("expected: {{{}}} [{}]", expected, expected.len());
assert!(frag.sql.trim() == expected.trim());
}
看看那些漂亮的生成的SQL
路线图
- 支持相反的方向吗?
- 用户编写模型并在数据库中创建相应的表
依赖项
~9.5MB
~185K SLoC