I am experiencing the issue that the code saves request.POST['name'] value to assumptions.Name field in database, but only the last value in the list which is 'B' in this case. How could I make it save both prefixes 'A' and 'B' to database field Name based on respective forms. When I debug I see both 'A' and 'B' with key 'name'.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
from django.core.exceptions import ValidationError
model_names = ['A', 'B']
def get_assumptions(request):
AssumptionFormset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if request.method == 'POST':
formsets = [AssumptionFormset(request.POST, prefix=thing) for thing in model_names]
if all([formset.is_valid() for formset in formsets]):
for formset in formsets:
for form in formset:
assumption_data = form.save(commit=False)
assumption_data.Name = request.POST.get('name')
assumption_data.save()
else:
formsets = [AssumptionFormset(prefix=thing) for thing in model_names]
return render(request, 'assumptions.html', {'formsets': formsets})
assumptions.html
<div class="form">
<form action="" method="post">
{% for formset in formsets %}
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<h1>{{formset.prefix}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="hidden" id={{formset.prefix}} name='name', value={{formset.prefix}} />
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = '__all__'
exclude = ['Name']
I figured it out. I should have used getlist method and then loop through values.
values = request.POST.getlist('name')
Cheers.
Related
In my Django app the delete view doesn't work. When I click on an entry the page just refreshes and nothing happens. The entry is not deleted from the database. Creating and updating entries works fine. Is there something wrong with any of the files?
microtarjetas/views.py:
class DeleteCardView(DeleteView):
model = Card
template_name = 'microtarjetas/delete_card.html'
form_class = CardForm
success_url = '/cards/'
templates/microtarjetas/delete_card.html:
{% extends 'base.html' %} {% load static %} {% block content %}
<h2>Delete card</h2>
<p>Are you sure you want to delete "{{ object }}"?</p>
<form method="post">{% csrf_token %}
<input type="submit" value="Confirm" />
</form>
{% endblock %}
a snippet from templates/microtarjetas/cards.html:
<tbody>
{% for card in cards %}
<tr>
<td>{{ card.word_en }}</td>
<td>{{ card.word_es }}</td>
<td>update</td>
<td>X</td>
</tr>
{% endfor %}
</tbody>
forms.py:
from django import forms
from .models import Card
class CardForm(forms.ModelForm):
class Meta:
model = Card
fields = "__all__"
models.py:
class Card(models.Model):
word_en = models.CharField(max_length = 30)
word_es = models.CharField(max_length = 30)
def get_absolute_url(self):
return "/cards"
def __str__(self):
return f"{self.word_en} --- {self.word_es}"
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
path('cards/', views.CardsView.as_view(), name='cards'),
path('cards/new', views.CreateCardView.as_view(), name='create_card'),
path('cards/<pk>/delete/', views.DeleteCardView.as_view(), name='delete_card'),
path('cards/<pk>/update/', views.UpdateCardView.as_view(), name='update_card'),
]
I add a {{ form }} tag to the delete_card.html form, it works (although it adds two input fields to the form which are unnecessary):
delete_card.html
You are not submitting the form with the form data. You should update the code like this:
<form method="post">{% csrf_token %}
{{ form }}
<input type="submit" value="Confirm" />
</form>
More information can be found in the documentation.
I'm a new in Django and trying to do a app but now I'm having this error: "Invalid block tag on line 24: 'form.as_p', expected 'endblock'."
TEMPLATE
{% extends "base.html" %}
{% block title %}
<title>Tarefas</title>
{% endblock title %}
{% block content %}
<div class="content">
{% if messages %}
<div class="container">
<br>
{% for message in messages %}
<div class="alert alert-info" role="alert">
{{message}}
</div>
{% endfor %}
</div>
{% endif %}
<div class="container tasks-box">
<table class="table table-striped">
<form method='POST'>
{% csrf_token %}
{% form.as_p %}
<button type="submit" class="btn btn-secondary btn-sec">Adicionar</button>
</form>
[...]
forms.py
from django.forms import ModelForm
from todolist.models import Tasks
class TaskForm(ModelForm):
class Meta:
model = Tasks
fields = ['task','responsible']
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from todolist.models import Tasks
from todolist.forms import TaskForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
#login_required
def todolist(request):
if request.method == 'POST':
form = TaskForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.manager = request.user
instance.save()
messages.success(request,("Tarefa adicionada"))
return redirect('todolist')
else:
form = TaskForm
all_tasks = Tasks.objects.filter(manager=request.user)
all_users = Tasks.objects.all()
return render(request,'todolist.html',{ 'form':form,
'all_tasks':all_tasks,
'all_users':all_users})
models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth import get_user_model
# Create your models here.
User = get_user_model()
class Tasks(models.Model):
manager = models.ForeignKey(User, on_delete=models.CASCADE,default=None, related_name='tasks_manager')
task = models.CharField(max_length=300)
done = models.BooleanField(default=False)
responsible = models.ForeignKey(User, on_delete=models.CASCADE, default=None, related_name='tasks_responsible', blank=True, null=True)
def __str__(self):
return self.task
I tryed don't use {{ form }} tag in template and thats work.
I think the problem is in views, but i can't figure out why.
Someone can help me?
From form rendering options as documented
All you need to do to get your form into a template is to place the form instance into the template context. So if your form is called form in the context, {{ form }} will render its and elements appropriately.
There are other output options though for the / pairs:
{{ form.as_table }} will render them as table cells wrapped in <tr> tags
{{ form.as_p }} will render them wrapped in <p> tags
{{ form.as_ul }} will render them wrapped in <li> tags
So you have a typo error in {% form.as_p %} just replace this with {{ form.as_p }}
I have to create a form to make a post with fields like Title, description and images. I could successfully create a post with a single image, but when it comes to multiple images, it is not getting saved. Upon research found that i have to use a separate model and form for images. But i cannot see how is it accomplished in the views. So how do i accomplish this the easy way?
Models.py
class news(models.Model):
title = models.CharField(max_length=100)
description= models.TextField(max_length=500)
class newsimages(models.Model):
image = models.ImageField(upload_to='/images/')
related = models.ForeignKey(news,on_delete=models.CASCADE,null=True)
You can do with inline_formset
create a form
class ImageForm(ModelForm):
class Meta:
model = newsimages
exclude = ()
ImageFormSet = inlineformset_factory(news, newsimage, form=ImageForm, extra=1)
You CreateView would be something like:
class NewsCreate(CreateView):
model = news
fields = ['title', 'description']
success_url = some_url
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['images'] = ImageFormSet(self.request.POST or None)
return ctx
def form_valid(self, form):
ctx = self.get_context_data()
images = context['images']
with transaction.atomic():
self.object = form.save()
if images .is_valid():
images.instance = self.object
images.save()
return super().form_valid(form)
in news_form.html
<form method="post" enctype="multipart/form-data">
{{ form.as_p }}
<table>
{{images.management_form}}
{% for f in images.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1,row2 %} formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Save"/>
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
$('.formset_row').formset({
addText: 'add image',
deleteText: 'remove',
prefix: 'images_set'
});
</script>
You will need to source jquery.formset.js which I think you'll find in django admin static files.
EDIT: Turns out my form was not validating. I added print(formset.errors) as an else in my view, which revealed that I had a required field not being completed.
I am trying to manually render formset fields in a template, but the fields are not being saved when I submit.
My method works for a different formset based on another model, and as far as I can tell I haven't done anything differently. I tried a number of changes, but the issue seems to be {{ form.id }} in the template; when I change it to {{ form }}, I am able to save some of the fields.
As mentioned, this same set up works for another model/formset; when I make changes to a field and save, the page is refreshed and the model shows as updated. The code below is for the model/formset that is not working.
models.py
class Call(models.Model):
name = models.CharField(max_length=200, null=True, blank=True)
customer_number = models.CharField(max_length=200, null=True, blank=True)
tracking_source = models.CharField(max_length=200, null=True, blank=True)
tracking_number = models.CharField(max_length=200, null=True,
qualified_offer = models.BooleanField(default=False)
recorded_date = models.DateTimeField(default=timezone.now)
def publish(self):
self.recorded_date = timezone.now()
self.save()
def t_number(self):
return self.tracking_number[-4:]
def __str__(self):
return str(self.name)
forms.py
class CallForm(forms.ModelForm):
class Meta:
model = Call
fields = ('name', 'customer_number', 'tracking_source', 'qualified_offer', 'recorded_date')
CallFormSet = modelformset_factory(Call, fields=('name', 'customer_number', 'tracking_source', 'recorded_date', 'qualified_offer'))
view.py
def call_list(request):
if request.method == 'GET':
formset = CallFormSet(queryset=Call.objects.all())
elif request.method == "POST":
formset = CallFormSet(request.POST)
if formset.is_valid():
for form in formset:
if form.cleaned_data.get('name'):
form.save()
return redirect('call_list')
return render(request, 'webapp/call_list.html', {'formset': formset})
call_list.html
{% extends 'webapp/base.html' %}
{% block content %}
<form class="form-horizontal" method="POST" action="">
<table style="width:100%" border="1">
<tr>
<th>Name</th>
<th>Qualified Offer</th>
</tr>
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
<tr>
<td>
<label>{{form.name}}</label></td>
<td>
<label>{{form.qualified_offer}}</label>
{% if form.qualified_offer.value %}
<label>True</label>
{% else %}
<label>False</label>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<div class="row spacer">
<div class="col-4 offset-2">
<button type="submit" class="btn btn-block btn-primary">Save</button>
</div>
</div>
</form>
{% endblock %}
With the code above, the page refreshes but the model is unchanged. Again, if I render the formset with {{ form }} instead of {{ form.id }} it does save, but then I am unable to manually render the fields.
Turns out my form was not validating. I added print(formset.errors) as an else in my view, which revealed that I had a required field not being completed.
I am getting error message the UnboundLocalError local variable 'Assumptions' referenced before assignment (after line else:). I wonder how is that even possible given that Assumptions is a model and is imported to the views.py. Any advise how to solve it would be highly appreciated. Also, I am trying to create multiple forms based on Assumptions modelform and save them into the database model Assumptions. Please advise if this code is the correct design pattern. Thank you.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
def get_assumptions(request):
if request.method == 'POST':
if 'name' in request.POST:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
if formset.is_valid():
print('valid form')
for form in formset:
print('Looping forms')
Assumptions = form.save(commit='False')
Assumptions.Name = 'name'
Assumptions.save()
else:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
return render(request, 'assumptions.html', {'formset': formset})
assumptions.html
<div class="form">
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
{% for name in ['A', 'B'] %}
<h1>var</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit", name='name', value="save" />
{% endfor %}
</form>
</div>
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst', 'Base', 'Best']
exclude = ['Name']
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
You are shadowing the imported Assumptions name by assigning a new value to it here:
Assumptions = form.save(commit='False')
You should use a different name for your model instance. The usual practice is to use a lowercase name, i.e.
assumptions = form.save(commit='False')
assumptions.Name = 'name'
assumptions.save()