Django Save Incomplete Progress on Form - python

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.

Related

Django, form.is_valid() What does it check?

I do not use the form of models.form
I received and stored each items of request.POST and request.FILES
I will make my validation function.
So I wonder what validation it does. (ex. input is empty, etc.)
Manually processing request.POST and request.FILES is really a bad idea: the data can be "forged", and the HTML specifications on how the request is constructed with a <form> have some peculiarities. For example if a checkbox <input type="checkbox" name="somename"> is not checked, then it does not appear in the request, whereas if it is checked, it appears with no value in the request.
A first problem is thus that not per se all fields of the form will appear in the request.POST, that is not per se a problem: some fields might not be required. The Django form will thus check for all fields that are required, if the value appears in request.POST and request.FILES.
Next the form will start cleaning the data. This means that it converts the data, which are all strings, to a more suitable datatype. For example for a DateField, it will try to convert this to a date object. It will normally try all formats in the DATE_INPUT_FORMAT setting [Django-doc], but you can customize that when you construct a DateField to thus use a different format for that specific field. Some fields, like a BooleanField will for example look if the field is indeed in the request.POST and then return True and otherwise False, so this is not just a simple mapping of the value in request.POST, since that value might not be there.
Cleaning does not only map strings on corresponding Python objects, it can also do some (small) post-processing on these fields. For example a CharField will by default trim the string, and thus remove leading and trailing spaces, whereas a ModelChoiceField will pick the corresponding model object for that primary key from the database. A form field has also a list of empty values: values that are considered to be empty. For example for a CharField, you can set '/' as an empty value. It will then thus consider the slash as the field being empty.
One can create extra form fields than the ones that are defined, and this thus means that it is easy to construct for example an EANField where you implement the cleaning once, but that would mean that you need to do that for every view. Form fields are thus used to make cleaning more reusable by implementing the cleaning process into the field.
Next in line are validations on the form fields. While a text field in HTML can have a <input type="text" minlength="6" maxlength="24">, you can nevery rely on what the browser will post as values: indeed POST requests can be forged, so while the HTML form might intent to prevent people from picking a username with less than six characters, or more than twenty-four characters, you can not be sure that this will be the case in the POST request. A form field can be passed validators=… [Django-doc]: a set of callables that raise a validation error in case the item is not valid. For example for a CharField, if you pass a value for the min_length=… and/or max_length=… validators, it will thus check these. It will add all ValidationErrors to a list, which is often cumbersome to implement in a view: most views that validate data will check a certain condition, for example the minimum length of the characters for a password, and if that fails, report an error, whereas with a Form, it can also add an extra error that you should use at least one digit. The form will do that for all form fields, and thus construct a dictionary mapping the name of the fields to a list of errors.
If all the form fields successfully return validate the data, then the form will create an attribute .cleaned_data [Django-doc] that maps the names of the fields to the corresponding value that has been cleaned by that form field. One can however further add additional cleaning and validation to the Form.
Then there are validations at the form level itself. If you want to slightly customize the form, then form a field named field_name, you can implement a clean_field_name method. This method can access the .cleaned_data dictionary, and then run additional validation on that field and raise a ValidationError, and furthermore "clean" the function, for example by returing an object of a different type, or doing some post-processing on the data that is then returned. The Form will thus collect these values and update the dictionary with these cleaned values. Furthermore if the method raises a ValidationError, the error will be added to the .errors for that specific field.
After that, the form will call the .clean() method [Django-doc]. This will perform cleaning on the entire form. Indeed, now that all fields are cleaned individually, it might still be possible that there are errors about the combination of the fields. For example whether the two password fields match. Usually the clean method will thus work with multiple fields in the .cleaned_data attribute, and return for example that dictionary, or another object. If you raise a ValidationError here, the error will be put in the .errors under the '__all__' key, since this is not related to a specific field.
For a ModelForm [Django-doc], the validation is not over yet. Indeed, the model itself can still have some validation. It will update the instance wrapped in the form with the values of the cleaned data, and call the .full_clean() method [Django-doc] on the instance, and later also call the .validate_unique(…) method [Django-doc] to validate that for fields that should be unique (have unique=True), these are indeed unique. That last task is often more complicated than it looks at first sight, since if the instance is edited in the form, the check in the database, should exclude the current instance you are editing.
Only after all these validation, the Form or ModelForm will accept the change, and then .is_valid() will return True. If in one of the steps thus fails, the error will be added to the .errors, and thus .errors will try to construct a dictionary that tries to report all the problems with the data. This is useful if you re-render the form, since then the errors can be put next to the corresponding field, and the user thus gets feedback on all the problems with the data, not the first one encountered by the form, since that could result in a lot of attempts to eventually fix the form.
To summarize: there is a lot of logic in place in a Form to validate and clean data. Trying to implement that in a view is a popular mistake. It is cumbersome to do it correctly in a view, but it is also not very flexible: if you want to add an extra validator for a password for example, it will take a lot of work to implement that, especially if you want to that in multiple views. Forms are created to work with reusable components like form fields, validators, uniqness checks, etc. It is also quite robust since it is aimed to validate request data that might be forged, and thus was not validated properly by the HTML attributes.

