I want to save a form in session so that a user doesn't have to reapply filters when going to a different url. Here is the error I'm getting:
'unicode' object has no attribute 'get'
This is the code I have:
views.py
def someview(request):
if request.method == 'POST':
form = Form(request.POST, initial=request.session.get('save_form', None))
if form.is_valid():
clean_form = json.dumps(form.cleaned_data, indent=4, sort_keys=True, default=str)
request.session['save_form'] = clean_form
else:
pass
else:
form = Form()
forms.py
class Form(forms.Form):
startdate = forms.DateField(
label='Start date',
initial= '2018-01-01',
widget=forms.TextInput(attrs={'class':'datepicker'})
)
enddate = forms.DateField(
label='End date',
initial= datetime.date.today() - datetime.timedelta(days=1),
widget=forms.TextInput(attrs={'class':'datepicker'})
)
manager = forms.MultipleChoiceField(
required=False,
choices=managers,
widget=forms.SelectMultiple(),
)
Being a beginner in Django, I'm not sure where to go from here. I'm pretty sure the error is in the initial argument. But, printing it, it seems print what I would expect.
TIA
My best guess is that you JSONfify the form values (json.dumps(form.cleaned_data, ...)), but you do not un-JSONify it when reloading from the session.
When reading the session you need to do json.loads(...) at some point:
saved_form = json.loads(request.session.get('save_form', ''))
The default value '' might not be the best, I haven't tried. If that's giving you an error, you might want to try replacing it with {}.
Hope that helps.
EDIT:
Note that loading content from the session should probably be done in the last else branch. You generally don't want to provide an initial value when the user is sending data, like in a POST request:
def someview(request):
if request.method == 'POST':
form = Form(request.POST)
if form.is_valid():
clean_form = json.dumps(form.cleaned_data, default=str)
request.session['save_form'] = clean_form
else:
pass
else:
saved_form = json.loads(request.session.get('save_form', ''))
form = Form(initial=saved_form)
EDIT 2:
Generally, what you want to do in a form view is often very similar, and you might find yourself repeating the same boilerplate. Luckily, Django has something to help with that, and it called class-based views, and I recommend ccbv.co.uk if you're interested to dive into that, for example FormView.
However, they can be a bit confusing and hard to get your head around when you're just starting. Maybe better to stick with function-based views (as you do) for now, but I thought I linked to these 2 resources which I wish I had when I started learning class-based views.
Related
I am using Python 3.5, Django 1.8 and PostgreSql 9.4.
So I have one edit method where in get request I am rendering form and when user submit form it will get updated. Hers's the code
def edit_case(request, case_id):
case = get_object_or_404(Case, pk=case_id)
if request.method == 'POST':
data = request.POST.copy()
form = CaseEditForm(data, instance=case)
if form.is_valid():
res = form.save()
return HttpResponseRedirect(reverse("case_list"))
else:
form = CaseEditForm(instance=case)
variables = RequestContext(request, {
"form": form,
})
return render_to_response(
'sample/edit_case.html',
variables,
)
Now I want to add row level locking on it like if one user is updating something at the same time other will not be able update anything unless previous transaction succeeded Or if someone have any other better suggestions rather then using Pessimistic Locking.
I know about select_for_update but don't have any idea how it will get implemented in case of form.save()
Any help would be really appreciated
After so much research I have figured out the solution. So now while getting queryset against id I am using "select_for_update", something like this
with transaction.atomic():
case = get_object_or_404(Case.objects.select_for_update(), pk=case_id)
form = CaseEditForm(data, instance=case)
if form.is_valid():
res = form.save()
return HttpResponseRedirect(reverse("case_list"))
So as you can see now I am fetching the query set object under the transaction and if any exception appear during transaction it will automatically rollback and Also as I am using select_for_update so it will lock the row until the transaction get succeeded or failed.
If anyone have better suggestion then kindly share it.
I'm having big trouble understanding the whole forms business in django. As I understand it the cleaned form data is a dictionary. So all my defined form fields should be in the dictionary like so: {'definedform': userinput, ...}. Is this correct?
I want to create a form in which a user can input data. This data should then be send to a different view, in which the inputted data is rendered with a latex template (and subsequently rendered into a pdf). This works more or less fine if I define the context in the /create_pdf/ view and grab the user input manually. But I suppose there is a nicer way. What I think should work:
def index(request):
if request.method == "POST":
persoform = PersonalForm(request.POST, prefix='personal')
if persoform.is_valid():
content = persoform.cleaned_data()
content = Context(content)
return HttpResponseRedirect('/create_pdf/')
else:
persoform = PersonalForm()
return render(request, 'app/template.html', {'persoform': persoform})
And in my /create_pdf/ view:
def create_pdf(request):
template = get_template('app/latextemplate.tex')
rendered_tpl = template.render(content)
[...]
So, how can I make sure, to pass the data from my index view to my create_pdf view?
EDIT:
Forgot to mention: The error is "'content' not defined". So I understand that the /create_pdf/ view doesn't get content dictionary, but I have no idea how I would make sure that it does.
Put the data in to the session on submit, and pop it out in the second view.
if form.is_valid():
request.session['perso'] = form.cleaned_data
return HttpResponseRedirect('/create_pdf/')
...
def create_pdf(request):
data = request.session.pop('perso'], {})
Is there a way I can pass data from a form submission over to the 'thank you' page. The reason i'd like to do this is because I have a form on the website, where the user will select multiple fields which all contains different PDF's.
So once the user has submitted the form the idea is to re-direct them to a thankyou page, where they can view the list of pdf/files they have selected on the form.
I hope this is enough info to go on. Here are my views / models.
def document_request(request, *args):
# template = kwargs['name'] + ".html"
if request.method == 'POST':
form = forms.ReportEnquiryForm(request.POST)
print(request.POST)
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
else:
form = forms.ReportEnquiryForm()
return render_to_response('test.html',{'form':form})
def thank_you(request):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
My initial idea was to pass the data to a new view called thank_you. But not this is possible.
class DocumentUpload(models.Model):
name = models.CharField(max_length="200")
document_upload = models.FileField(upload_to="uploads/documents")
def __unicode__(self):
return "%s" % self.name
class DocumentRequest(models.Model):
name = models.CharField(max_length="200")
company = models.CharField(max_length="200")
job_title = models.CharField(max_length="200")
email = models.EmailField(max_length="200")
report = models.ManyToManyField(DocumentUpload)
def __unicode__(self):
return "%s" % self.name
form.py
class ReportEnquiryForm(forms.ModelForm):
class Meta:
model = models.DocumentRequest
fields = ('name', 'company', 'job_title', 'email', 'report')
If you need anymore info, please ask :)
You've saved the user's submission in a DocumentRequest object. So you can pass the ID of that object in the URL when you redirect, and in the thank_you view you can get the DocumentRequest and render the list.
Edit The idea is to make the thank_you page like any other view that accepts a parameter from the URL:
url(r'thanks/(?P<id>\d+)/$, 'thank_you', name='thank_you')
and so the POST part of the form view becomes:
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
and thank_you is:
def thank_you(request, id):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
Second edit
As others have suggested, this makes it possible for anyone to see the request. So a better solution is to put it in the session:
docrequest = form.save()
request.session['docrequest_id'] = docrequest.id
and in thank_you:
def thank_you(request):
if not 'docrequest_id' in request.session:
return HttpResponseForbidden
docrequest = DocumentRequest.objects.get(request.session['docrequest_id'])
You can do as Daniel Roseman said but in this case the thank you pages can be accessed by anyone with the Ids.
Some ways to pass data between views are the following(the list is not mine.):
GET request - First request hits view1->send data to browser -> browser redirects to view2
POST request - (as you suggested) Same flow as above but is suitable when more data is involved
Using django session variables - This is the simplest to implement
Using client-side cookies - Can be used but there is limitations of how much data can be stored.
Maybe using some shared memory at web server level- Tricky but can be done.
Write data into a file & then the next view can read from that file.
If you can have a stand-alone server, then that server can REST API's to invoke views.
Again if a stand-alone server is possible maybe even message queues would work.
Maybe a cache like memcached can act as mediator. But then if one is going this route, its better to use Django sessions as it hides a whole lot of implementation details.
Lastly, as an extension to point 6, instead of files store data in some persistent storage mechanism like mysql.
The simplest way is to use sessions. Just add the id to the session and redirect to the thank you view, you read the id value and query the db with that id.
I'm brand new to django and fairly new to programming in general. I've done the django tutorial and searched the web for an answer to this question, but to no avail, so now I'm here. I am confused how post works with django. All of the tutorials I've looked at how have a return function in views that displays the webpage. I get that. But then how does a user update data if the page is being rendered from that return statement? After the return there can't be any more updates because the function stops, right? What am I missing here? Any help would be greatly appreciated, I'm getting fairly desperate here.
One pattern for Django views (by no means the only pattern) is to check the request method (GET or POST) at the beginning of the view. If it is POST, then handle the incoming data (before the view returns), and then return either a rendered template, or a redirect.
def view_function(request):
if request.method == 'POST':
if data_is_valid(request.POST):
save_data(request.POST)
return HttpResponseRedirect('/somewhere/good')
else:
return render('template', {'errors': what_went_wrong}
else:
return render('template')
The user updates data in the logic of the view function. That is to say, if the user wishes to update something, you place the update logic in the view function before the return. For example, you would do this:
def update(request):
item = <some model>.objects.get(<something>)
<more code>
return <something>
Usually an edit view function contains two parts -- one for updating data, and the other for displaying the update form. For example,
def user_edit(request):
if request.method == 'POST': # is this a save action?
# save the user data
user_id = request.POST.get('user_id')
username = request.POST.get('username')
description = request.POST.get('description')
user = User.objects.get(id=user_id)
user.username = username
user.description = description
user.save()
return HttpResponseRedirect('/user/') # redirect to index
else:
# show the edit form
user_id = request.GET.get('user_id')
user = User.object.get(id=user_id)
return render_to_response('/user/edit.html', { 'user': user })
There are many different choices for the if request.method == 'POST' line. You can also use if request.POST.get('user_id') to check if specified field is set, to determine if this is a save action.
My view calls some backend classes which need some user input. When user input is required, i halt processing and store the questions into the session - request.session['questions']. request.session['questions'] is a list of dictionaries. e.g.
request.session['question'] = []
request.session['question'].append({'question' : 'Whats your firstname', 'answer' : ''})
request.session['question'].append({'question' : 'Whats your firstname', 'answer' : ''})
I need to display these questions to the user along with an input box for each question. When the user submits the form, I need to dump the input into the answers part of the session variable. Could someone show me how to do this? I'm a little lost as this isn't really based on Django forms or models as such.
Thanks
You could use forms that aren't associated with models, like this:
class QuestionForm(forms.Form):
answer = forms.CharField()
def questions(request):
if request.method == 'POST':
form = QuestionForm(request.POST)
if form.is_valid():
# Process the data in form.cleaned_data
return HttpResponseRedirect('/done/')
else:
form = QuestionForm() # An unbound form
return render_to_response('questions.html', {'form': form,})
More documentation here.