Django - Form across multiple views with progress saving - python

I'm working on a Django project and to make the forms experience far smoother I want to spread a ModelForm across a few pages. It would be ideal if users who are logged in can save their progress in the form without actually posting the content (in this case, a JobApplication where users can come back to filling in info without actually sending off the application).
Currently I have looked at other answers on SO such as this one; but this only shows me how to utilise caching to store information between the views where the form is present.
Models.py (models, forms and views have been simplified for readability):
class JobApplication(models.Model):
job = models.ForeignKey(JobPost,on_delete=models.SET_NULL,...)
user = models.ForeignKey(AUTH_USER_MODEL,...)
details = models.CharField(max_length=300)
skills = models.CharField(max_length=300)
feedback = models.CharField(max_length=300)
#... [insert more fields] ...
Forms.py:
class Application(forms.ModelForm):
details = forms.CharField() # To go in page 1 of the form process
skills = forms.CharField() # To go in page 2
feedback = forms.CharField() # To go in page 3
class Meta:
model = JobApplication
fields = ['details','skills','feedback']
Views.py:
from . import forms
def view1(request):
form = forms.Application()
if request.method == 'POST':
form = forms.Application(data=request.POST)
... some logic here which I am not sure of ...
return render(request, 'view1.html', {})
def view2(request):
form = forms.Application()
if request.method == 'POST':
form = forms.Application(data=request.POST)
...
return render(request, 'view2.html', {})
def view3(request):
form = forms.Application()
if request.method == 'POST':
form = forms.Application(data=request.POST)
...
return render(request, 'view3.html', {})
Note that I'm happy to edit my forms or models to achieve this multi-page, save progress effect that you may see on job sites.
Let me know if there's any more code I can add that will be useful, since I'm not too sure what else will be required.
Thanks!

I had a similar use case in my application what I did was created
multiple forms out the models and a central view controlling the
progress of the form.
The view has a list of forms it has to propagate through
GET : /form/<index> => form/0
POST : Save data to the form
Initially the form will have no initial data, for index > 0 the
initial data will be the previously saved model object
When user clicks on next increment the URL index counter, Decrease
it for prev, Don't save anything on skip
Here is a gist of how it would look.
https://gist.github.com/bhavaniravi/b784c57ae9d24fce359ae44e2e90b8e3
I don't know if this is the best optimized method of all times but this is what I did. Any suggestions on improvement is most welcomed

You will need a Form for each action you need. With it on hands, you can use a feature from Django 1.7 called Form Wizard (Yes, it is built in), the best way achieving this is using Class-Based Views, that is way more flexible, clean and cohesive than FBV in this case.
https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#
Basically you will define a list of steps and forms, both tied to the same URL. You can use customized templates for each form:
https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#using-a-different-template-for-each-form
[EDITED]
As jayt said in comments, the formtools was deprecated since version 1.8 and is now apart from core package and can be found in https://github.com/django/django-formtools/
Good luck. =)

