1use crate::FormError;
7use crate::formset::FormSet;
8use crate::model_form::{FormModel, ModelForm, ModelFormConfig};
9use std::collections::HashMap;
10use std::marker::PhantomData;
11
12#[derive(Debug, Clone)]
14pub struct ModelFormSetConfig {
15 pub form_config: ModelFormConfig,
17 pub can_delete: bool,
19 pub can_order: bool,
21 pub extra: usize,
23 pub max_num: Option<usize>,
25 pub min_num: usize,
27}
28
29impl Default for ModelFormSetConfig {
30 fn default() -> Self {
31 Self {
32 form_config: ModelFormConfig::default(),
33 can_delete: false,
34 can_order: false,
35 extra: 1,
36 max_num: Some(1000),
37 min_num: 0,
38 }
39 }
40}
41
42impl ModelFormSetConfig {
43 pub fn new() -> Self {
55 Self::default()
56 }
57 pub fn with_extra(mut self, extra: usize) -> Self {
68 self.extra = extra;
69 self
70 }
71 pub fn with_can_delete(mut self, can_delete: bool) -> Self {
82 self.can_delete = can_delete;
83 self
84 }
85 pub fn with_can_order(mut self, can_order: bool) -> Self {
96 self.can_order = can_order;
97 self
98 }
99 pub fn with_max_num(mut self, max_num: Option<usize>) -> Self {
110 self.max_num = max_num;
111 self
112 }
113 pub fn with_min_num(mut self, min_num: usize) -> Self {
124 self.min_num = min_num;
125 self
126 }
127 pub fn with_form_config(mut self, form_config: ModelFormConfig) -> Self {
140 self.form_config = form_config;
141 self
142 }
143}
144
145pub struct ModelFormSet<T: FormModel> {
147 model_forms: Vec<ModelForm<T>>,
148 formset: FormSet,
149 _phantom: PhantomData<T>,
150}
151
152impl<T: FormModel> ModelFormSet<T> {
153 pub fn new(prefix: String, instances: Vec<T>, config: ModelFormSetConfig) -> Self {
166 let mut model_forms = Vec::new();
167
168 for instance in instances {
170 let model_form = ModelForm::new(Some(instance), config.form_config.clone());
171 model_forms.push(model_form);
172 }
173
174 for _ in 0..config.extra {
176 let model_form = ModelForm::empty(config.form_config.clone());
177 model_forms.push(model_form);
178 }
179
180 let formset = FormSet::new(prefix)
182 .with_extra(config.extra)
183 .with_can_delete(config.can_delete)
184 .with_can_order(config.can_order)
185 .with_max_num(config.max_num)
186 .with_min_num(config.min_num);
187
188 Self {
189 model_forms,
190 formset,
191 _phantom: PhantomData,
192 }
193 }
194 pub fn empty(prefix: String, config: ModelFormSetConfig) -> Self {
206 Self::new(prefix, Vec::new(), config)
207 }
208 pub fn prefix(&self) -> &str {
210 self.formset.prefix()
211 }
212 pub fn instances(&self) -> Vec<&T> {
214 self.model_forms
215 .iter()
216 .filter_map(|form| form.instance())
217 .collect()
218 }
219 pub fn form_count(&self) -> usize {
221 self.model_forms
223 .iter()
224 .filter(|form| form.instance().is_some())
225 .count()
226 }
227 pub fn total_form_count(&self) -> usize {
229 self.model_forms.len()
231 }
232 pub fn is_valid(&mut self) -> bool {
244 self.model_forms.iter_mut().all(|form| form.is_valid())
246 }
247 pub fn errors(&self) -> Vec<String> {
249 self.model_forms
251 .iter()
252 .flat_map(|model_form| {
253 model_form
254 .form()
255 .errors()
256 .values()
257 .flat_map(|errors| errors.iter().cloned())
258 })
259 .collect()
260 }
261 pub fn save(&mut self) -> Result<Vec<T>, FormError> {
273 if !self.is_valid() {
274 return Err(FormError::Validation("Formset is not valid".to_string()));
275 }
276
277 let mut saved_instances = Vec::new();
278
279 for model_form in &mut self.model_forms {
281 if model_form.instance().is_some() {
283 let instance = model_form.save()?;
285 saved_instances.push(instance);
286 }
287 }
288
289 Ok(saved_instances)
290 }
291 pub fn management_form_data(&self) -> HashMap<String, String> {
306 self.formset.management_form_data()
307 }
308}
309
310pub struct ModelFormSetBuilder<T: FormModel> {
312 config: ModelFormSetConfig,
313 _phantom: PhantomData<T>,
314}
315
316impl<T: FormModel> ModelFormSetBuilder<T> {
317 pub fn new() -> Self {
327 Self {
328 config: ModelFormSetConfig::default(),
329 _phantom: PhantomData,
330 }
331 }
332 pub fn extra(mut self, extra: usize) -> Self {
342 self.config.extra = extra;
343 self
344 }
345 pub fn can_delete(mut self, can_delete: bool) -> Self {
355 self.config.can_delete = can_delete;
356 self
357 }
358 pub fn can_order(mut self, can_order: bool) -> Self {
368 self.config.can_order = can_order;
369 self
370 }
371 pub fn max_num(mut self, max_num: usize) -> Self {
381 self.config.max_num = Some(max_num);
382 self
383 }
384 pub fn min_num(mut self, min_num: usize) -> Self {
394 self.config.min_num = min_num;
395 self
396 }
397 pub fn build(self, prefix: String, instances: Vec<T>) -> ModelFormSet<T> {
409 ModelFormSet::new(prefix, instances, self.config)
410 }
411 pub fn build_empty(self, prefix: String) -> ModelFormSet<T> {
422 ModelFormSet::empty(prefix, self.config)
423 }
424}
425
426impl<T: FormModel> Default for ModelFormSetBuilder<T> {
427 fn default() -> Self {
428 Self::new()
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use serde_json::Value;
436
437 struct Article {
439 id: i32,
440 title: String,
441 content: String,
442 }
443
444 impl FormModel for Article {
445 fn field_names() -> Vec<String> {
446 vec!["id".to_string(), "title".to_string(), "content".to_string()]
447 }
448
449 fn get_field(&self, name: &str) -> Option<Value> {
450 match name {
451 "id" => Some(Value::Number(self.id.into())),
452 "title" => Some(Value::String(self.title.clone())),
453 "content" => Some(Value::String(self.content.clone())),
454 _ => None,
455 }
456 }
457
458 fn set_field(&mut self, name: &str, value: Value) -> Result<(), String> {
459 match name {
460 "id" => {
461 if let Value::Number(n) = value {
462 self.id = n.as_i64().unwrap() as i32;
463 Ok(())
464 } else {
465 Err("Invalid type for id".to_string())
466 }
467 }
468 "title" => {
469 if let Value::String(s) = value {
470 self.title = s;
471 Ok(())
472 } else {
473 Err("Invalid type for title".to_string())
474 }
475 }
476 "content" => {
477 if let Value::String(s) = value {
478 self.content = s;
479 Ok(())
480 } else {
481 Err("Invalid type for content".to_string())
482 }
483 }
484 _ => Err(format!("Unknown field: {}", name)),
485 }
486 }
487
488 fn save(&mut self) -> Result<(), String> {
489 Ok(())
491 }
492 }
493
494 #[test]
495 fn test_model_formset_config() {
496 let config = ModelFormSetConfig::new()
497 .with_extra(3)
498 .with_can_delete(true)
499 .with_max_num(Some(10))
500 .with_min_num(1);
501
502 assert_eq!(config.extra, 3);
503 assert!(config.can_delete);
504 assert_eq!(config.max_num, Some(10));
505 assert_eq!(config.min_num, 1);
506 }
507
508 #[test]
509 fn test_model_formset_empty() {
510 let config = ModelFormSetConfig::new().with_extra(2);
511 let formset = ModelFormSet::<Article>::empty("article".to_string(), config);
512
513 assert_eq!(formset.prefix(), "article");
514 assert_eq!(formset.instances().len(), 0);
515 assert_eq!(formset.total_form_count(), 2);
516 }
517
518 #[test]
519 fn test_model_formset_with_instances() {
520 let instances = vec![
521 Article {
522 id: 1,
523 title: "First Article".to_string(),
524 content: "Content 1".to_string(),
525 },
526 Article {
527 id: 2,
528 title: "Second Article".to_string(),
529 content: "Content 2".to_string(),
530 },
531 ];
532
533 let config = ModelFormSetConfig::new();
534 let formset = ModelFormSet::new("article".to_string(), instances, config);
535
536 assert_eq!(formset.instances().len(), 2);
537 assert_eq!(formset.form_count(), 2);
538 }
539
540 #[test]
541 fn test_model_formset_builder() {
542 let formset = ModelFormSetBuilder::<Article>::new()
543 .extra(3)
544 .can_delete(true)
545 .max_num(5)
546 .build_empty("article".to_string());
547
548 assert_eq!(formset.total_form_count(), 3);
549 }
550
551 #[test]
552 fn test_model_formset_management_data() {
553 let config = ModelFormSetConfig::new().with_extra(2).with_min_num(1);
554 let formset = ModelFormSet::<Article>::empty("article".to_string(), config);
555
556 let mgmt_data = formset.management_form_data();
557
558 assert_eq!(mgmt_data.get("article-TOTAL_FORMS"), Some(&"2".to_string()));
559 assert_eq!(
560 mgmt_data.get("article-INITIAL_FORMS"),
561 Some(&"0".to_string())
562 );
563 assert_eq!(
564 mgmt_data.get("article-MIN_NUM_FORMS"),
565 Some(&"1".to_string())
566 );
567 }
568}