So I'm in the process of working on a web application that has implemented security questions into it's registration process. Because of the way my models are setup and the fact that I am trying to use Django's Class based views (CBV), I've had a bit of problems getting this all to integrate cleanly. Here are what my models look like:
Model.py
class AcctSecurityQuestions(models.Model):
class Meta:
db_table = 'security_questions'
id = models.AutoField(primary_key=True)
question = models.CharField(max_length = 250, null=False)
def __unicode__(self):
return u'%s' % self.question
class AcctUser(AbstractBaseUser, PermissionsMixin):
...
user_questions = models.ManyToManyField(AcctSecurityQuestions, through='SecurityQuestionsInter')
...
class SecurityQuestionsInter(models.Model):
class Meta:
db_table = 'security_questions_inter'
acct_user = models.ForeignKey(AcctUser)
security_questions = models.ForeignKey(AcctSecurityQuestions, verbose_name="Security Question")
answer = models.CharField(max_length=128, null=False)
Here is what my current view looks like:
View.py
class AcctRegistration(CreateView):
template_name = 'registration/registration_form.html'
disallowed_url_name = 'registration_disallowed'
model = AcctUser
backend_path = 'registration.backends.default.DefaultBackend'
form_class = AcctRegistrationForm
success_url = 'registration_complete'
def form_valid(self, form):
context = self.get_context_data()
securityquestion_form = context['formset']
if securityquestion_form.is_valid():
self.object = form.save()
securityquestion_form.instance = self.object
securityquestion_form.save()
return HttpResponseRedirect(self.get_success_url())
else:
return self.render_to_response(self.get_context_data(form=form))
def get_context_data(self, **kwargs):
ctx = super(AcctRegistration, self).get_context_data(**kwargs)
if self.request.POST:
ctx['formset'] = SecurityQuestionsInLineFormSet(self.request.POST, instance=self.object)
ctx['formset'].full_clean()
else:
ctx['formset'] = SecurityQuestionsInLineFormSet(instance=self.object)
return ctx
And for giggles and completeness here is what my form looks like:
Forms.py
class AcctRegistrationForm(ModelForm):
password1 = CharField(widget=PasswordInput(attrs=attrs_dict, render_value=False),
label="Password")
password2 = CharField(widget=PasswordInput(attrs=attrs_dict, render_value=False),
label="Password (again)")
class Meta:
model = AcctUser
...
def clean(self):
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise ValidationError(_("The two password fields didn't match."))
return self.cleaned_data
SecurityQuestionsInLineFormSet = inlineformset_factory(AcctUser,
SecurityQuestionsInter,
extra=2,
max_num=2,
can_delete=False
)
This post helped me a lot, however in the most recent comments of the chosen answer, its mentioned that formset data should be integrated into the form in the overidden get and post methods:
django class-based views with inline model-form or formset
If I am overiding the get
and post
how would I add in my data from my formset? And what would I call to loop over the formset data?