9

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!

jayt
  • 758
  • 1
  • 14
  • 32
  • Hi @jayt, have you found the solution to this question? I have a similar issue as well. – Fxs7576 Jun 21 '18 at 08:00
  • @Fxs7576 Apologies for the delayed reply! I had to create a rather 'static' solution to this problem. What I did was store the current form page number as an IntegerField in each UserProfile model - so if you were to complete the first step of the form and press 'Save' then the view would increment this IntegerField to 2 (since you're now allowed to access the second step of the form). You'll also have to make sure that the model that you're creating with the form has null=True set on the fields so that the model can be saved at each step. At the very end of the form you would need to change.. – jayt Jun 23 '18 at 10:29
  • @Fxs7576 ..a field on the model such as 'is_completed' from False to True or whatever - some sort of boolean to indicate that all of the info has been entered. I don't know if I've explained it clear enough for someone to implement so let me know if you have any questions! – jayt Jun 23 '18 at 10:33
  • 1
    You solution made sense, and it solves my issue. Thanks for being a savior. – Fxs7576 Jun 24 '18 at 13:03

3 Answers3

5
  1. 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
    
  2. Initially the form will have no initial data, for index > 0 the initial data will be the previously saved model object

  3. 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

Bhavani Ravi
  • 2,130
  • 3
  • 18
  • 41
  • Really like this idea. Have you got any suggestions on how I may implement the code for this in views? – jayt Mar 27 '18 at 11:47
  • https://gist.github.com/bhavaniravi/b784c57ae9d24fce359ae44e2e90b8e3 here is a sample of how the view would look like. – Bhavani Ravi Mar 27 '18 at 12:00
  • okay thanks - and how does this work across multiple templates may i ask? just a bit confused about the implementation here – jayt Mar 27 '18 at 12:02
  • I had one template and changed the form part alone with the context. If you want separate template for each form you have to maintain one more list I guess. – Bhavani Ravi Mar 27 '18 at 12:07
2

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. =)

Andre Machado
  • 316
  • 2
  • 3
  • hi, thank you for the suggestion but i believe django has deprecated the form wizard - i'm currently using django 2.0. – jayt Mar 20 '18 at 17:39
  • @jayt here https://github.com/django/django-formtools/ - the version 2.1 gives support for django 2.0 – Andre Machado Mar 20 '18 at 21:01
  • i think i would have used the form wizard tools for this except the form wizard does not help with saving a users progress in a multi-step form – jayt Mar 21 '18 at 10:38
2

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:

  1. create a new applicant (user) if not yet done
  2. create a new application for that user
  3. work on that application until the user says they are done (allowed to span multiple (browser) sessions)
  4. 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.

Risadinha
  • 16,058
  • 2
  • 88
  • 91