Django - Form across multiple views with progress saving

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.

Django default data (Fixtures?) in views.py on form.save()?

Wondering what would be the best approach in Django to insert some default DB data every time a new account is created with form.save().
Lets say the user just created a new business profile account and I would like to pre-load data that are customized with the new business ID and today's dates right after the form.save() (so I have the new account IDs available).
Obviously, I can use the hard coded approach and write 10 lines of Model.objects.create(...) and be done with it but I was wondering what would be the "correct" Django/pythonic way of doing this through something like JSON fixture file etc.
If your concern is only creating an id, you can have a model with null allowed fields and auto id field. Other fields can also have default values. default field option also allows callbacks.
ex:-
created_at = models.DateTimeField(default=datetime.now)
Look at:https://docs.djangoproject.com/en/1.9/topics/db/models/#field-options
A Model with all nullable fields or default value fields can essentially be created with a single line of code in form.save(). Assuming you intend to create a single default model object only.
As for the right way, you might want to have simple flags(to record an business profile as active leaving the default one inactive etc.) in your model and right relationships(FK) so that a user cannot repeatedly create default business profiles.
You can use a django post_save signal. Its easy to use.
https://docs.djangoproject.com/en/1.9/ref/signals/#django.db.models.signals.post_save
You can put the code in models.py of that application itself.
#receiver(post_save,sender=ModelName)
def do_some_operation(sender, **kwargs):
if kwargs['created']: # Executes only if created
obj = kwargs['instance']
# do modifications with your obj
obj.save()

Customize (override) Flask-Admin's Submit method from edit view

Preconditions:
I'm new to Python and to Flask-Admin in particular. I created a simple test service, which has MondoDB, keeping the data with relationship of 'one-to-one' kind.
employeeName -> salary
The model looks like that:
class Employee(db.Document):
fullName = db.StringField(max_length=160, unique=True)
salary = db.IntField()
And I use Flask-Admin to observe the table with the data and to edit it.
When I want to change the 'salary' field, I just press the 'edit' button and in Flask-Admin's default edit view I change the integer value. I press 'Submit' and a new value in the database is successfully applied.
Question:
But I need to override the Submit method in the way, that leaves as it is the functionality and adds some custom code. Like let's assume I want to add a comment in the log file after an actual db submit:
logging.warning('The salary of %s: was changed to /%s', fullName, salary)
Any suggestion on how to achieve that would be much appreciated. Perhaps you could direct me in the way to go, since the Flask-Admin documentation doesn't give me enough help so far.
You can override on_model_change method to add your custom logic. Check http://flask-admin.readthedocs.org/en/latest/api/mod_model/#flask.ext.admin.model.BaseModelView.on_model_change
I ended up overriding a save method in my Document-derived class.
So now my Employee class contains this kind of code:
def save(self, *args, **kwargs):
print 'whatever I want to do myself is here'
return super(Employee, self).save(*args, **kwargs)
Today I found that this solution is actually nothing new and is described on StackOverflow.
But for my specific case I think Joes' answer is better. I like it more, because if I override on_model_change I invoke my custom code only if I edit database through Admin webpage; and each programmatic operation over database (like save, update) will work using native code - which is exactly what I want. If I override save method, I will be handling every save operation myself, whether It was initiated by Admin area or programmatically by the server engine.
Solved, thank you!

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.

Categories