I'm working on a Django project and to make the forms experience far
smoother I want to spread a ModelForm across a few pages. It would be
ideal if users who are logged in can save their progress in the form
without actually posting the content (in this case, a JobApplication
where users can come back to filling in info without actually sending
off the application).
You are mixing UI/UX concepts with technical implementation here.
Your overall process is a job application where you have these states:
create a new applicant (user) if not yet done
create a new application for that user
work on that application until the user says they are done (allowed to span multiple (browser) sessions)
mark application as done -> actually apply to the job
How the data for the application is collected (in whatever forms or such) and "submitted" in a web development sense is independent of the actual job application action - it just need to happen beforehand.
So rather than just use one single Django form, you have these possibilities:
(A) Create smaller models that represent a certain content section of the form and that should get their own form. Create ModelForms for those and make them accessible in their own views.
(B) Stick with the single model as you have now but create custom Django forms for each of the separate pages as you have planned. You can still use ModelForm and model=JobApplication but each form has to specify the set of fields it covers and validate only these fields.
Progress Tracking
(A) or (B): You could track how much information has been input (in percent, e.g. by simply counting all fields and all non-empty fields and calculating their percentage.
(A): With different django models you could add a last modified timestamp to each model and show if and when this section was edited.
Main Page
In an overview page you could collect all the data for the user so that they can see what the application will look like (to someone else maybe even) - and there they can also click a button "apply to the job!", or they can open each specific form if they see that any of the data is not yet complete.
Actual Job Application
Once the user clicks on "apply to the job" a different POST happens that for example sets a datetime field on the JobApplication model. This way you can identify all applications that have reached this process step.
Same with any other steps that happen after that. Maybe the user can create another application by copying an existing one. Or their accepted status is also entered in that system and they can log in and check that. Or the system sends out email notifications or similar. Just add fields for anything that is of interest, then you can also reflect that in the UI if you want to.

Related

How to make my forms work in Django?

I just created a Bootstrap 3 website with Django. My website has 4 different pop-up modals in the following order: 1. Uploading your display image, 2. adding basic information, 3. choosing login credentials and 4. adding their personal biography.
These 4 modals all contain their own html form, so I have 4 different forms for the user to register their account. My question is how do I link all 4 forms together so I can pass the information to my Django server and generate the user in my database?
I've seen videos of people generating their forms through a Forms.py file, but I can't do it that way because I already have my forms. If I did not spend my time designing the frond end I would just delete the forms and generate them again through a Forms.py file, but I did.
Anyone who can help me out? I'm quite new to Django.
You misunderstand the concept of forms in django.
They're designed and created to validate user input before saving to database, and generating html from form classes is just additional feature, you mustn't use it.
To make your bootstrapped forms work, you must create django form with all the fields(all fields from your 4 forms), and then:
`............................
def my_view(request):
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
form.save()
.....................`
And override form.save with user-creating functionality.

Django admin - store small pieces of data

I am quite new to django.
I have one problem to solve.
My website has couple of places where i would like to put some content (static text) managed by admin panel.
I know that there is django flatpage or some project like django-chunk. Flatpage has only couple of fields and django-chunk has all in one place (key-value).
I would like to add to panel admin new position only with edit view where user can fill couple of text fields. After save data is storied in file (or in database).
Generally i would like to store small pieces of text like phone number, address, some tagline which are on several pages. And creating model with full CRUD is unnecessary.
How can i do this? Is it possible?
You mention that creating a model is unnecessary, but not why. I think it is the right way to go.
class Snippet(models.Model):
name = models.CharField(max_length=24)
body = models.TextField()
Register it with the admin, and create as many snippets as you need. You could disable add and delete permissions if you only want your users to be able to edit the ones you've specified.
Add them to the template context:
def get_context_data(self, request, *args, **kwargs):
data = super(MyView, self).get_context_data(request, *args, **kwargs)
data['snippet1'] = Snippet.objects.get(name='snippet1').body
data['snippet2'] = Snippet.objects.get(name='snippet2').body
return data
Then print them in the template:
...
Contact us at {{ snippet1 }}!
...
This is a bit simplified. You may want a view mixin that adds all the snippets you need based on a declarative list of names, you probably want to optimize the queries so you don't loop through and hit the database 10 times per request, but this is a good start to a simple solution.

Difference between Model and Form validation

I'm currently working on a model that has been already built and i need to add some validation managment. (accessing to two fields and checking data, nothing too dramatic)
I was wondering about the exact difference between models and forms at a validation point of view and if i would be able to just make a clean method raising errors as in a formview in a model view ?
for extra knowledge, why are thoses two things separated ?
And finnaly, what would you do ? There are already some methods written for the model and i don't know yet if i would rewrite it to morph it into a form and simply add the clean() method + i don't exactly know how they work.
Oh, and everything is in the admin interface, havn't yet worked a lot on it since i started django not so long ago.
Thanks in advance,
You should use model (field) validation to make sure the returning datatype meets your database's requirements. Usually you won't need this because django's builtin fields do this for you, so unless you've built some custom field or know what you are doing you shouldn't change things.
Form validation is where you clean the user's input, you can add a clean method for every form field by adding a clean_FIELD(self) method, e.g.
class ContactForm(forms.Form):
# Everything as before.
...
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "fred#example.com" not in data:
raise forms.ValidationError("You have forgotten about Fred!")
# Always return the cleaned data, whether you have changed it or
# not.
return data
Before a Form's main clean method is ran, it checks for a field level clean for each of its fields
Generally models represent business entities which may be stored in some persistent storage (usually relational DB). Forms are used to render HTML forms which may retreive data from users.
Django supports creating forms on the basis of models (using ModelForm class). Forms may be used to fetch data which should be saved in persistent storage, but that's not only the case - one may use forms just to get data to be searched in persistent storage or passed to external service, feed some application counters, test web browser engines, render some text on the basis of data entered by user (e.g. "Hello USERNAME"), login user etc.
Calling save() on model instance should guarantee that data will be saved in persistent storage if and only data is valid - that will provide consistent mechanism of validation of data before saving to persistent storage, regardless whether business entity is to be saved after user clicks "Save me" button on web page or in django interactive shell user will execute save() method of model instance.

Django: How to carry model form data from one page to another, and back, without commiting to the DB?

[Preamble: Whereas I realize there may be simpler ways to do this (i.e., just use Django built-in Admin, or use inlines to do all editing on one page, etc.), unfortunately, I am not in control of the designs, so I'm seeking help on how to work with what I've been tasked with implementing.]
I have 2 models, Deck and Slide. Slide has foreign key to Deck. (there is also an intermediate model--its a m2m relationship--but to simplify an already complex discussion, I'm going to pretend its a simpler many-to-one relationship.)
The interface i am implementing displays a page with a form to enter or edit Deck fields which also includes an embedded list of Slides in the Deck, with some fields (which I'm in the process of making text inputs) and also with an "edit" and a "remove" anchor link for each Slide. (see img) If you click an "edit" link, it takes you to a new page with a detailed form to input all the information representing the content for the corresponding slide. If you click submit in that form, it takes you back to the page for the deck.
As the title question proposes, I obviously don't want to commit any Deck or Slides to the DB until a user clicks Submit for the entire Deck, i.e. they can add or edit many slides in the interim and may decide to cancel the whole process.
What is the best, cleanest way of doing this?
I've looked at Django's FormWizard class ( http://docs.djangoproject.com/en/dev/ref/contrib/formtools/form-wizard/ ), but seems to be geared towards a multi-step linear flow, not my situation.
I already had to implement this for a demo, and I've gotten most of the way there by creating an inline FormSet for the Slides and a Form for the Deck, and then writing subclasses for my Forms that hide the form and the formset as I pass back and forth between the two pages. Here's some code demonstrating how I use a hidden form for the deck in the slide page:
class DeckForm(ModelForm):
class Meta:
#stuff here
class HiddenDeckForm(DeckForm):
def __init__(self, *args, **kwargs):
super(DeckHiddenForm, self).__init__(*args, **kwargs)
for name, field in self.fields.iteritems():
field.widget = field.hidden_widget()
field.required = False
So in my views, every time I go to the slide form page, I generate a HiddenDeckForm from the POST data and pass it in, and then in my view going back to the deck page, I regenerate the DeckForm (not hidden subclass) from the POST data. Not going to post all my Slide form code, since I'm basically asking whether there is a better way of doing it, but similarly, I have a HiddenSlideForm class, and pass a formset of those between my templates to save the state.
Then when the user clicks submit on the Deck page, the Deck form and Slides formset are all saved to the DB.
It works, but is this a good way to do it? Its been a good bit of code, and I've really had to dig into some Django internals--feel's like I'm using things in a way they weren't designed to be used. Or is there already a more standard way of handling this scenario?
I'll post more code if its helpful.
Thanks for the help!
PS As you can see, I'm implementing it in a jquery colorbox and using ajax for the transitions between the forms, but I am just using normal template/form rendering in my views and passing the rendered html back to the page with an ajax call . . . guess I could do something with json, but strongly prefer to stick with passing a rendered template, as such an implementation could be used without ajax or javascript if desired.
Idea A: Store the uncommitted data in the session until everything is ready for commit. Each form/roundtrip to the client just adds/updates the accumulating data. If they abandon it you only have crap lying around until the session is destroyed. You can save pretty much anything to the session.
Idea B: Add a boolean to the database table which indicates when a row has been committed (eg: "is_active" or "is_pending"). Means you never lose anything but a bit more of a nuisance to manage.
Having done the very similar requirement by working around the formset tools, I completely agree that going hoops and bounds to understand/customize the formset is totally not worth it.
For a screenshot like yours, I'd use just one form with all of the slides per deck. - Form, not formset.
You should handle the slide "edit/delete/new" all in ajax requests that include the deck you create when someone creates a "new deck". And then in the "Deck Form", you only change the Deck properties like name and association.
Or, if you are inlined to do all new elements in the page itself without Ajax and creating new "Slide" objects, you can use the formset and save the deck and all associated slides.

Django Save Incomplete Progress on Form

I have a django webapp with multiple users logging in and fill in a form.
Some users may start filling in a form and lack some required data (e.g., a grant #) needed to validate the form (and before we can start working on it). I want them to be able to fill out the form and have an option to save the partial info (so another day they can log back in and complete it) or submit the full info undergoing validation.
Currently I'm using ModelForm for all the forms I use, and the Model has constraints to ensure valid data (e.g., the grant # has to be unique). However, I want them to be able to save this intermediary data without undergoing any validation.
The solution I've thought of seems rather inelegant and un-django-ey: create a "Save Partial Form" button that saves the POST dictionary converts it to a shelf file and create a "SavedPartialForm" model connecting the user to partial forms saved in the shelf. Does this seem sensible? Is there a better way to save the POST dict directly into the db? Or is an add-on module that does this partial-save of a form (which seems to be a fairly common activity with webforms)?
My biggest concern with my method is I want to eventually be able to do this form-autosave automatically (say every 10 minutes) in some ajax/jquery method without actually pressing a button and sending the POST request (e.g., so the user isn't redirected off the page when autosave is triggered). I'm not that familiar with jquery and am wondering if it would be possible to do this.
before Saving:
for field in form.fields:
form.fields[field].required = False
then:
form.save()
The issue is that you have multiple Forms.
Partial. Incomplete. Complete. Ready for this. Ready for that.
Indeed, you have a Form-per-stage of a workflow.
Nothing wrong with this at all.
Figure out where in the workflow you are.
Populate and present the form for the next stage.
Forms can inherit from each other to save repeating validation methods.
Place the following into your form __init__
for field in form.fields:
form.fields[field].required = False
For example:
class MySexyForm(Form):
def __init__(self, *args, **kwargs):
super(MySexyForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = False
Then call:
form = MySexyForm(...)
form.save()
However you'll need to make sure your clean() method can handle any missing attributes by conditionally checking if they exist in cleaned_data. For example, if another form field validation relies on customer_id but your partial form have not specified one, then customer_id would not be in cleaned_data.
If this is for a model form, you could check if the value was in cleaned_data, and fallback onto instance.field if it was missing, for example;
def clean(self):
inst = self.instance
customer_id_new = self.cleaned_data.get('customer_id', None)
customer_id_old = getattr(self.instance, 'customer_id') if inst else None
customer_id = customer_id_new if customer_id_new else customer_id_old
Remember that the value new value will almost certainly not be in the same format as the old value, for example customer_id could actually be a RelatedField on the model instance but an pk int on the form data. Again, you'll need to handle these type differences within your clean.
This is one area where Django Forms really are lacking sadly.

Categories