This is almost certainly a newbie error but I can't work out what's wrong. I've read through the other similar questions on here but non seem to be quite the same.
I have a simple form which posts data but I always get a completely blank response.
Here's the code:
url.py
urlpatterns = patterns('shotgun.views',
# shotgun App URL patterns
url(r'^$', ControlPanelView.as_view(), name='index'),
)
form.py
class ProjectAssignmentForm(forms.Form):
projects = MultipleChoiceField(widget=forms.CheckboxSelectMultiple)
def __init__(self, user_projs, *args, **kwargs):
super(ProjectAssignmentForm, self).__init__(*args, **kwargs)
self.fields["projects"].choices = tuple([(item.name, item.name) for item in SGProject.objects.all() if item.name != "Template Project"])
self.fields["projects"].initial = user_projs
view.py
class ControlPanelView(SLBaseView):
template_name = "shotgun/index.html"
def get_context_data(self, **kwargs):
context = super(ControlPanelView, self).get_context_data(**kwargs)
sg_user = SGCustomNonProjectEntity01.objects.filter(code=self.request.user.get_full_name())[0]
projs = SGProject.objects.filter(Q(entities__entity_id=sg_user.id), Q(entities__type="CustomNonProjectEntity01"))
context["assigned_projs"] = projs
if self.request.method == "POST":
form = ProjectAssignmentForm(projs, self.request.POST)
if form.is_valid():
# update user projects logic
else:
form = ProjectAssignmentForm(projs)
context["pas_form"] = form
return context
template.html
<div class="full_width" >
<h4 class="no_pad" >{{ user.first_name|upper }}'S CONTROL PANEL</h4>
</div>
<div class="full_width top_tab">
<div class="border border_pad">
<h6>PROJECTS YOU ARE ASSIGNED TO</h6>
{% if assigned_projs %}
<form action="/intranet/shotgun/" method="post">{% csrf_token %}
<div class="border_pad">
{{ pas_form.projects.errors }}
{{ pas_form.projects }}
<input type="submit" value="Update" />
</div>
</form>
{% else %}
<p>You are not assigned to any projects.</p>
{% endif %}
</div>
</div>
I have the csrf middleware enabled in my settings.py
The behaviour I would expect from all this would be:
Unbound form - display the page with pre-populated data
Bound invalid - display the page with selected data
Bound valid - display the page with updated data
The unbound data page displays as expected but as soon as the post request is created by clicking the Update button on the form the a blank page is displayed. I tried throwing an exception after the if self.request.method == "POST" statement but the code doesn't seem to get this far. I'm really not sure what's going on.
There is a syntax error in your views.py file.
if self.request.method == "POST":
form = ProjectAssignmentForm(projs, self.request.POST)
should be
if self.request.method == "POST":
form = ProjectAssignmentForm(projs, self.request.POST)
Do you have debugging turned off? That would explain why you're not getting any output. Because there is a fatal error django would print an error report, but if debugging is off it will email the error report to the admin listed in settings.py instead.
Check that in settings.py:
DEBUG = True
Related
First post, apologies in advance for errors.
My problem:
I initialize my model fields from a proprietary db. These fields are displayed correctly in my template upon first display (I can see the html reflects the values in my proprietary db). I then change the initial selection options (for the MultiSelectField) in my template, and save these changed options to the Django db (verified its saved via the admin function) via post overloading in my views. My success_url is setup in urls.py to redisplay the same view. I have overloaded get_context_data in views to load the form data from the Django database, and to update context data with this loaded data, the idea being that when the templates redisplays (when selecting the submit_button), my html form should now displays the from data loaded from the Django db (not the initial values). What's happening, though, is that the initial values seems to be displayed, as opposed to the changed values I have loaded from the Django db. I would really appreciate help wrt understanding why the template displays values other than what I have loaded from the Django db. The form value that I update in my context before returning it in views.py is not what is displayed in the template.
my models.py:
class LiquidAssetModel(models.Model):
#Get my initial values from an external db
unq_assets = Jnance_base_data_main.get_unq_assets()
init_values = Jnance_base_data_main.get_liq_indexes()
liq_dates = Jnance_base_data_main.get_dates()
#initialize my fields
liquid_choices = MultiSelectField(choices = unq_assets, default = init_values)
from_date = models.CharField(max_length=200, choices=liq_dates, default=liq_dates[0][0])
to_date = models.CharField(max_length=200, choices=liq_dates, default=liq_dates[0][0])
def __str__(self):
return self.from_date
My froms.py:
class LiquidAssetsForm(forms.ModelForm):
# specify the name of model to use
class Meta:
model = LiquidAssetModel
fields = "__all__"
My post overload from views.py
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
#form = forms.LiquidAssetsForm(request.POST)
#form.is_valid goes to the LiquidAssetsForm class and clean(self) overload to check whether the submitted data is fine
if form.is_valid():
liquid_indexes = form.cleaned_data.get('liquid_choices')
from_index = form.cleaned_data.get('from_date')
to_index = form.cleaned_data.get('to_date')
#cleare the database
all_forms = LiquidAssetModel.objects.all()
all_forms.delete()
form.save(commit=False)
form.save(commit=True)
#save our liquid asset choices to db
Jnance_base_data_main.put_liq(liquid_indexes)
#make sure the db is set up, copy the excel files, and then load them into the db
Jnance_base_data_main.gen_liq_graph(liquid_indexes, int(from_index), int(to_index))
return self.form_valid(form, **kwargs)
else:
return self.form_invalid(form, **kwargs)
my get_context_data overload from views.py:
class LiquidView(FormView):
template_name = 'liquid_chart.html'
form_class = forms.LiquidAssetsForm
success_url ="/thanksLiquid/"
liq_file_name = ''
def get_context_data(self, **kwargs):
context = super(LiquidView, self).get_context_data(**kwargs)
#build the string to search for a date_NAV file
search_str = settings.STATIC_ROOT + '/Jnance_Mainapp/images/date_liq*'
search_str = search_str.replace('\\', '/')
#search for a file
the_files = glob.glob(search_str)
#split the string and only use the part after 'static/', since we will use the static template tag in the html
if the_files:
#just use the very first file we found in the list
self.liq_file_name = the_files[0].split('static/')[1]
self.liq_file_name = self.liq_file_name.replace('\\', '/')
else:
print("Jnance_Mainapp: views LiquidView no liquid chart found to display")
#update our context with the file name
context.update({'liquid_file_name': self.liq_file_name})
form_class = self.get_form_class()
form = self.get_form(form_class)
the_obj = LiquidAssetModel.objects.all()
if len(the_obj) == 0:
#no forms saved yet
pass
else:
form.liquid_choices = the_obj.values('liquid_choices')[0]
form.from_date = the_obj.values('from_date')[0]
form.to_date = the_obj.values('to_date')[0]
print (form.liquid_choices)
context.update({'form': form})
return context
my urls.py:
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
path('NAV_Charts/', views.NAVView.as_view(), name='NAV_Charts'),
path('Asset_Charts/', views.AssetView.as_view(), name='Asset_Charts'),
path('Liquid_Charts/', views.LiquidView.as_view(), name='Liquid_Charts'),
path('Sum_Charts/', views.SumView.as_view(), name='Sum_Charts'),
path('Base_Load/', views.BaseLoadView.as_view(), name='Base_Load'),
path('admin/', admin.site.urls, name= 'admin'),
path('thanksNAV/', views.NAVView.as_view()),
path('thanksAsset/', views.AssetView.as_view()),
path('thanksLiquid/', views.LiquidView.as_view()),
path('thanksBaseLoadForm/', views.BaseLoadView.as_view()),
path('Jnance_Mainapp/', include('Jnance_Mainapp.urls', namespace='Jnance_Mainapp')),]
my template:
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block body_block %}
<h1 class="text-center display-1" Bootstrap heading (2.5rem = 40px)>Jnance Liquid Assets Chart Generation</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<table class="row">
<div class="row">
<div class="col-sm-6 text-center align-left">
<img class="img-fluid mb-1" src="{% static liquid_file_name %}" alt="Unable to load chart!"/>
</div>
<br>
<div class="col-sm-3 text-left align-left">
{{ form.from_date|as_crispy_field }}
<br>
{{ form.to_date|as_crispy_field }}
<br>
<br>
<input name="submit_button" type="submit" value="Generate" id="update_button"
class="btn btn-success" enctype="multipart/form-data">
</div>
<div class="col-sm-3 text-left align-left">
{{ form.liquid_choices|as_crispy_field }}
</div>
</div>
</table>
</form>
{% endblock %}
Your setting the initial values to whatever the first object is that gets returned in the_obj = LiquidAssetModel.objects.all() when you set them with the_obj.values('liquid_choices')[0]
I have created a form that accepts a file upload and fetches some interesting data from the file upon POST.
However, upon refreshing the page, the form is back to its initial state but the data from the previous file remains. How do I fix it?
Here's my code:
forms.py
choices = (('all', 'all'),
('one', 'one'))
class TicketDetailForm(forms.Form):
file = forms.FileField()
type = forms.ChoiceField(choices=choices)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
views.py
def home(request):
detail = []
if request.method == 'POST':
form = TicketDetailForm(request.POST, request.FILES)
if form.is_valid():
if form.cleaned_data['type'] == 'all':
file = form.cleaned_data['file'].read()
detail.append([str(file, 'utf-8')])
# more workaround with the file
else:
form = TicketDetailForm()
return render(request, 'home.html', {'form': form,
'detail': detail})
home.html
{% extends 'base.html' %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{form.as_p}}</p>
<input type="submit" value="Submit"/>
</form>
{% if detail %}
<div class="row">
<p>The detail is as follows:</p>
{% for d in detail %}
{{ d }}
{% endif %}
</div>
{% endif %}
{% endblock %}
This is because you re-post the form when you refresh the page, since your view just renders the template even if the form is valid. The proper behaviour for a server is to always redirect after successfully submitting a POST. In other words, inside if form.is_valid() you should end by return redirect('home').
Your views should always do the following:
For a GET request, render the template
For a POST request and invalid form, render the template (with the invalid form so the user can fix errors). Hitting refresh is ok here, it will just re-submit the invalid form and therefore won't cause issues.
For a POST request and valid form, redirect, so that the last request is a GET request and the user cannot re-submit when refreshing. This also avoids the valid form to be rendered again.
I'm making a project related to quizzes online and I'm stuck at the moment where I want to pass data from one view to another. My goal is to get a quiz name from the user in the form in one view and pass it to another view, after submitting a form.
I've made 2 views:
add() - which has the first form with the name for the quiz and redirects to add_questions with the name of the quiz
add_questions() - which captures the name of the submitted quiz and displays another form for the questions
views.py
#login_required
def add(request):
if request.method == 'POST':
QuizForm = QuizNameForm(request.POST)
if QuizForm.is_valid():
# create a quiz
new_quiz = QuizForm.save(commit=False)
new_quiz.owner = request.user
new_quiz.save()
request.session['quiz_id'] = new_quiz.id
print("Quiz saved, now I'm redirecting to add_questions")
return redirect(add_questions)
#return HttpResponseRedirect('add_questions') --- didn't work
#return render(request, 'quiz/add_questions.html', {}) --- didn't work
else:
QuizForm = QuizNameForm()
return render(request, 'quiz/add.html', {'quizForm': QuizForm})
#login_required
def add_questions(request):
print("Add questions here! I've captured a quiz named: {}".format(quiz.name))
quiz = DB_questions.objects.get(id=request.session['quiz_id'])
if request.method == 'POST':
print("ok, post method - cool")
#create a question/answer forms
else:
print("Got into else in add_questions view")
Question_form = QuestionForm()
return render(request, 'quiz/add_questions.html', {'quiz_name': quiz.name, 'question_form': Question_form })
My template for adding quiz name - templates/add.html
<div class="container">
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
<!-- Get name of the quiz-->
<form action="{% url 'add' %}" method="post">
{% csrf_token %}
{{ quizForm.name }}
<input class="btn btn-success w-100" type="submit" value="Proceed">
</form>
</div>
<div class="col-md-4"></div>
</div>
</div>
Template to display the name and question form - templates/add_question.html
<div class="row">
<div class="col-md-5"></div>
<h2 class="select_title"> Quiz: {{ quiz_name }}</h2>
<div class="col-md-5"></div>
</div>
<form action="{% url 'add_questions' %}" method="post">
{% csrf_token %}
{{ question_form.content }}
</form>
And finally the urls.py
urlpatterns = [
# ...
url(r'^add', views.add, name='add'),
url(r'^add_questions', views.add_questions, name='add_questions'),
]
The main problem is that after submitting the quiz name the system adds a quiz into database but doesn't redirect me to "add_questions". It still stays in "add" view.
Also I'm not sure if that's the proper way of how I should implement it. I've found many different ways of how I could do it but none of those worked out for me. (Solutions like HttpResponseRedirect(), redirect(), render() etc.)
To sum up:
1) What's the best way, in this case, to transfer data between views after submiting it in the form?
2) How should I redirect to another view so that it would display proper template?
As far as I know, this should work fine for you:
views.py:
#login_required
def add(request):
if request.method == 'POST':
QuizForm = QuizNameForm(request.POST)
if QuizForm.is_valid():
# create a quiz
new_quiz = QuizForm.save(commit=False)
new_quiz.owner = request.user
new_quiz.save()
print("Quiz saved, now I'm redirecting to add_questions")
return render(request, 'quiz/add_questions.html', {'quiz_id': new_quiz.id})
else:
QuizForm = QuizNameForm()
return render(request, 'quiz/add.html', {'quizForm': QuizForm})
#login_required
def add_questions(request):
quiz = DB_questions.objects.get(id=request['quiz_id'])
if request.method == 'POST':
print("ok, post method - cool")
#create a question/answer forms
else:
print("Got into else in add_questions view")
Question_form = QuestionForm()
return render(request, 'quiz/add_questions.html', {'quiz_name': quiz.name, 'question_form': Question_form })
This way you can both render your template and also send the quiz_id through the request object (not session).
I face a problem which I can't find a solution for. I have a button in navbar which is available on all pages and it is a button responsible for creating some content.
View that links with button:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
return HttpResponseRedirect('/', {'username': request.user.username, 'uw': uw})
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
If you can see now I always redirect to main page '/' after creating content but I want to go back to the page with which I launched the creation of content.
You can add a next field to your form, and set it to request.path. After you processed your form you can redirect to the value of this path.
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit">Let's Go</button>
</form>
views.py
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
This is roughly what django.contrib.auth does for the login form if I remember well.
If you pass through an intermediate page, you can pass the 'next' value via the querystring:
some_page.html
Go to my form!
template.html
<form method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="{{ request.GET.next }}">
<button type="submit">Let's Go</button>
</form>
You can use the HTTP_REFERER value:
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
Note that this will not work if the client disabled sending referrer information (for example, using a private/incognito browser Window). In such a case it will redirect to /.
You can use this
return redirect(request.META.get('HTTP_REFERER'))
Make sure to import this
from django.shortcuts import redirect
My favorite way to do that is giving the request.path as GET parameter to the form.
It will pass it when posting until you redirect.
In Class-Based-Views (FormView, UpdateView, DeleteView or CreateView) you can directly use it as success_url.
Somewhere i read that it's bad practise to mix GET and POST but the simplicity of this makes it to an exception for me.
Example urls.py:
urlpatterns = [
path('', HomeView.as_view(), name='home'),
path('user/update/', UserUpdateView.as_view(), name='user_update'),
]
Link to the form inside of the template:
Update User
Class-Based-View:
class UserUpdateView(UpdateView):
...
def get_success_url(self):
return self.request.GET.get('next', reverse('home'))
In your function based view you can use it as follows:
def createadv(request):
uw = getuw(request.user.username)
if request.method =='POST':
form = AdverForm(request.POST, request.FILES)
if form.is_valid():
form.instance.user = request.user
form.save()
next = request.GET.get('next', reverse('home'))
return HttpResponseRedirect(next)
args = {}
args.update(csrf(request))
args['username'] = request.user.username
args['form'] = AdverForm()
args['uw'] = uw
return render_to_response('createadv.html', args)
you could do this easily with a simple one-liner JS
<button onclick="history.back()">Go Back</button>
This will take you back to the previous page of your history list.
If you don't have a history
https://www.w3schools.com/jsref/met_his_back.asp
Use HTTP_REFERER value:
for use in func return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
for use in template Go Back
In case this helps someone I got this to work in class based UpdateView
template
<form class="form" method="POST">
{% csrf_token %}
<!-- hidden form field -->
<input type="hidden" id="previous_page" name="previous_page"
value="/previous/page/url">
<!-- any other form fields -->
{{ form.name|as_crispy_field }}
{{ form.address|as_crispy_field }}
<!-- form submit button -->
<button class="btn btn-primary" type="submit" id="submit">Submit</button>
</form>
<!-- JS to insert previous page url in hidden input field -->
<script>
prev = document.getElementById("previous_page");
prev.value = document.referrer;
</script>
views.py
class ArticleUpdateView(generic.UpdateView):
model = Article
form_class = ArticleForm
template_name = 'repo/article_form.html'
def form_valid(self, form):
form.instance.author = self.request.user
# if form is valid get url of previous page from hidden input field
# and assign to success url
self.success_url = self.request.POST.get('previous_page')
return super().form_valid(form)
The view now redirects you back to the page where you had clicked the "Update/Edit" button. Any URL query parameters are also preserved.
I have a simple Django form being passed through a view to a template where it should display, but, for a reason that I -- after 5 hours -- have failed to deduce, it does not. Any and all ideas welcome, I'm dying to solve this irksome problem.
I have the following Django form:
class BandAddToCartForm(forms.Form):
LENGTH_CHOICES = ( ('XS', 'XS'),
('S', 'S'),
('M', 'M') )
length = forms.Select(choices=LENGTH_CHOICES)
quantity = forms.IntegerField(widget=forms.HiddenInput())
band_sku = forms.CharField(widget=forms.HiddenInput())
# override the default __init__ so we can set the request
def __init__(self, request=None, *args, **kwargs):
self.request = request
super(BandAddToCartForm, self).__init__(*args, **kwargs)
# custom validation to check for cookies
def clean(self):
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError("Cookies must be enabled.")
return self.cleaned_data
It is passed to the template through the following view:
def show_custom_watches(request,
template_name="catalog/custom_watches.html"):
bands = Band.objects.all()
page_title = "Custom Watches"
meta_keywords = "custom, watches, beaded"
meta_description = "Custom beaded watches for every occassion."
return render_to_response(template_name,
locals(),
context_instance=RequestContext(request))
# need to evaluate the HTTP method
if request.method == 'POST':
#add to cart, create bound form
postdata = request.POST.copy()
form = BandAddToCartForm(request, postdata)
#check if posted data is valid
if form.is_valid():
#add to cart and redirect to cart page
cart.add_band_to_cart(request)
# if test cookie worked, get rid of it
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
url = urlresolvers.reverse('show_cart')
return HttpResponseRedirect(url)
else:
# it's a GET, create the unbound form. Note request as a kwarg
band_form = BandAddToCartForm(request=request, label_suffix=':')
# set the test cookie on our first GET request
request.session.set_test_cookie()
return render_to_response("catalog/custom_watches.html",
locals(),
context_instance=RequestContext(request))
Lastly, here is the relevant bit of template where the form is failing to display:
{% for b in bands %}
<div class="watch_list_item">
<img class="" src="{{ MEDIA_URL }}images/bands/thumbnails/{{ b.image }}" alt="{{ b.name }}" />
<div class="watch_form_area">
<p>{{ b.name }}</p>
<form method="post" action="." class="cart">{% csrf_token %}
{{ band_form.as_p }}
<input type="submit" value="Add To Cart" name="add_product" alt="Add To Cart" class="add_to_cart_button" id="add_only_product" />
</form>
</div>
</div>
{% endfor %}
The Add to cart button appears as it should, but the length selector completely fails to display. Any ideas?
The first
return render_to_response(template_name,
locals(),
context_instance=RequestContext(request))
always happens before you initialise the form, remove it and it should work.