Skip to main content

reinhardt_forms/
formsets.rs

1//! Advanced FormSet functionality
2//!
3//! This module provides advanced FormSet features including inline formsets,
4//! model-based formsets, and dynamic formset generation.
5
6use crate::FormError;
7use crate::formset::FormSet;
8use crate::model_form::{FormModel, ModelForm};
9use std::marker::PhantomData;
10
11/// InlineFormSet for managing forms related to a parent model
12///
13/// InlineFormSets are used to edit related objects together with a parent object,
14/// similar to Django's inline formsets for admin.
15pub struct InlineFormSet<P: FormModel, C: FormModel> {
16	parent: P,
17	_formset: FormSet,
18	fk_field: String,
19	child_forms: Vec<ModelForm<C>>,
20	_phantom_parent: PhantomData<P>,
21	_phantom_child: PhantomData<C>,
22}
23
24impl<P: FormModel, C: FormModel> InlineFormSet<P, C> {
25	/// Create a new InlineFormSet
26	///
27	/// # Arguments
28	///
29	/// * `parent` - The parent model instance
30	/// * `fk_field` - The foreign key field name on the child model
31	///
32	/// # Examples
33	///
34	/// ```ignore
35	/// let parent = Author { id: 1, name: "John".to_string() };
36	/// let formset = InlineFormSet::new(parent, "author_id".to_string());
37	/// ```
38	pub fn new(parent: P, fk_field: String) -> Self {
39		Self {
40			parent,
41			_formset: FormSet::new("inline".to_string()),
42			fk_field,
43			child_forms: Vec::new(),
44			_phantom_parent: PhantomData,
45			_phantom_child: PhantomData,
46		}
47	}
48
49	/// Add a child form to the formset
50	pub fn add_child_form(&mut self, form: ModelForm<C>) {
51		self.child_forms.push(form);
52	}
53
54	/// Get the parent model instance
55	pub fn parent(&self) -> &P {
56		&self.parent
57	}
58
59	/// Get the foreign key field name
60	pub fn fk_field(&self) -> &str {
61		&self.fk_field
62	}
63
64	/// Get all child forms
65	pub fn child_forms(&self) -> &[ModelForm<C>] {
66		&self.child_forms
67	}
68
69	/// Save the formset and all related child instances.
70	///
71	/// This method saves the parent model first, retrieves the parent's primary
72	/// key, sets the foreign key on each child instance, then saves each child.
73	///
74	/// # Errors
75	///
76	/// Returns an error if any save operation fails or if the parent model
77	/// does not have an `id` field after saving.
78	pub fn save(&mut self) -> Result<(), FormError> {
79		// Save parent first
80		self.parent
81			.save()
82			.map_err(|e| FormError::Validation(format!("Failed to save parent: {}", e)))?;
83
84		// Get parent ID to set as foreign key on child instances
85		let parent_id = self.parent.get_field("id").ok_or_else(|| {
86			FormError::Validation("Parent model does not have an 'id' field".to_string())
87		})?;
88
89		// Save each child with the foreign key set to the parent ID
90		let fk_field = self.fk_field.clone();
91		for child_form in &mut self.child_forms {
92			// Set the foreign key on the child instance before saving
93			child_form.set_field_value(&fk_field, parent_id.clone());
94
95			child_form
96				.save()
97				.map_err(|e| FormError::Validation(format!("Failed to save child: {}", e)))?;
98		}
99
100		Ok(())
101	}
102
103	/// Validate all child forms
104	pub fn is_valid(&mut self) -> bool {
105		let mut all_valid = true;
106
107		for child_form in &mut self.child_forms {
108			if !child_form.is_valid() {
109				all_valid = false;
110			}
111		}
112
113		all_valid
114	}
115}
116
117/// ModelFormSet for managing multiple model instances
118///
119/// This is similar to the base FormSet but specifically designed for model instances.
120pub struct ModelFormSet<T: FormModel> {
121	forms: Vec<ModelForm<T>>,
122	prefix: String,
123	can_delete: bool,
124	can_order: bool,
125	extra: usize,
126	max_num: Option<usize>,
127	min_num: usize,
128	errors: Vec<String>,
129	_phantom: PhantomData<T>,
130}
131
132impl<T: FormModel> ModelFormSet<T> {
133	/// Create a new ModelFormSet
134	///
135	/// # Examples
136	///
137	/// ```ignore
138	/// let formset = ModelFormSet::<User>::new("user".to_string());
139	/// ```
140	pub fn new(prefix: String) -> Self {
141		Self {
142			forms: Vec::new(),
143			prefix,
144			can_delete: false,
145			can_order: false,
146			extra: 1,
147			max_num: Some(1000),
148			min_num: 0,
149			errors: Vec::new(),
150			_phantom: PhantomData,
151		}
152	}
153
154	/// Set extra forms count
155	pub fn with_extra(mut self, extra: usize) -> Self {
156		self.extra = extra;
157		self
158	}
159
160	/// Enable deletion
161	pub fn with_can_delete(mut self, can_delete: bool) -> Self {
162		self.can_delete = can_delete;
163		self
164	}
165
166	/// Enable ordering
167	pub fn with_can_order(mut self, can_order: bool) -> Self {
168		self.can_order = can_order;
169		self
170	}
171
172	/// Set maximum number of forms
173	pub fn with_max_num(mut self, max_num: Option<usize>) -> Self {
174		self.max_num = max_num;
175		self
176	}
177
178	/// Set minimum number of forms
179	pub fn with_min_num(mut self, min_num: usize) -> Self {
180		self.min_num = min_num;
181		self
182	}
183
184	/// Add a model form to the formset.
185	///
186	/// Returns an error if adding the form would exceed `max_num`.
187	pub fn add_form(&mut self, form: ModelForm<T>) -> Result<(), String> {
188		if let Some(max) = self.max_num
189			&& self.forms.len() >= max
190		{
191			return Err(format!(
192				"Cannot add form: maximum number of forms ({}) reached",
193				max
194			));
195		}
196		self.forms.push(form);
197		Ok(())
198	}
199
200	/// Get all forms
201	pub fn forms(&self) -> &[ModelForm<T>] {
202		&self.forms
203	}
204
205	/// Get mutable access to forms
206	pub fn forms_mut(&mut self) -> &mut Vec<ModelForm<T>> {
207		&mut self.forms
208	}
209
210	/// Validate all forms in the formset
211	pub fn is_valid(&mut self) -> bool {
212		self.errors.clear();
213
214		let mut all_valid = true;
215		for form in &mut self.forms {
216			if !form.is_valid() {
217				all_valid = false;
218			}
219		}
220
221		// Check minimum number
222		if self.forms.len() < self.min_num {
223			self.errors
224				.push(format!("Please submit at least {} forms", self.min_num));
225			all_valid = false;
226		}
227
228		// Check maximum number
229		if let Some(max) = self.max_num
230			&& self.forms.len() > max
231		{
232			self.errors
233				.push(format!("Please submit no more than {} forms", max));
234			all_valid = false;
235		}
236
237		all_valid
238	}
239
240	/// Get validation errors
241	pub fn errors(&self) -> &[String] {
242		&self.errors
243	}
244
245	/// Save all forms in the formset
246	pub fn save(&mut self) -> Result<(), FormError> {
247		if !self.is_valid() {
248			return Err(FormError::Validation(
249				"Cannot save invalid formset".to_string(),
250			));
251		}
252
253		for form in &mut self.forms {
254			form.save()
255				.map_err(|e| FormError::Validation(format!("Failed to save form: {}", e)))?;
256		}
257
258		Ok(())
259	}
260
261	/// Get the formset prefix
262	pub fn prefix(&self) -> &str {
263		&self.prefix
264	}
265}
266
267/// Factory for creating FormSets dynamically
268///
269/// This allows you to create FormSets with different configurations
270/// without defining them statically.
271pub struct FormSetFactory {
272	prefix: String,
273	extra: usize,
274	can_delete: bool,
275	can_order: bool,
276	max_num: Option<usize>,
277	min_num: usize,
278}
279
280impl FormSetFactory {
281	/// Create a new FormSetFactory
282	///
283	/// # Examples
284	///
285	/// ```
286	/// use reinhardt_forms::FormSetFactory;
287	///
288	/// let factory = FormSetFactory::new("form".to_string());
289	/// assert_eq!(factory.extra(), 1);
290	/// ```
291	pub fn new(prefix: String) -> Self {
292		Self {
293			prefix,
294			extra: 1,
295			can_delete: false,
296			can_order: false,
297			max_num: Some(1000),
298			min_num: 0,
299		}
300	}
301
302	/// Set extra forms count
303	///
304	/// # Examples
305	///
306	/// ```
307	/// use reinhardt_forms::FormSetFactory;
308	///
309	/// let factory = FormSetFactory::new("form".to_string())
310	///     .with_extra(3);
311	/// assert_eq!(factory.extra(), 3);
312	/// ```
313	pub fn with_extra(mut self, extra: usize) -> Self {
314		self.extra = extra;
315		self
316	}
317
318	/// Enable deletion
319	///
320	/// # Examples
321	///
322	/// ```
323	/// use reinhardt_forms::FormSetFactory;
324	///
325	/// let factory = FormSetFactory::new("form".to_string())
326	///     .with_can_delete(true);
327	/// assert!(factory.can_delete());
328	/// ```
329	pub fn with_can_delete(mut self, can_delete: bool) -> Self {
330		self.can_delete = can_delete;
331		self
332	}
333
334	/// Enable ordering
335	///
336	/// # Examples
337	///
338	/// ```
339	/// use reinhardt_forms::FormSetFactory;
340	///
341	/// let factory = FormSetFactory::new("form".to_string())
342	///     .with_can_order(true);
343	/// assert!(factory.can_order());
344	/// ```
345	pub fn with_can_order(mut self, can_order: bool) -> Self {
346		self.can_order = can_order;
347		self
348	}
349
350	/// Set maximum number of forms
351	///
352	/// # Examples
353	///
354	/// ```
355	/// use reinhardt_forms::FormSetFactory;
356	///
357	/// let factory = FormSetFactory::new("form".to_string())
358	///     .with_max_num(Some(10));
359	/// assert_eq!(factory.max_num(), Some(10));
360	/// ```
361	pub fn with_max_num(mut self, max_num: Option<usize>) -> Self {
362		self.max_num = max_num;
363		self
364	}
365
366	/// Set minimum number of forms
367	///
368	/// # Examples
369	///
370	/// ```
371	/// use reinhardt_forms::FormSetFactory;
372	///
373	/// let factory = FormSetFactory::new("form".to_string())
374	///     .with_min_num(2);
375	/// assert_eq!(factory.min_num(), 2);
376	/// ```
377	pub fn with_min_num(mut self, min_num: usize) -> Self {
378		self.min_num = min_num;
379		self
380	}
381
382	/// Get extra forms count
383	pub fn extra(&self) -> usize {
384		self.extra
385	}
386
387	/// Check if deletion is enabled
388	pub fn can_delete(&self) -> bool {
389		self.can_delete
390	}
391
392	/// Check if ordering is enabled
393	pub fn can_order(&self) -> bool {
394		self.can_order
395	}
396
397	/// Get maximum number of forms
398	pub fn max_num(&self) -> Option<usize> {
399		self.max_num
400	}
401
402	/// Get minimum number of forms
403	pub fn min_num(&self) -> usize {
404		self.min_num
405	}
406
407	/// Create a FormSet from this factory
408	///
409	/// # Examples
410	///
411	/// ```
412	/// use reinhardt_forms::{FormSetFactory, FormSet};
413	///
414	/// let factory = FormSetFactory::new("form".to_string())
415	///     .with_extra(3)
416	///     .with_can_delete(true);
417	///
418	/// let formset = factory.create();
419	/// assert_eq!(formset.prefix(), "form");
420	/// assert!(formset.can_delete());
421	/// ```
422	pub fn create(&self) -> FormSet {
423		FormSet::new(self.prefix.clone())
424			.with_extra(self.extra)
425			.with_can_delete(self.can_delete)
426			.with_can_order(self.can_order)
427			.with_max_num(self.max_num)
428			.with_min_num(self.min_num)
429	}
430
431	/// Create a ModelFormSet from this factory
432	///
433	/// # Examples
434	///
435	/// ```ignore
436	/// use reinhardt_forms::FormSetFactory;
437	///
438	/// let factory = FormSetFactory::new("user".to_string())
439	///     .with_extra(2);
440	///
441	/// let formset = factory.create_model_formset::<User>();
442	/// ```
443	pub fn create_model_formset<T: FormModel>(&self) -> ModelFormSet<T> {
444		ModelFormSet::new(self.prefix.clone())
445			.with_extra(self.extra)
446			.with_can_delete(self.can_delete)
447			.with_can_order(self.can_order)
448			.with_max_num(self.max_num)
449			.with_min_num(self.min_num)
450	}
451}
452
453#[cfg(test)]
454mod tests {
455	use super::*;
456	use crate::ModelFormConfig;
457	use crate::model_form::FieldType;
458	use serde_json::Value;
459
460	// Test model implementation
461	#[derive(Clone)]
462	struct TestModel {
463		id: Option<i64>,
464		name: String,
465		email: String,
466	}
467
468	impl FormModel for TestModel {
469		fn field_names() -> Vec<String> {
470			vec!["id".to_string(), "name".to_string(), "email".to_string()]
471		}
472
473		fn field_type(name: &str) -> Option<FieldType> {
474			match name {
475				"id" => Some(FieldType::Integer),
476				"name" => Some(FieldType::Char {
477					max_length: Some(100),
478				}),
479				"email" => Some(FieldType::Email),
480				_ => None,
481			}
482		}
483
484		fn get_field(&self, name: &str) -> Option<Value> {
485			match name {
486				"id" => self.id.map(|id| Value::Number(id.into())),
487				"name" => Some(Value::String(self.name.clone())),
488				"email" => Some(Value::String(self.email.clone())),
489				_ => None,
490			}
491		}
492
493		fn set_field(&mut self, name: &str, value: Value) -> Result<(), String> {
494			match name {
495				"id" => {
496					self.id = value.as_i64();
497					Ok(())
498				}
499				"name" => {
500					self.name = value
501						.as_str()
502						.ok_or("Expected string for name")?
503						.to_string();
504					Ok(())
505				}
506				"email" => {
507					self.email = value
508						.as_str()
509						.ok_or("Expected string for email")?
510						.to_string();
511					Ok(())
512				}
513				_ => Err(format!("Unknown field: {}", name)),
514			}
515		}
516
517		fn save(&mut self) -> Result<(), String> {
518			if self.id.is_none() {
519				self.id = Some(1);
520			}
521			Ok(())
522		}
523	}
524
525	// Test child model
526	#[derive(Clone)]
527	struct ChildModel {
528		id: Option<i64>,
529		parent_id: Option<i64>,
530		content: String,
531	}
532
533	impl FormModel for ChildModel {
534		fn field_names() -> Vec<String> {
535			vec![
536				"id".to_string(),
537				"parent_id".to_string(),
538				"content".to_string(),
539			]
540		}
541
542		fn field_type(name: &str) -> Option<FieldType> {
543			match name {
544				"id" | "parent_id" => Some(FieldType::Integer),
545				"content" => Some(FieldType::Text),
546				_ => None,
547			}
548		}
549
550		fn get_field(&self, name: &str) -> Option<Value> {
551			match name {
552				"id" => self.id.map(|id| Value::Number(id.into())),
553				"parent_id" => self.parent_id.map(|id| Value::Number(id.into())),
554				"content" => Some(Value::String(self.content.clone())),
555				_ => None,
556			}
557		}
558
559		fn set_field(&mut self, name: &str, value: Value) -> Result<(), String> {
560			match name {
561				"id" => {
562					self.id = value.as_i64();
563					Ok(())
564				}
565				"parent_id" => {
566					self.parent_id = value.as_i64();
567					Ok(())
568				}
569				"content" => {
570					self.content = value
571						.as_str()
572						.ok_or("Expected string for content")?
573						.to_string();
574					Ok(())
575				}
576				_ => Err(format!("Unknown field: {}", name)),
577			}
578		}
579
580		fn save(&mut self) -> Result<(), String> {
581			if self.id.is_none() {
582				self.id = Some(1);
583			}
584			Ok(())
585		}
586	}
587
588	#[test]
589	fn test_inline_formset_creation() {
590		let parent = TestModel {
591			id: Some(1),
592			name: "Parent".to_string(),
593			email: "parent@example.com".to_string(),
594		};
595
596		let formset = InlineFormSet::<TestModel, ChildModel>::new(parent, "parent_id".to_string());
597
598		assert_eq!(formset.fk_field(), "parent_id");
599		assert_eq!(formset.parent().name, "Parent");
600		assert_eq!(formset.child_forms().len(), 0);
601	}
602
603	#[test]
604	fn test_inline_formset_add_child() {
605		let parent = TestModel {
606			id: Some(1),
607			name: "Parent".to_string(),
608			email: "parent@example.com".to_string(),
609		};
610
611		let mut formset =
612			InlineFormSet::<TestModel, ChildModel>::new(parent, "parent_id".to_string());
613
614		let child = ChildModel {
615			id: None,
616			parent_id: None,
617			content: "Child content".to_string(),
618		};
619		let child_form = ModelForm::new(Some(child), ModelFormConfig::new());
620		formset.add_child_form(child_form);
621
622		assert_eq!(formset.child_forms().len(), 1);
623	}
624
625	#[test]
626	fn test_inline_formset_save() {
627		let parent = TestModel {
628			id: Some(1),
629			name: "Parent".to_string(),
630			email: "parent@example.com".to_string(),
631		};
632
633		let mut formset =
634			InlineFormSet::<TestModel, ChildModel>::new(parent, "parent_id".to_string());
635
636		let child = ChildModel {
637			id: None,
638			parent_id: None,
639			content: "Child content".to_string(),
640		};
641		let child_form = ModelForm::new(Some(child), ModelFormConfig::new());
642		formset.add_child_form(child_form);
643
644		let result = formset.save();
645		assert!(result.is_ok());
646	}
647
648	#[test]
649	fn test_model_formset_creation() {
650		let formset = ModelFormSet::<TestModel>::new("test".to_string());
651
652		assert_eq!(formset.prefix(), "test");
653		assert_eq!(formset.forms().len(), 0);
654		assert!(!formset.can_delete);
655		assert!(!formset.can_order);
656	}
657
658	#[test]
659	fn test_model_formset_add_form() {
660		let mut formset = ModelFormSet::<TestModel>::new("test".to_string());
661
662		let instance = TestModel {
663			id: None,
664			name: "Test".to_string(),
665			email: "test@example.com".to_string(),
666		};
667		let form = ModelForm::new(Some(instance), ModelFormConfig::new());
668		formset.add_form(form).unwrap();
669
670		assert_eq!(formset.forms().len(), 1);
671	}
672
673	#[test]
674	fn test_model_formset_validation() {
675		let mut formset = ModelFormSet::<TestModel>::new("test".to_string())
676			.with_min_num(2)
677			.with_max_num(Some(5));
678
679		let instance = TestModel {
680			id: None,
681			name: "Test".to_string(),
682			email: "test@example.com".to_string(),
683		};
684		let form = ModelForm::new(Some(instance), ModelFormConfig::new());
685		formset.add_form(form).unwrap();
686
687		assert!(!formset.is_valid());
688		assert!(!formset.errors().is_empty());
689	}
690
691	#[test]
692	fn test_formset_factory_creation() {
693		let factory = FormSetFactory::new("form".to_string());
694
695		assert_eq!(factory.extra(), 1);
696		assert!(!factory.can_delete());
697		assert!(!factory.can_order());
698		assert_eq!(factory.max_num(), Some(1000));
699		assert_eq!(factory.min_num(), 0);
700	}
701
702	#[test]
703	fn test_formset_factory_builder() {
704		let factory = FormSetFactory::new("form".to_string())
705			.with_extra(3)
706			.with_can_delete(true)
707			.with_can_order(true)
708			.with_max_num(Some(10))
709			.with_min_num(2);
710
711		assert_eq!(factory.extra(), 3);
712		assert!(factory.can_delete());
713		assert!(factory.can_order());
714		assert_eq!(factory.max_num(), Some(10));
715		assert_eq!(factory.min_num(), 2);
716	}
717
718	#[test]
719	fn test_formset_factory_create() {
720		let factory = FormSetFactory::new("form".to_string())
721			.with_extra(3)
722			.with_can_delete(true);
723
724		let formset = factory.create();
725
726		assert_eq!(formset.prefix(), "form");
727		assert!(formset.can_delete());
728	}
729
730	#[test]
731	fn test_formset_factory_create_model_formset() {
732		let factory = FormSetFactory::new("user".to_string())
733			.with_extra(2)
734			.with_min_num(1);
735
736		let formset = factory.create_model_formset::<TestModel>();
737
738		assert_eq!(formset.prefix(), "user");
739		assert_eq!(formset.min_num, 1);
740	}
741}