I've been racking my brain over this problem for the past few days and I've read numerous other questions regarding the same error but they all seem to be different cases (not including management form, forgetting to update TOTAL_FORMS, etc etc) and do not resolve my problem. I have a page which could contain multiple formsets in a single HTML form. When I am posting the data back to the server, it fails on the is_valid() check for the formsets with the error in the title. I am new to web development and Django so please forgive me if I made a silly mistake or am taking an approach that will not work.
def purchase(request):
return generic_form_view(request, "inventory_tracking/add_purchases.html",
"Successfully added purchases for %s.",
PurchaseForm,
[formset_factory(PurchaseForm.LiquorForm),
formset_factory(PurchaseForm.NonLiquorForm)])
def generic_form_view(request, template, success_message, ParentForm, FormSets):
if request.method == 'POST':
request_params = copy(request.POST)
parent_form = ParentForm(request_params)
formsets = list(map(lambda form_set: form_set(request_params), FormSets))
if parent_form.is_valid(): # This works.
for formset in formsets:
if formset.is_valid(): # Fails here.
Here is a snippet from my template:
<form action="{% block form_action %}{% endblock %}" method="post">
{% csrf_token %}
<div class="row">
<div class="row">
<div class=" well well-lg">
<div class="row">
{{ parent_form.management_form }}
{% for field in parent_form %}
<div class="col-lg-6">
<div class="form-group">
<label class="control-label">{{ field.label }}</label>
{{ field }}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="row">
{% for formset in formsets %}
{{ formset.management_form }}
<div class="row">
<div class="well well-lg">
{% for form in formset %}
<div id="{{ form.prefix }}" class="row">
...
I've been trying to debug this and I noticed something a little interesting but since I am not too familiar with Django it could be a red herring. In the POST, I see the management_form data for the formsets I am creating but I do not see the management_form data for the parent formset (in this case PurchaseForm). However the parent_form is passing validation and the other formsets are not.
I expected this to be a silly problem and I turned about to be right! When my generic_form_view method creates the formsets on the GET request I was adding a prefix like the documentation mentioned but I was not adding a prefix when creating the formsets on the POST.
Related
I'm building a simple flask app. It accepts numbers from a form, calculates a value from these and then gives it back to the user. I am struggling with an error which keeps popping up saying that 'The method is not allowed for the requested URL.' I (think) ive correctly included the get and post methods for this so im not sure why im getting this error. The app has a wsgi.py type set up. Code is below:
In routes.py:
#app.route('/extraplace/advancedcalculator', methods=['GET', 'POST'])
def calculator():
form = Extraplacecalculatorform()
chanceep = 1.555
investmentreturn= 1.555
if request.method == 'POST':
back_odds = form.back_odds.data
ran = form.Number_running.data
chanceep = calculate_chanceep(back_odds, ran)
investmentreturn = (chanceep * form.back_odds.data)
return render_template('completedcalculator.jinja2', chanceep=chanceep, form=form, investmentreturn=investmentreturn)
In completedcalculator.jinja2:
{% block body %}
<h1>Input your information</h1>
<div class="formwrapper">
<h2 class="title">Contact</h2>
<form method="POST" action="/">
<div class="form-back_odds">{{ form.back_odds.label }} {{ form.back_odds(size=20) }}</div>
<div class="form-lay_odds">{{ form.lay_odds.label }} {{ form.lay_odds }}</div>
<div class="form-extraplace_payout">{{ form.extraplace_payout.label }} {{ form.extraplace_payout }}</div>
<div class="form-Number_running">{{ form.Number_running.label }} {{ form.Number_running }}</div>
<div class="submit-button"> {{ form.submit }}</div>
</form>
</div>
<br>
<br>
{% if chanceep == 1.555 %}
<div>
<a class="btn btn-success" role="button">From analysis of over 10,000 horse races in the past 10 years. Our model predicts a {{chanceep}}% chance that the horse will finish in the extraplace meaning that you will have a {{investmentreturn}}% return of investment»</a>
</div>
{% endif %}
{% endblock %}
The idea behind the code is that by declaring the values of chanceep every time the page is accessed, the value of chanceep could be 1.555 if it is a get request, or the new calculated value if it is a post request. Then in the Jinja file, it will only show the bottom button with details of the calculations if the value is not 1.555 (it is currently only showing if it is 1.555 however for simplicity).
I am able to access the page for the first time however I get the error as soon as I try to post. If anyone has any ideas why this might be I would really appreciate it.
ANSWER:
I solved this issue by changing the code within the formwrapper from:
<div class="formwrapper">
<h2 class="title">Contact</h2>
<form method="POST" action="/">
<div class="form-back_odds">{{ form.back_odds.label }} {{ form.back_odds(size=20) }}</div>
to:
<div class="formwrapper">
<form method="POST" enctype="multipart/form-data">
{{ form.csrf_token }}
<div class="form-back_odds">{{ form.back_odds.label }} {{ form.back_odds(size=20) }}</div>
I have a create and an update
notes views.
Each view has the same form to create/update a note.
I was wondering on a good way to render the value of my content only in the update view but not in the create view.
Here what I have today :
<div class="field">
{{ form.title.label }}
{% if request.endpoint == 'notes.update' %}
<div class="control">
{{ form.title(class="input", value=note.title) }}
</div>
{% else %}
<div class="control">
{{ form.title(class="input"}}
</div>
{% endif %}
</div>
I use a condition on the endpoint to do what I want but I don't think it's a good way to do this.
Thank you for help 🙏
Populate the form in your endpoint.
form = NotesForm(request.form)
form.populate_obj(note)
There is a solution for dictionaries, too.
form = NotesForm(request.form, data={'title':'Untitled Note'})
I am having trouble submitting a modelformset in my view. For some reason, the formset is not meeting is_valid criteria, whatever I set the actual data to.
Here is the relevant part of the view:
def admin_tools(request):
ElectionFormSet = modelformset_factory(Election, exclude=('Complete',), formset=BaseElectionFormSet, extra=0)
if request.method == 'POST':
if 'edit_elections' in request.POST:
election_formset = ElectionFormSet(request.POST)
# print(formset)
if election_formset.is_valid():
election_formset.save()
messages.add_message(request, messages.SUCCESS, 'Election settings saved')
return redirect(reverse('elections:home'))
else:
messages.add_message(request, messages.ERROR, 'Problem')
return redirect(reverse('elections:home'))
else:
election_formset = ElectionFormSet()
return render(request, 'admin/admin_tools.html',{
'formset': election_formset,
})
Every time I submit the formset I get the problem message indicating the formset is not valid and I don't know why.
Here is form tag in the template:
<form method="post" action="">
{{ formset.management_form }}
{% for form in formset %}
{% csrf_token %}
<div class='card'>
<div class='card-body w-75 mx-auto'>
<div class='row'>
<div class='col-6 text-center'>
<p>Name<br>{{form.Name}}</p>
</div>
<div class='col-6 text-center'>
<p>Videos<br>{{form.FlipGrid}}</p>
</div>
</div>
<div class='row'>
<div class='col-12 text-center'>
<p>Description<br>{{form.Description}}</p>
</div>
</div>
<div class='row'>
<div class='col-6 text-center'>
<p>Allow registration: {{form.CandidateReg}}</p>
</div>
<div class='col-6 text-center'>
<p>Allow voting: {{form.VotingOpen}}</p>
</div>
</div><p>{{form.id}}</p>
</div>
</div>
{% endfor %}
<div class='text-center'>
<br><button type="submit" class='btn btn-outline-dark' name='edit_elections'>Save</button>
</div>
</form>
I'm not sure what the error would be here. My thought is that maybe each form should be submitted individually and not as a whole formset? I'm also not sure my csrf token is in the right place.
How do I get my formset to submit correctly?
When the formset is invalid, you typically don't redirect. That allows you to re-display the formset along with errors.
In your case, you are rendering the formset manually, so you are not displaying any errors. You could start with the simplest possible template.
<form method="post" action="">
<table>
{{ formset }}
</table>
</form>
Once you're happy the view works, you can customize the template more. See the docs on using formsets in templates for more info.
If you really want to redirect when the formset is invalid, you can check check formset.errors in your view to debug the problem.
I am having trouble with pointing specific comments to specific objects in my Django website. On the HTML page below, 5 posts along with their respective attributes are displayed. Each post has it's own comment_form where users can leave comments. Yet, when a comment is left, ALL of the posts share the same comments, instead of their own unique set of comments. Does any one have a recommended way for me to give each post their own distinct set of comments so that they all aren't sharing the same ones?
Here is my current HTML:
<div class="mainContent">
<div class="content">
{% for post in posts %}
<article class="topContent">
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.body }}</p>
<h6><i>published {{ post.pub_date }}</i></h6>
<div class="commentForm">
{% for comment in comments %}
<p id="commentP">{{ comment.comment }} - {{ comment.name }}</p>
{% endfor %}
</div>
<form method="POST" action="">
{% csrf_token %}
<div id='comment_form'>
<div id="namePrompt">
<p> Name:</p>
</div>
<div id="formName">
{{form.name}}
</div>
<div id="commentPrompt">
<p> Comment: </p>
<div id="formComment">
{{form.comment}}
</div>
<div id="submitBtn">
<input type='submit' name='submit' value='Add Comment'>
</div>
</div>
</form>
</div>
</article>
{% endfor %}
</div>
</div>
here is the view:
def projects(request):
print request
posts = ProjectsPost.objects.all()
comment_from_db = ProjectComment.objects.all()
form = ProjectCommentForm(request.POST or None)
if form.is_valid():
new_form = form.save(commit=False)
name = form.cleaned_data['name']
comment = form.cleaned_data['comment']
context_comment = ProjectComment.objects.get_or_create(name=name, comment=comment)
print context_comment
template = "blog/projects.html"
context = {"form":form}
project_posts = {'posts':posts}
return render(request, template, {'form':form, 'posts':posts, 'comments':comment_from_db})`
I'm assuming that your models look like these:
class ProjectPosts(models.Model):
....
class ProjectComments(models.Model):
....
post = models.ForeignKey(ProjectPosts)
Then in your template, you need to do this:
<div class="commentForm">
{% for comment in post.projectcomments_set.all %}
<p id="commentP">{{ comment.comment }} - {{ comment.name }}</p>
{% endfor %}
</div>
Yeah. You can iterate over your reverse FK relations like that in templates.
EDIT: I'm seeing now that you're taking all the comments in your view, which is not for a specific post too. You don't need to do that. Whenever you need to show comments for a specific post, do in your template what I showed above.
If there isn't an association between a post and it's related comment then each time you query the db you are getting all the comments and passing it on to the template.
As you mentioned in your own comment, adding a foreignkey on the Comment model should work. So when you query the comments for a particular article then you should be able to query only the comments related to that article.
So in essence something like this:
class ProjectPosts(models.Model):
....
class ProjectComments(models.Model):
....
post = models.ForeignKey(ProjectPosts)
And when retrieving all the comments related to a post you can do something like this:
post_comment = PorjectPosts.projectcomments_set.filter(id=foo)
You can read up more on realationships in the Django ORM here.
I am quite new to Django as such, and have been playing around with ModelForms. So far, I have been able to create ModelForms with ease. However, one problem seems to bug me a bit:
When a user fills the form, and if there is an error (say int instead of char or a missing blank=False value), the form spits out the error and seems to forget the values the user entered when the form failed to validate. I am wondering if there is a way to remember these values so that the user does not have to enter them again.
At the moment, I have something like the following:
class ContactForm(CreateView):
form_class = ContactForm
template_name = "superform.html"
success_url = '/ty/'
def form_valid(self, form):
# do something useful with validated form
return super(ContactForm, self).form_valid(form)
def form_invalid(self, form):
# do something useful with invalidated form.
return super(ContactForm,self).form_invalid(form)
I am assuming I need to do something in form_invalid to pass the values back to the form - but I am unsure how to do this(?).
I would really appreciate if someone could point me in the right direction.
Thanks.
----Edit---
<form class="form" role="form" action="{% url 'coolurl' %}" method="post">{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert">
{% for error in form.non_field_errors %}
<span class="label">Error: {{ error|escape }}</span>
{% endfor %}
</div>
{% endif %}
<div class="form-group">
{% if form.name.errors %}
<div class="alert">
{% for error in form.name.errors %}
<span class="label">Error: {{ error|escape }}</span>
{% endfor %}
</div>
{% endif %}
<label class="col-sm-3" for="id_name">Name:</label>
<div class="col-sm-6">
<input class="form-control"
id="id_name"
type="text"
name="name"
maxlength="128"
placeholder="Your name..">
</div>
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
Try to mention the value of the input tag:
<input class="form-control"
id="id_name"
type="text"
name="name"
maxlength="128"
value="{{ form.name.value|default_if_none:'' }}"
placeholder="Your name..">
</div>
You've taken the responsibility for rendering those fields away from Django by simply hard-coding them in the HTML. That means that Django has no way of inserting the current values; not only when redisplaying after errors, but also if you have a form that modifies existing database content.
Don't do it like that. I understand that you don't want to just juse form.as_p, but there is a good middle ground: output each field in the template with {{ form.my_field }}. You can add relevant classes to the fields in the definition in forms.py, and then Django will take care of outputting it correctly.