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.
Related
I am pretty new to Django and still learning, but I am having a hard time trying to figure out how to let a user upload a .txt file but instead have the uploaded .txt file overwrite in the textfield itself.
Example: When uploaded https://imgur.com/a/jdCjlVS
I haven't been able to find understandable resources, but this is all I have at the moment:
forms.py
class NewInput(forms.Form):
text = forms.CharField(label='Input', max_length=1000, required=False)
file = forms.FileField(required=False)
models.py
class Collection(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="collection", null=True)
text = models.TextField(max_length=1000, default='')
create.html
{% extends 'main/base.html' %}
{% load crispy_forms_tags %}
{% block title %}
New Input
{% endblock %}
{% block content %}
<center>
<h3>Create a New Input:</h3>
<p class="text-primary"></p>
<form method = "post" action = "/create/" class="form-group" enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<div class="input-group mb-3">
<div class="col text-center">
<button type="submit" name="save" class="btn btn-success">Create</button>
</div>
</div>
</form>
</center>
{% endblock %}
views.py
def create(response):
if response.user.is_authenticated:
username = response.user.username
if response.method == "POST":
form = NewInput(response.POST)
if form.is_valid():
n = form.cleaned_data["text"]
t = Collection(text=n)
t.save()
response.user.collection.add(t)
return HttpResponseRedirect("/collections/%s" % username)
else:
form = NewInput()
return render(response, "main/create.html", {"form": form})
else:
return HttpResponseRedirect("/login")
I tried adding a separate class as a form field but I was unable to figure out how to make it overwrite the text area instead.
I'm new to Django and I have a problem that makes me quite confused. I have a page when users click to change profile, the corresponding page shows up and lets users update their profile. Here is my model:
from django.db import models
import os
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from django.utils import timezone
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class Account(models.Model):
user = models.ForeignKey(User, on_delete="CASCADE")
phone = models.CharField(max_length=18)
room = models.CharField(max_length=8)
dob = models.DateField(default=timezone.datetime.now())
active = models.BooleanField(default=True)
avatar = models.ImageField(upload_to='images/', default=os.path.join(settings.STATIC_ROOT, 'avatar.png'))
def __str__(self):
return self.user.username
Here are my forms:
class UserForm(forms.ModelForm):
first_name = forms.CharField(max_length=100,
widget=forms.TextInput(attrs={'class': 'uk-input', 'placeholder': 'Last Name'}))
last_name = forms.CharField(max_length=100,
widget=forms.TextInput(attrs={'class': 'uk-input', 'placeholder': 'Last Name'}))
class Meta:
model = User
fields = ['first_name', 'last_name', 'email']
class ProfileForm(forms.ModelForm):
class Meta:
model = Account
fields = ['phone', 'room', 'dob', 'active', 'avatar']
And I have my views.py like this:
def show_form(request):
user_basic_info = UserForm(request.POST)
form = ProfileForm(request.POST)
if form.is_valid() and user_basic_info.is_valid():
form.save() and user_basic_info.save()
messages.sucess(request, _('Your profile has been successfully updated'))
redirect('my_account')
else:
UserForm()
ProfileForm()
context = {
'user_basic_info': user_basic_info,
'form': form,
}
return render(request, 'my_account.html', context)
Here is my_account.html template:
{% extends 'base.html' %}
{% block title %}My account{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item active">Update your information</li>
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-6 col-md-8 col-sm-10">
<form method="post" novalidate>
{% csrf_token %}
{% include 'includes/form.html' %}
<button type="submit" class="btn btn-success">Save changes</button>
</form>
</div>
</div>
{% endblock %}
And forms.html:
{% load widget_tweaks %}
<table>
<div class="form-group">############
{{ user_basic_info }} ############# This displays first_name, last_name and email. Also the problem's here, I want to make 2 forms in one form with a better style like the form below.
</div>############
</table>
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
<p{% if forloop.last %} class="mb-0"{% endif %}>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
{% for field in form %}
<div class="form-group">
{{ field.label_tag }} {% if form.is_bound %} {% if field.errors %} {% render_field field class="form-control is-invalid" %} {% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %} {% else %} {% render_field field class="form-control is-valid" %} {% endif %} {% else %} {% render_field field class="form-control" %} {% endif %} {% if field.help_text %}
<small class="form-text text-muted">
{{ field.help_text|safe }}
</small> {% endif %}
</div>
{% endfor %}
So when users want to change their profile, those fields first_name, last_name, email, phone, room, dob, active, avatar need to be displayed. But the first 3 fields belong to the User model and the rest fields are defined in my Account model. I want when users submit the form, those fields are linked together, e.g Account is an instance of User and that information is properly saved to the database in Account model (I have watched some tutorials but I still cannot properly do it). And when logging to the page, login authentication using User model, but when updating the profile it's Account model and User model, but there is no relationship between them, how can I fix all of those errors, thanks in advance.
you need to use OneToOne Relationship here since each user has only one and unique profile details. so in your models.py use :-
user = models.OneToOneField(User, on_delete=models.CASCADE)
If you use default user there is 4 way to extends it so here i give a link follow them,
if you use default user you need to use Usercreation form for Register because the user model have password encryption algorithmuse so when you use you user define form it will not store your password
follow instruction for extends user abstract base model
extends user using
registration form using usercreation form
Define view of save data
follow instruction for extends user using one to one
Create new model Profile and give one to one relationship
Create registration form usring model.formplus additional fields you want to take from user define in form class explicity
in view save form and add user reference in Profile model using create method
link:
extend user model
if you use usercreation form
go through i give link if you got error let me know
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.
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()
Hi i used formsets in generic FormView like this:
class RequestRecommendationView(FormView):
template_name = "account/stepfour.html"
form_class = formset_factory(StepFourForm)
def form_valid(self,form):
print form.is_valid()
cleaned_data = form.cleaned_data
# return redirect(reverse("some_url_name"))
form for formset_factory is like this:
class StepFourForm(forms.Form):
contact_person = forms.CharField(required=True)
email = forms.EmailField(required=True)
company = forms.CharField(required=True)
request_message = forms.CharField(required=True)
my html structure is like this:
<div style="padding-top:100px;padding-left:10px;">
<h4> Request a Recommendation </h4>
<form method="post" action="">
{% csrf_token %}
<table id="myForm">
<tbody>
{% for f in form %}
{% for field in f %}
<tr>
<td>{{field.label_tag}}</td>
<td>{{field}}</td>
{% for error in field.errors %}
<td><span>{{ error }}</span></td>
{% endfor %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<button class="btn btn-primary btn-xlarge" type="submit" name="submit">Request Now</button>
{{ form.management_form }}
</form>
</div>
Then i used django-dynamic-formset (https://code.google.com/p/django-dynamic-formset/)
to add/remove extra forms through these:
<script type="text/javascript">
$(function() {
$('#myForm tbody').formset();
})
</script>
the problem is:
if i leave the form empty,(every field is required), it manages to get to form_valid() of my class-view (though it should not), if i fill one of the fields (leaving others unfilled), it displays the errors associated message successfully. why is this happening ? what should i do to fix it ? is providing form_class a formset is behind all of this ?
This question has been asked 6 years ago, I found it because i faced the same issue, since it is unanswered, I'll provide the solution i found.
according to the documentation to validate a minimum of forms, you have to provide a min_num and set validate_min=True in the formset_factory:
formset = formset_factory(your_form, min_num=2, validate_min=True, extra=2)