Django, DataWizard, SessionWizardView get current user - django

I am creating a series of forms, processed via Django's form wizard, however, the forms consist of many dropdown boxes, the content of which depend on the current user and therefore, I need to pass the User as a kewword argument.
What I have at the moment is this:
class ViewDataWizard(SessionWizardView):
template_name="wizards/data/view_data.html"
def done(self, form_list,**kwargs):
form_data = process_form_data(form_list)
return render_to_response('wizards/data/view_data_done.html',{'form_data':form_data})
However, I am seeking to produce this:
class ViewDataWizard(SessionWizardView):
template_name="wizards/data/view_data.html"
def get_form_kwargs(self, step):
if step == 0:
return {'user':<USEROBJECT>}
else:
return {}
def done(self, form_list,**kwargs):
form_data = process_form_data(form_list)
return render_to_response('wizards/data/view_data_done.html',{'form_data':form_data})
Where in the second example above, I need to substitute USEROBJECT with the current user, to seed the series of forms.
Am I missing something really obvious? Traditionally I would get user from request.user in a given view, however, this seems elusive in the forms wizard process...

You can get the request object from self.
def get_form_kwargs(self, step):
if step == 0:
return {'user': self.request.user}
else:
return {}

Related

Django conditional field display on form

I am trying to make a simple form, that conditionally shows the website input field based on the value of another database field (that is not on the form) status. For the sake of this process the status field is not editable by the user, just by the admin. Both fields are in the same table: profile.
After working at this for a while I copped-out and just did the conditional hiding and showing on the template. But, I realise this is the unsophisticated method, and would like to improve it.
What I tried so far in forms.py:
class WebsiteForm(forms.ModelForm):
class Meta:
model = Profile
fields = (
'e-mail',
'website',
)
if Profile.status == 'personal' :
exclude = ('website',)
This method in forms.py works effectively, in that I can conditionally show and hide the field if I use test comparitors in the if statement like:
if 1 == 1:
or
if 1 != 1:
But, I cannot get an effective test using the field Profile.status, the value in the field seems to be unavailable at the point the if test in forms.py is performed.
If I use print(Profile.status) I get the following output in the terminal: user__profile__status__isnull, so I think this means that I am at least testing the correct field in the database. Although I am also noting that this output only shows at initialisation of runserver, not when the form page is accessed.
One final point, the user is authenticated and editing their own record.
Any help very much appreciated.
After a lot of trial and even more error, and some wide-ranging searching, I found the answer via the documentation at https://ccbv.co.uk/.
Essentially the path I decided to take was to use a different form for the respective fields that I wanted to use (I'm sure there are other solutions out there that add or subtract fields from the views). This involved changing the form_class with get_form_class:
# views.py
class telephone_view(UpdateView):
template_name = 'account/telephone.html'
#no need to define "form_class" here
#form_class = TelephoneForm
success_url = '/accounts/telephone/'
def get_form_class(self):
if self.request.user.profile.status == 'managed':
messages.success(self.request, _('you got the managed form'))
return TelephoneFormExtended
else:
messages.success(self.request, _('you got the other form'))
return TelephoneFormLight
def get_object(self, queryset=None):
return Profile.get_or_create_for_user(self.request.user)
def form_valid(self, form):
messages.success(self.request, _('Your telephone setting was updated'))
return super(telephone_view, self).form_valid(form)
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(telephone_view, self).dispatch(*args, **kwargs)
After working it out for myself I also found this answer which does the same thing:
Updateview with dynamic form_class

Re-display table after form submit

Good afternoon,
I have a simple Django 2.2 application for users to check in equipment they have checked out. A table of users and items they have checked out dominates the top of the page. On the very bottom row, a single text/submit form. I would like this to happen:
user enters equipment id and submits
page re-displays with: name removed from table (if success), form cleared, success/fail message next to cleared form.
I am close. All of my logic and queries work, my item gets checked back in. However, the page re-renders with no table of users, just the form with the old data still in it.
views.py
class EquipmentReturn(View):
def get(self, request, *args, **kwargs):
# get checked out items for display table -this works
form = ExpressCheckInForm(request.POST)
return render(request, 'eq_return.html',
context={'master_table': master_table,
'form': form}
def post(self, request):
if request.method == 'POST'
form = ExpressCheckInForm(request.POST)
if form.is_valid():
# this checks the item back in (or not) and creates messages-works
else:
form - ExpressCheckInForm()
return render(request, 'eq_return.html', context={'form': form}
I know there is a better way to do this. For instance, my form would not appear until I declared it in the get function. How can I make all of this happen on one page? Thanks!
I think something like this might work. I assume that there is missing code here, for example where you get the master_table.
class EquipmentReturn(View):
def get(self, request, *args, **kwargs):
# get checked out items for display table -this works
form = ExpressCheckInForm()
return render(
request, 'eq_return.html',
context={'master_table': master_table, 'form': form},
)
def post(self, request):
form = ExpressCheckInForm(request.POST)
if form.is_valid():
# this checks the item back in (or not) and creates messages-works
# after saving the form or whatever you want, you just need to redirect back
# to your url. It will call get again and start over
return HttpResonseRedirect(reverse('your-url-name'))
return render(request, 'eq_return.html', context={'form': form})
It looks like you are still in the function based view mindset. Search differences and how to understand and use class based views.

Call post function of a class based view using formview

I am new to django class based views and may be the way I am approaching this is a little naive, so I would appreciate if you could suggest a better way.
So my problem is here:
There are three types of users in my project. 1. Student, 2. Teacher, 3. Parent. I need to be able to show different user settings pertaining to each type of user when the user requests the settings page in their respective forms. Also, I need to be able to save the data into the respective tables as the user submits the form.
I have a class based view (UserSettingsView):
class UserSettingsView(LoginRequiredMixin, FormView):
success_url = '.'
template_name = 'accts/usersettings.html'
def get_initial(self):
if self.request.user.is_authenticated():
user_obj = get_user_model().objects.get(email=self.request.user.email)
if user_obj.profile.is_student:
return {
'first_name': user_obj.profile.first_name,
'last_name': user_obj.profile.last_name,
""" and other student field variables """
}
if user_obj.profile.is_teacher:
return {
""" Teacher field variables """
}
else:
return render_to_response('allauth/account/login.html')
def form_valid(self, form):
messages.add_message(self.request, messages.SUCCESS, 'Settings Saved!')
return super(UserSettingsView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(UserSettingsView, self).get_context_data(**kwargs)
context['user'] = get_user_model().objects.get(email=self.request.user.email)
context['userprofile'] = UserProfile.objects.get(user_id=context['user'])
return context
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
form.full_clean()
if form.is_valid():
user = request.user
user.profile.first_name = form.cleaned_data['first_name']
user.profile.last_name = form.cleaned_data['last_name']
user.profile.save()
if user.profile.is_student:
""" update student database """
user.save()
user.student.save()
if user.profile.is_teacher:
""" update teacher database """
user.save()
user.teacher.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
Different instances of Usersettings view are called using the pick_settings generic view.
url(regex=r'^profilesettings/',view=pick_settings,name='profilesettings'),
And here is the pick_settings view:
def pick_settings(request):
if request.user.is_authenticated():
if request.method == 'GET':
if request.user.profile.is_student:
return UserSettingsView.as_view(form_class=StudentSettingsForm)(request)
if request.user.profile.is_teacher:
return UserSettingsView.as_view(form_class=TeacherSettingsForm)(request)
if request.user.profile.is_parent:
return UserSettingsView.as_view(form_class=ParentSettingsForm)(request)
else:
if request.method == 'POST':
"""
return ***<--- I need to know what to pass here to be able to call the appropriate post function of the UserSettingsView?---->"""***
else:
return HttpResponseRedirect('/accounts/login/')
I need to be able to call the post function of the UserSettingsView. May be using the get_context_data? But I am not sure how.
Again it will be great, if someone could suggest a better way because I am pretty sure this might be violating the DRY principle. Although, I am not too concerned with that as long as the job gets done as I am running a deadline. :) Thanks!
FormView has a method get_form_class(). It is called from get() and post(), so self.request will already be set (as will be self.request.user). Consequently,
class UserSettingsView(LoginRequiredMixin, FormView):
[...]
def get_form_class(self):
# no need to check is_authenticated() as we have LoginRequiredMixin
if request.user.profile.is_student:
return StudentSettingsForm
elif user.profile.is_teacher:
return TeacherSettingsForm
elif user.profile.is_parent:
return ParentSettingsForm
This should already to the trick as you get the correct form for each user type.
If you also need to render different templates, override get_template_names():
def get_template_names(self):
if request.user.profile.is_student:
return ['myapp/settings/student.html']
elif user.profile.is_teacher:
return ['myapp/settings/teacher.html']
elif user.profile.is_parent:
return ['myapp/settings/parent.html']
DRY can be achieved using proper inheritance in the templates combining common template fragments.
And lest I forget (I already forgot): To get rid of the if in the post() method of your view, simple override the save() method of you forms which I assume are ModelForms, anyway.

Django - Need help for TemplateView: Queryset and not updating

I am building a TemplateView with 2 forms, one to allow user to select the customer (CustomerForm) and another to add the order (OrderForm) for the customer.
Code:
class DisplayOrdersView(TemplateView):
template_name = 'orders/orders_details_form.html'
def get_context_data(self, **kwargs):
context = kwargs
context['shippingdetailsform'] = ShippingDetailsForm(prefix='shippingdetailsform')
context['ordersform'] = OrdersForm(prefix='ordersform')
return context
def dispatch(self, request, *args, **kwargs):
return super(DisplayOrdersView, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
profile=request.user.get_profile()
if context['shippingdetailsform'].is_valid():
instance = context['shippingdetailsform'].save(commit=False)
instance.profile = profile
instance.save()
messages.success(request, 'orders for {0} saved'.format(profile))
elif context['ordersform'].is_valid():
instance = ordersform.save(commit=False)
shippingdetails, created = shippingdetails.objects.get_or_create(profile=profile)
shippingdetails.save()
instance.user = customer
instance.save()
messages.success(request, 'orders details for {0} saved.'.format(profile))
else:
messages.error(request, 'Error(s) saving form')
return self.render_to_response(context)
Firstly, I can't seem to load any existing data into the forms. Assuming a onetoone relationship between UserProfile->ShippingDetails (fk: UserProfile)->Orders (fk:ShippingDetails), how can I query the appropriate variables into the form on load?
Also, how can I save the data? It throws an error when saving and I have been unable to retrieve useful debug information.
Is my approach correct for having multiple forms in a templateview?
You're not passing the POST data into the forms at any point. You need to do this when you instantiate them. I would move the instantiation out of get_context_data and do it in get and post: the first as you have it now, and the second passing request.POST.
Also note that you probably want to check both forms are valid before saving either of them, rather than checking and saving each in turn. The way you have it now, if the first one is valid it won't even check the second, let alone save it, so you won't get any errors on the template if the first is valid but the second is invalid.

Django FormWizards: How to painlessly pass user-entered data between forms?

I'm using the FormWizard functionality in Django 1.4.3.
I have successfully created a 4-step form. In the first 3 steps of the form it correctly takes information from the user, validates it, etc. In Step #4, it right now just shows a "Confirm" button. Nothing else. When you hit "Confirm" on step #4, does something useful with it in the done() function. So far it all works fine.
However, I would like to make it so that in step 4 (The confirmation step), it shows the user the data they have entered in the previous steps for their review. I'm trying to figure out the most painless way to make this happen. So far I am creating an entry in the context called formList which contains a list of forms that have already been completed.
class my4StepWizard(SessionWizardView):
def get_template_names(self):
return [myWizardTemplates[self.steps.current]]
def get_context_data(self, form, **kwargs):
context = super(my4StepWizard, self).get_context_data(form=form, **kwargs)
formList = [self.get_form_list()[i[0]] for i in myWizardForms[:self.steps.step0]]
context.update(
{
'formList': formList,
}
)
return context
def done(self, form_list, **kwargs):
# Do something here.
return HttpResponseRedirect('/doneWizard')
Form #1 has an input field called myField.
So in my template for step #4, I would like to do {{ formList.1.clean_myField }}. However, when I do that, I get the following error:
Exception Value:
'my4StepWizard' object has no attribute 'cleaned_data'
It seems that the forms I am putting into formList are unbounded. So they don't contain the user's data. Is there a fix I can use to get the data itself? I would really like to use the context to pass the data as I'm doing above.
Try this:
def get_context_data(self, form, **kwargs):
previous_data = {}
current_step = self.steps.current # 0 for first form, 1 for the second form..
if current_step == '3': # assuming no step is skipped, this will be the last form
for count in range(3):
previous_data[unicode(count)] = self.get_cleaned_data_for_step(unicode(count))
context = super(my4StepWizard, self).get_context_data(form=form, **kwargs)
context.update({'previous_cleaned_data':previous_data})
return context
previous_data is a dictionary and its keys are the steps for the wizard (0 indexed). The item for each key is the cleaned_data for the form in the step which is the same as the key.