use crate::forms::ValidationError;
pub trait ModelMeta {
fn table_name() -> &'static str;
fn verbose_name() -> &'static str {
Self::table_name()
}
fn verbose_name_plural() -> &'static str {
""
}
fn ordering() -> &'static [&'static str] {
&[]
}
fn get_latest_by() -> Option<&'static str> {
None
}
fn unique_together() -> &'static [&'static [&'static str]] {
&[]
}
}
pub trait ModelValidation {
fn validate(&self) -> Result<(), ValidationError> {
Ok(())
}
fn full_clean(&self) -> Result<(), ValidationError> {
self.validate()
}
}
#[cfg(test)]
mod tests {
use super::{ModelMeta, ModelValidation};
use crate::forms::ValidationError;
struct DefaultMetaModel;
impl ModelMeta for DefaultMetaModel {
fn table_name() -> &'static str {
"widgets"
}
}
struct CustomMetaModel;
impl ModelMeta for CustomMetaModel {
fn table_name() -> &'static str {
"inventory_item"
}
fn verbose_name() -> &'static str {
"inventory item"
}
fn verbose_name_plural() -> &'static str {
"inventory items"
}
fn ordering() -> &'static [&'static str] {
&["name", "-created_at"]
}
fn get_latest_by() -> Option<&'static str> {
Some("created_at")
}
fn unique_together() -> &'static [&'static [&'static str]] {
&[&["sku", "warehouse_id"]]
}
}
#[derive(Default)]
struct ValidModel;
impl ModelValidation for ValidModel {}
#[derive(Default)]
struct InvalidModel;
impl ModelValidation for InvalidModel {
fn validate(&self) -> Result<(), ValidationError> {
Err(ValidationError {
field: "name".to_string(),
message: "This field is required.".to_string(),
code: "required".to_string(),
})
}
}
#[test]
fn model_meta_defaults_use_table_name_and_empty_options() {
assert_eq!(DefaultMetaModel::table_name(), "widgets");
assert_eq!(DefaultMetaModel::verbose_name(), "widgets");
assert_eq!(DefaultMetaModel::verbose_name_plural(), "");
assert!(DefaultMetaModel::ordering().is_empty());
assert_eq!(DefaultMetaModel::get_latest_by(), None);
assert!(DefaultMetaModel::unique_together().is_empty());
}
#[test]
fn model_meta_supports_custom_options() {
assert_eq!(CustomMetaModel::table_name(), "inventory_item");
assert_eq!(CustomMetaModel::verbose_name(), "inventory item");
assert_eq!(CustomMetaModel::verbose_name_plural(), "inventory items");
assert_eq!(CustomMetaModel::ordering(), &["name", "-created_at"]);
assert_eq!(CustomMetaModel::get_latest_by(), Some("created_at"));
assert_eq!(
CustomMetaModel::unique_together(),
&[&["sku", "warehouse_id"]][..]
);
}
#[test]
fn model_validation_defaults_to_noop() {
ValidModel
.full_clean()
.expect("default validation should pass");
}
#[test]
fn model_validation_full_clean_delegates_to_validate() {
let error = InvalidModel
.full_clean()
.expect_err("full_clean should return validate errors");
assert_eq!(
error,
ValidationError {
field: "name".to_string(),
message: "This field is required.".to_string(),
code: "required".to_string(),
}
);
}
}