I am having problem saving inline forms. It does save default forms. But whenever i add new inline forms, It doesnt save. What am i missing? can anyone show me the mistake? Thank you.
models.py
class Student(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class Course(models.Model):
student = models.ForeignKey(Student)
course = models.CharField(max_length=18)
forms.py
class StudentForm(forms.ModelForm):
class Meta:
model = Student
class CourseForm(forms.ModelForm):
class Meta:
model = Course
CourseFormset = inlineformset_factory(Student,Course, extra=1)
views.py
class CourseCreateView(View):
def post(self,request, *args, **kwargs):
form = StudentForm(request.POST)
if form.is_valid():
std = form.save(commit=False)
formset = CourseFormset(request.POST, instance=std)
if formset.is_valid():
std.save()
formset.save()
return HttpResponseRedirect("/course/list")
def get(self, request,*args, **kwargs):
studentform = StudentForm()
formset = CourseFormset()
return render(request,'example/course_form.html', {'formset': formset, 'studentform': studentform})
and the jquery-formset.js
https://dpaste.de/sVPT0/
Well I can't see any mistakes, but maybe you can use easier solution to add new form, so that you won't have to use jquery formset at all.
Formset class has nice attribute called empty_form:
https://docs.djangoproject.com/en/1.4/topics/forms/formsets/#empty-form
You can pass it as "empty_form" context variable and add this script inside template:
<script type="text/template" id="row-template">
<tr>
{% for field in empty_form %}
<td>{{ field }}</td>
{% endfor %}
</tr>
</script>
<script type="text/javascript">
var formset = {};
$(function() {
$('.btn-add-extra-form').click(function() {
formset.$total = $('#id_rows-TOTAL_FORMS');
formset.$initial = $('#id_rows-INITIAL_FORMS');
formset.templateRowStr = $('#row-template').html();
formset.newTotal = parseInt(formset.$total.val());
formset.appendRowStr = formset.templateRowStr.replace(/__prefix__/g, formset.newTotal);
formset.$total.val(formset.newTotal + 1);
$('.table-inline-rows tbody').append(formset.appendRowStr);
});
});
</script>
There.. no need to use jquery formset :) and that's the only changes I'm making, I'm not adding any extra code, django takes care of everything.
Related
Im trying to add a field called, interested_fields inside my personalInfo model which users can choose from and the choices themselves come from another models' objects with the help of ManyToMany relation between the two models. Here are my models.py codes(I simplified my personal model by removing some other fields like name, age, etc in order to make it more readable for you):
class Field(models.Model):
id = models.AutoField(primary_key=True)
slug = models.CharField(max_length=16, default='default')
title = CharField(max_length=32)
class PersonalInfo(models.Model):
id = models.AutoField(primary_key=True)
interested_fields = models.ManyToManyField(Field, blank=True)
then, I created a ModelForm like this:
class InterestedFieldsForm(forms.ModelForm):
interested_fields = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=Field.objects.all(), required=False)
class Meta:
model = PersonalInfo
fields = ['interested_fields']
and created a get and post functions inside my views like this:
class PersonalView(View):
template_name = 'reg/personal.html'
def get(self, request, *args, **kwargs):
context = {}
context['fields'] = Field.objects.all()
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
user = request.user
if request.method == 'POST':
form = InterestedFieldsForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
else:
form = InterestedFieldsForm()
return render(request, 'reg/done.html', context={'form': form})
and finally in template, inside the form I added this for loop:
{% for field in fields %}
<label class="containerq ant-col ant-col-md-6 ant-col-xs-8" >
<span>
<input type="checkbox" name="interested_fields" {% if field.slug in user.personalInfo.interested_fields %} checked="checked" {% endif %} value="{{field.title}}">
<span style="margin-left:7px" class="checkmark"></span>
</span>
<span>{{field.title}}</span>
</label>
{% endfor %}
when I submit the form it gives me this error:
cannot unpack non-iterable Field object
Im new to django so I really dont know what am I doing wrong. thank you for your answers
You should use a ModelMultipleChoiceField
interested_fields = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Field.objects.all(), required=False).
I got two models:
Project:
class Project(Model):
name = CharField(max_length=50)
members = ManyToManyField("accounts.User", through='ProjectUser')
organization = ForeignKey(Organization, related_name="projects", on_delete=CASCADE)
def __str__(self):
return self.name
and Task:
class Task(Model):
task = CharField(max_length=100)
project = ForeignKey(Project, on_delete=CASCADE)
class Meta:
db_table = 'task'
I got a UpdateView class:
class ProjectUpdateView(UpdateView):
form_class = ProjectUpdateForm
template_name = 'projects/project_edit.html'
success_url = reverse_lazy('projects:list')
How can I allow a user to add tasks (through an inline formset) on the same page as where they'd edit a Project instance?
E.g one consolidated form where the user can edit the Project name, and add / remove Task instances, all in one place
Form/Formset:
First, create a form and a formset for your Task model
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ['task']
def __init__(self, *args, **kwargs):
super(TaskForm, self).__init__(*args, **kwargs)
class TaskBaseFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(TaskBaseFormSet, self).__init__(*args, **kwargs)
TaskFormset = inlineformset_factory(
Project, # parent_model
Task, # model
form=TaskForm,
formset=TaskBaseFormSet
)
Or maybe all that you need to do to create a TaskFormset if you dont need a TaskForm class is this
TaskFormset = inlineformset_factory(Project, Task, fields=('task',))
View:
I see you're using a UpdateView class for your view, so you can do this to get a TaskFormset in your context_data, so now you can use the TaskFormset in the template that you declared in the 'template_name' property of your UpdateView class
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['task_formset'] = forms.TaskFormset(self.request.POST)
else:
context['task_formset'] = forms.TaskFormset()
return context
# In the form_valid method of your UpdateView class you can validate the data
# and assign the Project instance to all the tasks that were create by the formsets
def form_valid(self, form):
task_formset = context['task_formset']
# you can validate formset data like this
if not task_formset.is_valid():
return self.form_invalid(form)
project = form.save()
# Here you assign the Project instance to the Tasks
task_formset.instance = project
task_formset.save()
return super().form_valid(form)
Template:
Now all that you need to do is to print the management_form and each form from the formset using a loop as you can see in the code below
<form method="post">
<!-- Your ProjectUpdateForm form here... -->
{{ task_formset.management_form }}
<table>
{% for form in task_formset %}
{{ form }}
{% endfor %}
</table>
</form>
Hope this can help! There are some links to the official Django documentation that you may find useful:
https://docs.djangoproject.com/en/3.1/topics/forms/formsets/#using-a-formset-in-views-and-templates
https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/#inline-formsets
https://docs.djangoproject.com/en/3.1/ref/forms/models/#inlineformset-factory
I am trying to create "Edit" for my form.
urls.py
url(r'^app_1/(?P<id>[-\w]+)/edit/$',views.edit, name = 'edit'),
forms.py
class ClanakForma(forms.ModelForm):
class Meta:
model = Clanak
fields = '__all__'
models.py
class Clanak(models.Model):
naslov = models.CharField(null=False, blank=True, max_length=120)
datumObjave = models.DateField(null=False, blank=False)
autor = models.CharField(null=False, blank=True, max_length=50)
email = models.EmailField(max_length=75, null=True, blank=True)
def __str__(self):
return str(self.naslov) + ', ' + str(self.datumObjave) + ', ' + str(self.autor)
views.py
def edit(request, id):
data = get_object_or_404(Clanak, id = id)
if request.method == "POST":
form = ClanakForma(request.Clanak, instance=data)
if form.is_vaild():
data = form.save(commit=False)
data.naslov = request.user
data.datumObjave = request.user
data.autor = request.user
data.email = request.user
return redirect('readAllNew')
else:
form = ClanakForma(instance=data)
template = 'readAllNew.html'
context = {'form': form}
return render(request, template, context)
readAllNew.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<table border="1">
<tr>
<th>Naslov</th>
<th>Datum</th>
<th>Autor</th>
<th>Mail</th>
</tr>
{% for x in data %}
<tr>
<td>{{x.naslov}}</td>
<td>{{x.datumObjave}}</td>
<td>{{x.autor}}</td>
<td>{{x.email}}</td>
<td>delete</td>
<td>edit</td>
</tr>
{% endfor %}
</table>
</body>
</html>
So when I hit edit link at my "readAllNew.html" I get error:
TypeError at /app_1/5/edit/
init() got an unexpected keyword argument 'instance'
I think something is wrong with my view but I am not sure what.
-------------------------------UPDATE--------------------------------
As suggested I edited "forms.py" but now I am not seeing any form field so I can't edit nothing:
See screenshot
---------------- UPDATE 2 -------------------------
I created new html file as "edit.html"
I edited "views.py":
def edit(request, id):
data = get_object_or_404(Clanak, id = id)
if request.method == "POST":
form = ClanakForma(instance=data)
if form.is_vaild():
data = form.save(commit=False)
data.naslov = request.user
data.datumObjave = request.user
data.autor = request.user
data.email = request.user
return redirect('readAllNew.html')
else:
form = ClanakForma(instance=data)
template = 'edit.html'
context = {'form': form}
return render(request, template, context)
Now I see fields and informations in it properly, but when I change something and click on "SUBMIT" I get error:
AttributeError at /app_1/6/edit/
'ClanakForma' object has no attribute 'is_vaild'
Use forms.ModelForm instead of forms.Form
class Forma(forms.ModelForm):
naslov = forms.CharField(label='naslov')
datumObjave = forms.DateField(label='datumObjave')
autor = forms.CharField(label='autor')
email = forms.EmailField(label='email')
class Meta:
model = Clanak
fields = ('naslov', 'datumObjave', 'autor', 'email')
If you want to use all the fields of Model class in your form, you could specify the fields attribute of Meta class as fields = '__all__'
#example
class Forma(forms.ModelForm):
class Meta:
model = Clanak
fields = '__all__'
If you are using forms.ModelForm, you could avoid most of the implementation which makes things easier :)
Update
You need to render the form in your template.So, change your template as
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
You can't pass instance to simple form - it has to be ModelForm. Use this:
class Forma(forms.ModelForm):
class Meta:
model = Clanak
fields = ['naslov', 'datumObjave', 'autor', 'email']
labels = {
'naslov': 'naslov',
'datumObjave': 'datumObjave',
'autor': 'autor',
'email': 'email'
}
I am working on an UpdateView of a form. I am having hard time displaying the initial value of grades per subject. I can display the subjects fine in the template, however I can't display the grades using the DecimalField. If change DecimalField to ModelChoiceField in forms.py, I can view the grades in a drop down menu in template but this is not what I want. I want the user to be able to edit using DecimalField.
forms.py
class GradeUpdateForm(CrispyFormMixin, forms.ModelForm):
s_name = forms.ModelChoiceField(queryset=Subject.objects.none(), empty_label=None)
final_grade = forms.DecimalField(widget=forms.NumberInput(attrs={'style':'width:80px'}), decimal_places=2, max_digits=5,)
class Meta:
model = SGrade
fields = [
's_name',
'final_grade',
]
views.py
class SchoolDashboardGradesUpdateView(SchoolStudentMixin, UpdateView):
template_name = 'education/dashboard/grades_update.html'
model = SubjectGrade
form_class = GradeUpdateForm
# def get_object(self):
# return get_object_or_404(Recipient, pk=self.kwargs['pk'])
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
form.fields['subject_name'].queryset = Subject.objects.filter(
sgrade__recipient__id=self.kwargs.get('pk'),
sgrade__status='approved').values_list('name', flat=True)
form.fields['final_grade'].queryset = SGrade.objects.filter(
recipient__id=self.kwargs.get('pk'),
status='approved').values_list('final_grade', flat=True)
student = Student.objects.get(id=self.kwargs.get('pk')
for x in student.sgrade_set.all():
if x.status == 'approved':
forms.fields['final_grade'].initial = x.final_grade
template
<tbody>
{% for instance in form.subject_name.field.choices %}
<tr>
<td>{{instance.1}}</td>
<td>{{form.final_grade}}</td>
</tr>
{% endfor %}
</tbody>
Any suggestions on how to approach this? I am a complete beginner.
I have two models like this:
class Person(models.Model):
name = models.CharField(max_length=100)
house = models.ForeignKey('House')
class House(models.Model):
address = models.TextField()
Is there some way to create a model form for Person and have it include inline the form to edit the related House object as well? From what I understand of the inline formsets stuff, I would only use that if I have a form editing a House and I want to display forms for all the related Persons. Any ideas?
Just stick the HouseForm into the PersonForm, evaluate it as part of the clean() process, and save it as part of the save() process. Works in a modelformset too.
class HouseForm(forms.modelForm):
""" Edit a house """
class Meta:
model = House
exclude = ()
class PersonForm(forms.ModelForm):
""" Edit a person and her house """
class Meta:
model = Person
exclude = ()
def __init__(self, *args, **kwargs):
super(PersonForm, self).__init__(*args, **kwargs)
self.fields['house'].required = False
data = kwargs.get('data')
# 'prefix' parameter required if in a modelFormset
self.house_form = HouseForm(instance=self.instance and self.instance.house,
prefix=self.prefix, data=data)
def clean(self):
if not self.house_form.is_valid():
raise forms.ValidationError("House not valid")
def save(self, commit=True):
obj = super(PersonForm, self).save(commit=commit)
obj.house = self.house_form.save()
obj.save()
Then in your markup:
<form ...>
{{ person_form }}
{{ person_form.house_form }}
</form>
You have access to the related House object through the Person. As such, I would use the house object as the instance for a Modelform.
HouseForm(ModelForm):
class Meta:
model = House
Say you have a Person object of Paul with a related House.
house_form = HouseForm(instance=Paul.house)
Is this what you were getting at?
I'm not sure whether it is the best way to solve it, but I would do something like this:
Define a ModelForm from each model:
class PersonForm(ModelForm):
class Meta:
model = Person
class HouseForm(ModelForm):
class Meta:
model = House
Define a template like this one, outputting both forms:
<form action="" method="post">
<table>
{{ form1 }}
{{ form2 }}
</table>
<input type="submit">
</form>
A view to create the form to edit the information from both models.
def edit(request):
# You could grab the id from the request that calls the edit form
p = models.Person.objects.get(pk=request.GET.get('id'))
h = models.House.objects.get(pk=p.house.id)
return render_to_response('template.html',
RequestContext(request,
{'form1': PersonForm(instance=p), 'form2': HouseForm(instance=h)}
)
)
And so on.