I want to create a grouped formset, based on data within an existing model.
I have the below models, which are pre-populated with data. Where there are several technique ‘categories’ I want to group my formsets by in TechniqueCategory, with associated ‘subcategories’ part of each group in TechniqueSubCategory.
Note that I also want to use the title field entries in TechniqueSubCategory as labels for each form.
class TechniqueCategory(models.Model):
title = models.CharField(max_length=120)
class Meta:
verbose_name = "Technique Category"
verbose_name_plural = "Technique Categories"
ordering = ('title',)
class TechniqueSubCategory(models.Model):
title = models.CharField(max_length=120)
class Meta:
verbose_name = "Technique Sub Category"
verbose_name_plural = "Technique Sub Categories"
ordering = ('title',)
class Technique(models.Model):
technique_category = models.ForeignKey(
TechniqueCategory,
related_name='technique',
verbose_name=_('Technique Category'),
on_delete=models.CASCADE
)
technique_sub_category = models.ForeignKey(
TechniqueSubCategory,
related_name='technique',
verbose_name=_('Technique Sub Category'),
on_delete=models.CASCADE
)
class Meta:
verbose_name = "Technique"
verbose_name_plural = "Techniques"
I then have the following model, which is what I want to iteratively create my formsets from. I want to allow the user to set a ‘rating’ for each technique sub category. Note that the profile field is associated to each user.
class TechniqueRating(models.Model):
profile = models.ForeignKey(
Profile,
related_name='technique_rating',
verbose_name=_('Profile'),
on_delete=models.CASCADE
)
technique_sub_category = models.ForeignKey(
TechniqueSubCategory,
related_name='technique_rating',
verbose_name=_('Technique Sub Category'),
on_delete=models.CASCADE
)
rating = models.PositiveSmallIntegerField(_('Rating'), default=0)
class Meta:
verbose_name = "Technique Rating"
verbose_name_plural = "Technique Rating"
How would I go about doing this? At the moment, I have the following in get_context_data of my formview:
technique_sub_category_qs = TechniqueSubCategory.objects.all()
technique_rating_qs = TechniqueRating.objects.filter(profile=user_profile_instance).order_by('technique_sub_category__title').values()
technique_rating_formset_class = modelformset_factory(
TechniqueRating,
fields=('technique_sub_category', 'rating'),
extra=len(technique_sub_category_qs),
max_num=len(technique_sub_category_qs),
formset=TechniqueRatingFormSet
)
ctx['technique_formset'] = technique_rating_formset_class(
self.request.POST or None,
prefix='technique',
initial=technique_rating_qs
)
Where these are my form classes:
class TechniqueRatingFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(TechniqueRatingFormSet, self).__init__(*args, **kwargs)
technique_sub_category_qs = TechniqueSubCategory.objects.all().order_by('title').values_list('title', flat=True)
for i in range(0, len(self)):
self[i].fields['technique_sub_category'].label = technique_sub_category_qs[i]
class TechniqueRatingModelForm(forms.ModelForm):
class Meta:
model = TechniqueRating
fields = ('technique_sub_category', 'rating',)
And in my view post, I’m doing this:
technique_formset = technique_formset_class(request.POST, prefix='technique')
if technique_formset.is_valid():
ls_technique_cleaned_data = []
for count, technique_form in enumerate(technique_formset):
ls_technique_cleaned_data.append(
TechniqueRating(
profile=user_profile_instance,
**technique_form.cleaned_data
)
)
TechniqueRating.objects.filter(profile=user_profile_instance).delete()
TechniqueRating.objects.bulk_create(ls_technique_cleaned_data)