Django - how to redirect from to another basic on first forms choices - django

I would like to write simple app in which i'd like to have workflow :
1) user answers form "A"
2) basic on answer of qustion A.a redirect to form "B" or to form "C"
3) save whole instance
All three forms will use the same model (leaving unused fiels as blank).
What is the best way - to save instance partialy and redirect to next forms using for example primary key parameter or push "partialy" answered form to the next form and then save all at once.
How should i redirect ? set success_url of A to a view which, based on POST A.a redirects me the another form ?

Are you sure that you have only one instance?
I would try to refactor it using 3 instances.
If this is not possible, I would store the partial entity in the user session and save at the last step (B or C).
As for the redirect, if you are using the Class Based Views, take a look at form_valid method: it receives the (already validated) form and you just need to save it and redirect.
This is an awesome reference to Class Based Views.

Related

Django function execution

In views, I have a function defined which is executed when the user submits the form online. After the form submission there are some database transactions that I perform and then based on the existing data in the database API's are triggered:
triggerapi():
execute API to send Email to the user and the administrator about
the submitted form
def databasetransactions():
check the data in the submitted form with the data in DB
if the last data submitted by the user is before 10 mins or more:
triggerapi()
def formsubmitted(request):
save the user input in variables
Databasetransactions()
save the data from the submitted form in the DB
In the above case, the user clicks on submit button 2 times in less than 5 milliseond duration. So 2 parallel data starts to process and both trigger Email which is not the desired behavior.
Is there a way to avoid this ? So that for a user session, the application should only accept the data once all the older data processing is completed ?
Since we are talking in pseudo-code, one way could be to use a singleton pattern for triggerapi() and return Not Allowed in case it is already istantiated.
There are multiple ways to solve this issue.
One of them would be to create a new session variable
request.session['activetransaction'] = True
This would however require you to pass request, unless it is already passed and we got a changed code portion. You can also add an instance/ class flag for it in the same way and check with it.
Another way, which might work if you need those submissions handled after the previous one, you can always add a while request.session['activetransaction']: and do the handling afterwards.
def formsubmitted(request):
if 'activetransaction' not in request.session or not request.session['activetransaction']:
request.session['activetransaction'] = True
# save the user input in variables
Databasetransactions()
# save the data from the submitted form in the DB
request.session['activetransaction'] = False
...

How to Avoid multi post request by one click?

I have a simple form which adds personal information of a family. sometime it saves two instance of a person just by one submit. Maybe my mouse has problem and and double clicks instead of one click (it has some problems). I think this is not possible and django accepts just one post request from an instance of a form and not more (maybe it accepts). what if may code has problem? if it is problem of my code, why it happens once a while?
house = get_object_or_404(House, id=code)
if request.method == 'POST':
form = ParentForm(request.POST)
if form.is_valid():
# save it if it's valid
parent = form.save(commit=False)
if parent.living == 0:
parent.in_family = 0
if not parent.guardian:
parent.save()
if parent.guardian and parent.in_family:
parent.save()
I use Django 1.8
Edit to clear: this is not the only view sometime saves twice. Maybe it is a bug in django
To solve this problem, first you need to create unique constraint on the corresponding database table. The real solution is based on the database schema. I don't know what fields (columns) you have in the parent table, you can start from add unique constraint to these two fields: child_id and parent_name.
The other problem is you need to prevent the second click. So basically you need to write some JavaScript code: it listens to the onClick event of the submit button. Once the button get clicked, the listener sets the disabled attribute to that button to prevent further clicks.

Django context processors and URL arguments

I have some code that is repeated at the start of my Django views. It basically just adds some variables to the context, but based on the URL argument, e.g.
def someView(request, id):
target = Target.objects.get(id=id)
# name will be added to ctx
name = target.name
(there are more attributes added and other attributes from related models, but this gives the general idea --- There are quite a few lines of repeat code at the start of each view)
I thought I could make my code more DRY by taking advantage of Django's context processors, but it would seem these don't access to the URL arguments?
Is there another way to avoid these repeat lines? Maybe middleware or something else?
You can access the URL parameters via request through the resolver_match attribute. So for instance you can do request.resolver_match.kwargs['id'] to get the ID kwarg.

How to validate against related objects in Django admin?

We have two simple models:
class Master(models.Model):
pass
class Detail(models.Model):
master = models.ForeignKey(Master)
order = models.IntegerField()
I want to validate that the order of all details for a master are a sequence from one up, e.g. three details means 1, 2, 3.
I tried in Master.clean(), but I cant see how to access the details if they have been changed on a master-detail page.
Now I am trying to do it in an admin form for Master, but I have the same problem there, how can I access the modified datail data? Besides that, I would prefer to do the check somewhere in the model, so I don't have to repeat myself for other forms.
A tricky way is getting help from transaction in validation. for example:
#transaction.commit_on_success
def save_form(self, master, details):
# Save or update master instance and details list
if not is_sequence(): # check sequences in details.
raise Exception('invalid')
Then instead of using form's save method, use this method anywhere that is needed, or just override form's save method with this.
Implementation isn't important, I just want to propose idea of using transaction. In this way you save all values in databases so in your is_sequence() you can easily access the details also you validate them before commiting this transaction and if it weren't valid roll-back occurs.

Django: How to access the model id's within an AJAX script?

I was wondering what is the correct approach,
Do I create HiddenInput fields in my ModelForm and from the
View I pass in the primaryKey for the models I am about to edit into
the hiddenInput fields and then grab those hiddenInput fields from
the AJAX script to use it like this?
item.load(
"/bookmark/save/" + hidden_input_field_1,
null,
function () {
$("#save-form").submit(bookmark_save);
}
);
Or is there is some more clever way of doing it and I have no idea?
Thanks
It depends upon how you want to implement.
The basic idea is to edit 1. you need to get the existing instance, 2. Save provided information into this object.
For #1 you can do it multiple ways, like passing ID or any other primary key like attribute in url like http://myserver/edit_object/1 , Or pass ID as hidden input then you have to do it through templates.
For #2, I think you would already know this. Do something like
inst = MyModel.objects.get(id=input_id) # input_id taken as per #1
myform = MyForm(request.POST, instance=inst)
if myform.is_valid():
saved_inst = myform.save()
I just asked in the django IRC room and it says:
since js isn't processed by the django template engine, this is not
possible.
Hence the id or the object passed in from django view can't be accessed within AJAX script.