I have two models connected by manytomany relationship and I am trying to use formset to create a dynamic form. I am able to save the form but the problem arise when I am trying to edit the saved instance, I don't know how to properly pass the instance to the formset such that it shows the instance data in form for editing
Here are the details:
Models.py
class Player(models.Model):
pname = models.CharField(max_length=50)
hscore = models.IntegerField()
age = models.IntegerField()
def __str__(self):
return self.pname
class Team(models.Model):
tname = models.CharField(max_length=100)
player= models.ManyToManyField(Player)
def __str__(self):
return self.tname
Forms.py
class PlayerForm(forms.ModelForm):
class Meta:
model = Player
fields = '__all__'
PlayerFormset= formset_factory(PlayerForm)
class TeamForm(forms.ModelForm):
player= PlayerFormset()
class Meta:
model = Team
fields = '__all__'
exclude = ["player"]
Views.py
def team(request):
if request.POST:
form = TeamForm(request.POST)
form.player_instances = PlayerFormset(request.POST)
if form.is_valid():
team= Team()
team.tname= form.cleaned_data['tname']
team.save()
if form.player_instances.cleaned_data is not None:
for item in form.player_instances.cleaned_data:
player = Player()
player.pname= item['pname']
player.hscore= item['hscore']
player.age= item['age']
player.save()
team.player.add(player)
team.save()
else:
form = TeamForm()
return render(request, 'packsapp/employee/new.html', {'form':form})
def updateTeam(request,pk):
team = Team.objects.get(id=pk)
form = TeamForm(instance=team)
// something here to initialize the formset ??
if request.method == "POST":
form = TeamForm(request.POST, instance=team)
if form.is_valid():
form.save()
context = {'form': form}
return render(request, 'packsapp/employee/new.html', context)
Html
<html>
<head>
<title>gffdfdf</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="/static/jquery.formset.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form id="myForm" action="" method="post" class="">
{% csrf_token %}
<h2> Team</h2>
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
{{ form.player.management_form }}
<h3> Product Instance(s)</h3>
<table id="table-product" class="table">
<thead>
<tr>
<th>player name</th>
<th>highest score</th>
<th>age</th>
</tr>
</thead>
{% for player in form.player %}
<tbody class="player-instances">
<tr>
<td>{{ player.pname }}</td>
<td>{{ player.hscore }}</td>
<td>{{ player.age }}</td>
<td><input id="input_add" type="button" name="add" value=" Add More "
class="tr_clone_add btn data_input"></td>
</tr>
</tbody>
{% endfor %}
</table>
<button type="submit" class="btn btn-primary">save</button>
</form>
</div>
<script>
var i = 1;
$("#input_add").click(function () {
$("tbody tr:first").clone().find(".data_input").each(function () {
if ($(this).attr('class') == 'tr_clone_add btn data_input') {
$(this).attr({
'id': function (_, id) {
return "remove_button"
},
'name': function (_, name) {
return "name_remove" + i
},
'value': 'Remove'
}).on("click", function () {
var a = $(this).parent();
var b = a.parent();
i = i - 1
$('#id_form-TOTAL_FORMS').val(i);
b.remove();
$('.player-instances tr').each(function (index, value) {
$(this).find('.data_input').each(function () {
$(this).attr({
'id': function (_, id) {
console.log("id", id)
var idData = id;
var splitV = String(idData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + index + "-" + tData
},
'name': function (_, name) {
console.log("name", name)
var nameData = name;
var splitV = String(nameData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + index + "-" + tData
}
});
})
})
})
} else {
$(this).attr({
'id': function (_, id) {
console.log("id", id)
var idData = id;
var splitV = String(idData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + i + "-" + tData
},
'name': function (_, name) {
console.log("name", name)
var nameData = name;
var splitV = String(nameData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + i + "-" + tData
}
});
}
}).end().appendTo("tbody");
$('#id_form-TOTAL_FORMS').val(1 + i);
$("tbody tr:last :input").each(function () {
$(this).attr({
'id': function (_, id) {
return id.replace(/\d/g, i)
},
'name': function (_, name) {
return name.replace(/\d/g, i)
},
})
})
i++;
});
</script>
</body>
</html>
update:
Use modelformset_factory in the views directly:
def post(request):
tform = TeamForm()
pform = modelformset_factory(Player, form=PlayerForm, extra = 1)
pform = pform(request.POST or None, queryset = Player.objects.filter(id__isnull = True))
if request.method == 'POST':
t = Team()
tform = TeamForm(request.POST, instance=t)
if tform.is_valid() and pform.is_valid():
tform.save()
instances = pform.save(commit=False)
for i in instances:
player = Player()
player.pname = i.pname
player.hscore = i.age
player.age = i.hscore
player.save()
t.player.add(player)
t.save()
return redirect('/exams/dashboard/')
else:
print('invalid data')
return render(request, 'team/team_create.html', {'exform': tform, 'exformset': pform})
def update(request, pk = None):
team = Team.objects.get(id = pk)
tform = TeamForm(instance = team)
pform = modelformset_factory(Player, form=PlayerForm, extra=0)
print("players", Player.objects.filter(team=team))
pform = pform(request.POST or None, queryset=Player.objects.filter(team=team))
if request.method == 'POST':
tform = TeamForm(request.POST, instance=team)
print("tform ", tform)
print("pform ", pform)
if tform.is_valid() and pform.is_valid():
tform.save()
instances = pform.save(commit=False)
for i in instances:
player = Player()
player.pname = i.pname
player.hscore = i.age
player.age = i.hscore
player.save()
t.player.add(player)
t.save()
return redirect('/exams/dashboard/')
else:
print('invalid data')
return render(request, 'team/team_create.html', {'exform': tform, 'exformset': pform})
The TeamForm has to set the queryset of the PlayerFormset.
The following shows how.
class TeamForm(forms.ModelForm):
player= PlayerFormset()
class Meta:
model = Team
fields = '__all__'
exclude = ["player"]
def __init__(self,*args, **kwargs):
super(TeamForm,self).__init__(*args,**kwargs)
self.player = PlayerFormSet(queryset=Players.objects.filter(team=self.instance)
Information from the documentation:
https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#changing-the-queryset
maybe also worth a look:
https://stackoverflow.com/a/34323401/13168118
EDIT:
your PlayerFormset should be created with a modelformset_factory like:
PlayerFormset = modelformset_factory(Player, form=PlayerForm)
modelformset documentation: https://docs.djangoproject.com/en/2.2/ref/forms/models/#modelformset-factory
Related
productscreate.html
<form data-bind="submit: save" method="post">
{% csrf_token %}
<table border="1">
<tr>
<td>Title:
<input type="text" name="title" id="title" data-bind="value: $data.title"></td>
<br>
</tr>
<tr>
<td>Description:
<textarea name="description" id="description" data-bind="value: $data.description">Description</textarea></td>
<br>
</tr>
<tr>
<td>Image:
<input type="file" name="image" id="image" ></td>
<br>
</tr>
<tr>
<td><button type="button" id="submit" data-bind="click: save">Submit</button></td>
</tr>
</table>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script>
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
</script>
<script>
var ViewModel = function () {
var self = this;
self.title = ko.observable("");
self.description = ko.observable("");
var FormData = {
title: self.title,
description: self.description,
};
console.log(FormData);
self.save = function () {
$.ajax({
type: "POST",
url: 'http://127.0.0.1:8000/productsadd',
data: FormData,
contentType: "application/json",
headers: {'X-CSRFToken': csrftoken},
cache: false,
enctype: "multipart/form-data",
//processData: false,
success: function (FormData) {
alert("successfull")
window.location = '{% url "productslist" %}';
},
error: function () {
alert("fail");
}
});
};
};
ko.applyBindings(new ViewModel())
</script>
views.py
class ProductsCreate(CreateView):
model = Products
template_name = "productscreate.html"
fields = ['title', 'description', 'image']
success_url=reverse_lazy('productslist')
def productsAdd(request):
if request.is_ajax and request.method == "POST":
product=Products()
product.title = request.POST.get('title')
product.description = request.POST.get('description')
# product.image = request.FILES['image']
product.save()
return render(request,'productslist.html')
else:
return render(request,'productscreate.html')
class ProductsDetailView(DetailView):
template_name = "productsdetail.html"
queryset = Products.objects.all()
context_object_name = 'products'
model = Products
models.py
class Products(models.Model):
title = models.CharField(max_length=200,null=True)
description = models.CharField(max_length=200,null=True)
image = models.FileField(blank=True)
def __str__(self):
return str(self.title)
When i create a product in form it save as none in django admin I don't know where is the issue
I have given html with knockout js and ajax,views and models
I want to submit title,description and image in django admin using form in createproduct html
Please help me to solve this
Thanks in advance
In my django app I am trying to understand many to many relationship and I am using formset to store the data like this:
Views.py
def Team_Form(request):
if request.POST:
form = TeamForm(request.POST)
form.player_instances = PlayerFormset(request.POST)
if form.is_valid():
team= Team()
team.tname= form.cleaned_data['tname']
team.save()
if form.player_instances.cleaned_data is not None:
for item in form.player_instances.cleaned_data:
player = Player()
player.pname= item['pname']
player.hscore= item['hscore']
player.age= item['age']
player.save()
team.player.add(player)
team.save()
else:
form = TeamForm()
return render(request, 'packsapp/employee/new.html', {'form':form})
Models.py
class Player(models.Model):
pname = models.CharField(max_length=50)
hscore = models.IntegerField()
age = models.IntegerField()
def __str__(self):
return self.pname
class Team(models.Model):
tname = models.CharField(max_length=100)
player= models.ManyToManyField(Player)
def __str__(self):
return self.tname
Forms.py
class PlayerForm(forms.Form):
pname = forms.CharField()
hscore= forms.IntegerField()
age = forms.IntegerField()
PlayerFormset= formset_factory(PlayerForm)
class TeamForm(forms.Form):
tname= forms.CharField()
player= PlayerFormset()
HTML
<html>
<head>
<title>gffdfdf</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="/static/jquery.formset.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<form id="myForm" action="" method="post" class="">
{% csrf_token %}
<h2> Team</h2>
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% endfor %}
{{ form.player.management_form }}
<h3> Product Instance(s)</h3>
<table id="table-product" class="table">
<thead>
<tr>
<th>player name</th>
<th>highest score</th>
<th>age</th>
</tr>
</thead>
{% for player in form.player %}
<tbody class="player-instances">
<tr>
<td>{{ player.pname }}</td>
<td>{{ player.hscore }}</td>
<td>{{ player.age }}</td>
<td><input id="input_add" type="button" name="add" value=" Add More "
class="tr_clone_add btn data_input"></td>
</tr>
</tbody>
{% endfor %}
</table>
<button type="submit" class="btn btn-primary">save</button>
</form>
</div>
<script>
var i = 1;
$("#input_add").click(function () {
$("tbody tr:first").clone().find(".data_input").each(function () {
if ($(this).attr('class') == 'tr_clone_add btn data_input') {
$(this).attr({
'id': function (_, id) {
return "remove_button"
},
'name': function (_, name) {
return "name_remove" + i
},
'value': 'Remove'
}).on("click", function () {
var a = $(this).parent();
var b = a.parent();
i = i - 1
$('#id_form-TOTAL_FORMS').val(i);
b.remove();
$('.player-instances tr').each(function (index, value) {
$(this).find('.data_input').each(function () {
$(this).attr({
'id': function (_, id) {
console.log("id", id)
var idData = id;
var splitV = String(idData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + index + "-" + tData
},
'name': function (_, name) {
console.log("name", name)
var nameData = name;
var splitV = String(nameData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + index + "-" + tData
}
});
})
})
})
} else {
$(this).attr({
'id': function (_, id) {
console.log("id", id)
var idData = id;
var splitV = String(idData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + i + "-" + tData
},
'name': function (_, name) {
console.log("name", name)
var nameData = name;
var splitV = String(nameData).split('-');
var fData = splitV[0];
var tData = splitV[2];
return fData + "-" + i + "-" + tData
}
});
}
}).end().appendTo("tbody");
$('#id_form-TOTAL_FORMS').val(1 + i);
$("tbody tr:last :input").each(function () {
$(this).attr({
'id': function (_, id) {
return id.replace(/\d/g, i)
},
'name': function (_, name) {
return name.replace(/\d/g, i)
},
})
})
i++;
});
</script>
</body>
</html>
What I failed to understand is that how to edit the formset that I just Saved or to better phrase the question, How to pass the saved instance to the formset to edit it ?
Update:
I tried the modelformset_factory and it fetches all the objects from Player in post as well as update
Forms.py
PlayerFormset= modelformset_factory(Player, fields=('pname','hscore','age'))
Screenshot:
While tried to edit team Matt:
Many to many here means that one player can be in several team and also that one team can have many players.
To resolve your problem you have to create an other view, (link to the same form) that will display your form but already fill.
In your function pk for your team.
def updateTeam(request,pk):
team = Team.objects.get(id=pk)
form = TeamForm(instance=team)
if request.method == "POST":
form = TeamForm(request.POST, instance=team)
if form.is_valid():
form.save()
context = {'form': form}
return render(request, 'accounts/order_form.html', context)
That should resolve your problem !
Do not hesitate if you have any questions
If you want that, then you have to pass the filled-in form back to the template. A good example of this in the docs is https://docs.djangoproject.com/en/3.0/topics/forms/formsets/#using-a-formset-in-views-and-templates. In your code this would look like this (haven't tried in with a template):
def team_view(request):
PlayerFormset = formset_factory(PlayerForm)
if request.POST:
form = TeamForm(request.POST)
form.player_instances = PlayerFormset(request.POST)
if form.is_valid():
team = Team()
team.tname = form.cleaned_data['tname']
team.save()
if form.player_instances.cleaned_data is not None:
for item in form.player_instances.cleaned_data:
player = Player()
player.pname= item['pname']
player.hscore= item['hscore']
player.age= item['age']
player.save()
team.player.add(player)
team.save()
else:
form = TeamForm()
return render(request, 'packsapp/employee/new.html', {'form': form})
I have changed a couple of things. First of all, use lowercase for function based views, and try not to use the name 'form' in a view. Furthermore, notice the indentation: the 'if form.player_instances.cleaned_data...' has an extra indent. There is not much use checking players if there is no team, you will not be able to save the (non-existent) team. Then: the 'return'-statement is now on the same level as the first if/else statement. In your version there is no return after saving the form. By doing this, the filled-in form (from the if-part of the statement) is returned in the context-variable. It is then the job of the template to decide what to do with it. In the else-case, an empty form is returned.
I've noticed that this project is apparently a tutorial, there are at least two related questions on StackOverflow: Django Dynamic form for manytomany relationship and How can i save django dynamic formset data using forms and views. Maybe you can learn from those.
Additionally to the answer https://stackoverflow.com/a/61185348/13168118 of Matthieu-OD
you could change the
PlayerFormset= formset_factory(PlayerForm)
to a
modelformset_factory
https://docs.djangoproject.com/en/3.0/ref/forms/models/#django.forms.models.modelformset_factory
and in the init method of the 'TeamForm' you should be able to adjust the queryset of the modelformset to only show the players of this team
if you don't adjust it every player will be shown
EDIT:
i would also suggest that you use modelforms since your forms are for models:
https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/#modelform
i also found this question which seems to be similar:
Django ModelForm for Many-to-Many fields
I have a form with a selection box related to a foreign key (for example, category). And on the same page, I have another link that opens a new page to add a new instance of the foreign key. Once the new instance is added, how can I update the current form to add the new option, and preserve the text in the text field (just like how the admin page behaves)?
Here are some of my code snippets:
update_post.html:
...
<form method="post" novalidate action='.'>
{% csrf_token %}
{% include 'base_form.html' with form=form %}
# button to add a category
{% trans "Add category" %}
<button type="submit" class="btn btn-primary" name="publish" value={% trans 'Publish' %}>{% trans 'Publish' %}</button>
</form>
...
create_category.html:
...
<form action="./{% if request.GET %}?{{ request.GET.urlencode }}{% endif %}" method="post" novalidate>
{% csrf_token %}
{% include 'base_form.html' with form=form %}
<button type="submit" class="btn btn-primary" value={% trans 'Create' %}>{% trans 'Create' %}</button>
</form>
...
views.py:
...
class PostUpdate(UpdateView):
template_name = 'update_post.html'
success_url = '/'
model = Post
fields = ['title', 'body', 'category']
class CategoryCreate(CreateView):
template_name = 'create_category.html'
model = Category
fields = ['name']
def get_success_url(self):
if 'next' in self.request.GET:
return self.request.GET.get('next')
else:
return reverse('index')
...
What I want to do is that when a new category is added, it becomes available in the update_post page right away, and any changes to the body field are preserved.
I have done this yesterday inspired by django admin ForeignKey popup window add.my condition is a goods has goodcategory and i can add/edit/delet goodscategory in goods's add/update view and the result add/edit/delet goodcategory will sync to goods's add/update view.Here is a demo,which popup is support by layui.
as you can see i can add\change\delete ForeignKey without refresh the parent page.
first custom a new Field to ForeignKey which will receieve add_url\update_url\delete_url:
class ForeignKeyWidget(Select):
template_name = 'widgets/foreign_key_select.html'
def __init__(self, url_template, *args, **kw):
super(ForeignKeyWidget, self).__init__(*args, **kw)
# Be careful that here "reverse" is not allowed
self.url_template = url_template
def get_context(self, name, value, attrs):
context = super(ForeignKeyWidget, self).get_context(name, value, attrs)
context['add_url'] = self.url_template
context['update_url'] = self.url_template
context['delete_url'] = self.url_template + 'lang_delete/'
return context
second is custom a widget for your custom field which can popup add/update category windows and use ajax to delete category:
foreign_key_select.html:
{% include "django/forms/widgets/select.html" %}
<style>
#{{ widget.attrs.id }}_add, #{{ widget.attrs.id }}_change, #{{ widget.attrs.id }}_delete {
margin-top: 10px;
padding: 0 10px;
height: 25px;
line-height: 25px;
}
</style>
<a class="layui-btn layui-btn-mini" id="{{ widget.attrs.id }}_add">
add
</a>
<a class="layui-btn layui-btn-mini layui-btn-disabled" id="{{ widget.attrs.id }}_change">
change
</a><a class="layui-btn layui-btn-mini layui-btn-disabled" id="{{ widget.attrs.id }}_delete">
delete
</a>
<script>
$('#{{ widget.attrs.id }}_add').click(function () {
var index = layui.layer.open({
title: "add_category",
type: 2,
area: ['700px', '500px'],
content: "{{ add_url }}" + '?popup=1&to_field={{ widget.attrs.id }}',
success: function (layer, index) {
}
});
});
$("#{{ widget.attrs.id }}_change").click(function () {
var id = $('#{{ widget.attrs.id }}').val();
if (id) {
var index = layui.layer.open({
title: "change_category",
type: 2,
area: ['700px', '500px'],
content: '{{ update_url }}' + id + '?popup=1&to_field={{ widget.attrs.id }}',
success: function (layer, index) {
}
});
}
});
$("#{{ widget.attrs.id }}_delete").click(function () {
var id = $('#{{ widget.attrs.id }}').val();
var value = $('#{{ widget.attrs.id }} option[value=' + id + ']').text();
var indexGood = value.lastIndexOf('>');
var valueN = indexGood > 0 ? value.substring(indexGood + 1, value.length) : value;
if (id) {
layer.confirm('corform delete' + valueN + ' ?', {icon: 3, title: 'delete'}, function (index) {
$.ajax({
type: "POST",
data: {},
url: '{{ delete_url }}' + id + '/',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
},
success: function (data, textStatus) {
layer.close(index);
$('#{{ widget.attrs.id }} option[value=' + data.id + ']').remove();
$("#{{ widget.attrs.id }}_change,#{{ widget.attrs.id }}_delete").addClass('layui-btn-disabled');
return false;
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
layer.alert('delete failed' + XMLHttpRequest.responseText)
}
});
});
}
});
function {{ widget.attrs.id }}_isDisabled() {
if ($('#{{ widget.attrs.id }}').val()) {
$("#{{ widget.attrs.id }}_change,#{{ widget.attrs.id }}_delete").removeClass('layui-btn-disabled');
} else {
$("#{{ widget.attrs.id }}_change,#{{ widget.attrs.id }}_delete").addClass('layui-btn-disabled');
}
}
$('#{{ widget.attrs.id }}').change(function () {
{{ widget.attrs.id }}_isDisabled();
});
{{ widget.attrs.id }}_isDisabled();
</script>
third is use your custom field for category in your forms.py:
class GoodsForm(ModelForm):
def __init__(self, *args, **kwargs):
super(GoodsForm, self).__init__(*args, **kwargs)
self.fields['category'].widget.attrs.update({'class': 'form-control'})
self.fields['title'].widget.attrs.update({'class': 'form-control'})
self.fields['content'].widget.attrs.update({'class': 'form-control'})
class Meta:
model = Goods
fields = ['category', 'title', 'content']
widgets = {
'category': ForeignKeyWidget(url_template=reverse_lazy('goods_category_ajax_create')),
}
and new a goodcategory form is forms.py
class GoodsCategoryForm(TranslatableModelForm):
def __init__(self, *args, **kwargs):
super(GoodsCategoryForm, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'class': 'form-control'})
self.fields['cover'].widget.attrs.update({'class': 'form-control'})
self.fields['parent'].widget.attrs.update({'class': 'form-control'})
class Meta:
model = GoodsCategory
fields = ['name', 'cover', 'parent']
four is handle request in your views.py:
class GoodsCategoryAjaxCreateView(BaseContextMixin, IsStaffUserMixin, CreateView):
form_class = GoodsCategoryForm
template_name = 'goods_category_ajax/create.html'
def get_context_data(self, **kwargs):
if 'to_field' in self.request.GET:
kwargs['to_field'] = self.request.GET['to_field']
return super(GoodsCategoryAjaxCreateView, self).get_context_data(**kwargs)
def form_valid(self, form):
self.object = form.save()
context = {'op': 'create', 'id': self.object.id, 'value': self.object.__str__()}
if 'to_field' in self.request.GET:
context['to_field'] = self.request.GET['to_field']
return TemplateResponse(self.request, 'goods_category_ajax/success.html', context=context)
class GoodsCategoryAjaxUpdateView(BaseContextMixin, IsStaffUserMixin, UpdateView):
model = GoodsCategory
form_class = GoodsCategoryForm
slug_field = 'id'
context_object_name = 'goods_category'
template_name = 'goods_category_ajax/update.html'
def get_context_data(self, **kwargs):
if 'to_field' in self.request.GET:
kwargs['to_field'] = self.request.GET['to_field']
return super(GoodsCategoryAjaxUpdateView, self).get_context_data(**kwargs)
def form_valid(self, form):
self.object = form.save()
context = {'op': 'create', 'id': self.object.id, 'value': self.object.__str__()}
if 'to_field' in self.request.GET:
context['update'] = self.request.GET['to_field']
return TemplateResponse(self.request, 'goods_category_ajax/success.html', context=context)
class GoodsCategoryAjaxLangDeleteView(BaseContextMixin, IsStaffUserMixin, FakeDeleteView):
model = GoodsCategory
slug_field = 'id'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
data = {'op': 'delete', 'id': self.object.id, 'value': self.object.__str__()}
self.object.delete()
return JsonResponse(data=data)
urls.py:
url(r'^ajax/$', GoodsCategoryAjaxCreateView.as_view(), name='goods_category_ajax_create'),
url(r'^ajax/(?P<slug>\d+)/$', GoodsCategoryAjaxUpdateView.as_view(), name='goods_category_ajax_update'),
url(r'^ajax/lang_delete/(?P<slug>\d+)/$', GoodsCategoryAjaxLangDeleteView.as_view(),
name='goods_category_ajax_lang_delete'),
five is your add popup windows will open url handle by GoodsCategoryAjaxCreateView and the return template is:
{% extends "manage/base.html" %}
{% block main %}
<form id='goods_category_ajax_create' class="form-horizontal" enctype="multipart/form-data"
action="{% url 'goods_category_ajax_create' %}{% if to_field %}?to_field={{ to_field }}{% endif %}"
method="post">
{% include 'manage/widgets/form.html' %}
<div class="form-group">
<div class="col-sm-offset-1 col-sm-10">
<input class="layui-btn layui-btn-normal" type="submit" value="add_category"/>
</div>
</div>
</form>
{% endblock %}
you submit a new category with modelform and createview,when the form is_vaild a success.html will be return by TemplateResponse(as you can see in GoodsCategoryAjaxCreateView form_valid),and the point is success.html is nothing but a script that can close popup window and insert new option to the to_field element in parent window.here is success.html:
{% extends "manage/base.html" %}
{% block main %}
<script>
var to_field = '#{{ to_field }}', op = '{{ op }}', id = '{{ id }}', value = '{{ value }}';
if (to_field) {
switch (op) {
case 'create':
if (id) {
var index = parent.layer.getFrameIndex(window.name); //get current iFrame index
parent.layer.close(index); //close
$option = '<option value=' + id + ' selected>' + value + '</option>';
$(to_field, window.parent.document).append($option);
$(to_field + '_change,' + to_field + '_delete', window.parent.document).removeClass('layui-btn-disabled');
}
break;
case 'update':
if (id) {
var index = parent.layer.getFrameIndex(window.name);
parent.layer.close(index);
$(to_field + ' option[value=' + id + ']', window.parent.document).html(value);
}
break;
}
}
</script>
{% endblock %}
So I am new to Django and I have created a View which uses a total of 8 forms dynamically. There is one base form which is always displayed and then there are 7 more forms that are displayed only if user selects that option from a drop down (drop down is in the HTML template).
I am now trying to validate the form fields and when I display all 8 forms statically, the validation (clean methods) for each of the forms work perfectly! However when I change that to dynamically display the forms based on user selection, the validation for the base form fails every time.
Any idea why that could be happening? I could provide instances of the forms/view if that would help!
Template:
<form0 id="BaseForm" action="/test_created/" method="post">
{% csrf_token %}
{{create_test_form.as_ul}} <br><br>
</form0>
<form1 id="vc1Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc1_form.as_ul}} <br><br>
</form1>
<form2 id="vc2Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc2_form.as_ul}} <br><br>
</form2>
<form3 id="vc3Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc3_form.as_ul}} <br><br>
</form3>
<form4 id="vc4Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc4_form.as_ul}} <br><br>
</form4>
<form5 id="vc5Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc5_form.as_ul}} <br><br>
</form5>
<form6 id="vc6Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc6_form.as_ul}} <br><br>
</form6>
<form7 id="vc7Form" action="" method="post" style="display:none">
{% csrf_token %}
{{vc7_form.as_ul}} <br><br>
</form7>
<div>
<select id="validation_classes_id" name="all_validation_classes" onchange="showForm()">
<option value="%">Choose the validation class</option>
{% for val_class in all_validation_classes %}
<option value="{{ val_class.id }}">{{ val_class.id}} {{val_class.name }}</option>
{% endfor %}
</select> <br> <br>
<form id="submit" action="" method="post">
{% csrf_token %}
<input type="submit" value="Submit" onclick=""/>
</form>
</div>
<br><br>
<script>
function showForm(){
if (validation_classes_id.value == 1){
console.log("Chose validation class 1");
var div = document.getElementById('vc1Form');
console.log(div)
div.style.display='block';
}
else if (validation_classes_id.value == 2){
console.log("Chose validation class 2");
var div = document.getElementById('vc2Form');
console.log(div)
div.style.display='block';
}
else if (validation_classes_id.value == 3){
console.log("Chose validation class 3");
var div = document.getElementById('vc3Form');
console.log(div)
div.style.display='block';
}
else if (validation_classes_id.value == 4){
console.log("Chose validation class 4");
var div = document.getElementById('vc4Form');
console.log(div)
div.style.display='block';
}
else if (validation_classes_id.value == 5){
console.log("Chose validation class 5");
var div = document.getElementById('vc5Form');
console.log(div)
div.style.display='block';
}
else if (validation_classes_id.value == 6){
console.log("Chose validation class 6");
var div = document.getElementById('vc6Form');
console.log(div)
div.style.display='block';
}
else if (validation_classes_id.value == 7){
console.log("Chose validation class 7");
var div = document.getElementById('vc7Form');
console.log(div)
div.style.display='block';
}
}
</script>
View:
def create_test(request):
context = {
'all_validation_classes': ValidationClass.objects.all(),
'create_test_form': CreateTestForm,
'vc1_form': VC1Form,
'vc2_form': VC2Form,
'vc3_form': VC3Form,
'vc4_form': VC4Form,
'vc5_form': VC5Form,
'vc6_form': VC6Form,
'vc7_form': VC7Form
}
if request.method == 'POST':
create_test_form = CreateTestForm(request.POST)
vc1_form = VC1Form(request.POST)
vc2_form = VC2Form(request.POST)
vc3_form = VC3Form(request.POST)
vc4_form = VC4Form(request.POST)
vc5_form = VC5Form(request.POST)
vc6_form = VC6Form(request.POST)
vc7_form = VC7Form(request.POST)
if create_test_form.is_valid():
print("This is where I am")
print("Create Test form looks valid")
vc_list = request.POST.getlist('validation_class', None)
selected_vc = ValidationClass.objects.filter(pk__in=vc_list)
global_val_class = selected_vc
if vc1_form.is_valid():
print("VC1Form is valid")
else:
print("Failing at VC1")
return HttpResponseRedirect('/test_not_created/')
if vc2_form.is_valid():
print("VC2Form is valid")
else:
print("Failing at VC2")
return HttpResponseRedirect('/test_not_created/')
if vc3_form.is_valid():
print("VC3Form is valid")
else:
print("Failing at VC3")
return HttpResponseRedirect('/test_not_created/')
if vc4_form.is_valid():
print("VC4Form is valid")
else:
print("Failing at VC4")
return HttpResponseRedirect('/test_not_created/')
if vc5_form.is_valid():
print("VC5Form is valid")
else:
print("Failing at VC5")
return HttpResponseRedirect('/test_not_created/')
if vc6_form.is_valid():
print("VC6Form is valid")
else:
print("Failing at VC6")
return HttpResponseRedirect('/test_not_created/')
if vc7_form.is_valid():
print("VC7Form is valid")
else:
print("Failing at VC7")
return HttpResponseRedirect('/test_not_created/')
return HttpResponseRedirect('/test_created/')
else:
print("Failing at create_test")
return HttpResponseRedirect('/test_not_created/')
else:
create_test_form = CreateTestForm()
vc1_form = VC1Form()
vc2_form = VC2Form()
vc3_form = VC3Form()
vc4_form = VC4Form()
vc5_form = VC5Form()
vc6_form = VC6Form()
vc7_form = VC7Form()
return render (request, 'create_test.html', context)
Forms:
class CreateTestForm(forms.ModelForm):
class Meta:
model = Test
fields = ['name', 'test_group', 'description', 'query_text', 'failure_condition', 'status']
def clean_status(self):
print("This is where I am")
print(self.cleaned_data)
def getKey(self):
return "create_test_form"
class VC1Form(forms.Form):
expected_relation = forms.ChoiceField(choices = [('<','<'), ('>','>'), ('=','='), ('<=','<='), ('>=','>='), ('!=','!=')], required = True, label = 'Expected Relation: ')
num_rows = forms.IntegerField(initial = 0)
def getKey(self):
return "vc1_form"
class VC2Form(forms.Form):
expected_relation = forms.ChoiceField(choices=[('<', '<'), ('>', '>'), ('=', '='), ('<=', '<='), ('>=', '>='), ('!=', '!=')], required=True, label='Expected Relation: ')
comparing_value_type = forms.ChoiceField(choices=[('Integer', 'Integer'), ('String', 'String')], required=True, label='Comparing Value Type')
comparing_value = forms.CharField(initial = 0)
def clean_comparing_value(self):
exp_rel = self.cleaned_data['expected_relation']
input_val_type = self.cleaned_data['comparing_value_type']
input_val = self.cleaned_data['comparing_value']
if (input_val_type == 'Integer'):
print("I am in integer")
try:
int(input_val)
print(int(input_val))
return input_val
except ValueError:
print("Getting a value error")
raise forms.ValidationError('Should be an Integer')
elif (input_val_type == 'String'):
print("I am in string")
if (exp_rel != '!=' and exp_rel != '='):
print("I am in here...")
raise forms.ValidationError('Must have either = or != as comparator for String')
try:
int(input_val)
print(int(input_val))
print("getting a value error")
raise forms.ValidationError('Should be a string')
except ValueError:
print("No value error")
return input_val
def getKey(self):
return "vc2_form"
class VC3Form(forms.Form):
comparing_value_2 = forms.CharField(label = 'Comparing Value', initial=" ") #Need to figure out if its needed to make this into an array?
# Not sure for now if there is a need to validate this data
def getKey(self):
return "vc3_form"
class VC4Form(forms.Form):
# # This is mostly not needed as we will only check the values in the first column of the query results (as per documentation)
wanted_value = forms.CharField(label = 'Name of corresponding column', initial=" ") #Need to figure out how the input will be an actual variable from the select query
acceptable_error_rate = forms.IntegerField(min_value = 0, max_value = 100, initial=0)
def getKey(self):
return "vc4_form"
class VC5Form(forms.Form):
expected_relation_2 = forms.ChoiceField(choices=[('<', '<'), ('>', '>'), ('=', '='), ('<=', '<='), ('>=', '>='), ('!=', '!=')], required=True,label='Expected Relation: ')
comparing_value_type_2 = forms.ChoiceField(choices=[('Integer', 'Integer'), ('String', 'String')], required=True, label='Comparing Value Type')
def clean_comparing_value_type_2(self):
expected_relation_choices = ('<', '>', '=', '<=', '>=', '!=')
exp_rel = self.cleaned_data['expected_relation_2']
input_val_type = self.cleaned_data['comparing_value_type_2']
print("This is the input val type")
print(input_val_type)
if (input_val_type == 'String'):
print("I get in here")
if (exp_rel != '=' and exp_rel != '!='):
raise forms.ValidationError('Must have either = or != as comparator for String')
return exp_rel
def getKey(self):
return "vc5_form"
class VC6Form(forms.Form):
expected_relation_3 = forms.ChoiceField(choices=[('<', '<'), ('>', '>'), ('=', '='), ('<=', '<='), ('>=', '>='), ('!=', '!=')], required=True, label='Expected Relation: ')
def getKey(self):
return "vc6_form"
class VC7Form(forms.Form):
expected_relation_one = forms.ChoiceField(choices=[('<', '<'), ('>', '>'), ('=', '='), ('<=', '<='), ('>=', '>='), ('!=', '!=')], required=True, label='Expected Relation to First Value: ')
comparing_value_one = forms.IntegerField(label = 'First comparing value', initial=0)
expected_relation_two = forms.ChoiceField(choices=[('<', '<'), ('>', '>'), ('=', '='), ('<=', '<='), ('>=', '>='), ('!=', '!=')], required=True, label='Expected Relation to Second Value: ')
comparing_value_two = forms.IntegerField(label = 'Second comparing value', initial=0)
def getKey(self):
return "vc7_form"
I have three forms, forms.py:
class HotelForm(forms.Form):
rooms = forms.IntegerField(label=(u'Rooms'), min_value=1)
class TouristsForm(forms.Form):
adult = forms.IntegerField(label=(u'Adults'), min_value=1, initial=1)
children = forms.IntegerField(label=(u'Children'), min_value=0, initial=0, required=False)
class ChildrenAgeForm(forms.Form):
children_age = forms.IntegerField(label=(u'Children Age'), min_value=2, max_value=10, initial=2, required=False)
That's how i realize formset and validation in views.py:
def bookingForm(request):
TouristsFormSet = formset_factory(TouristsForm, extra = 1, max_num = 15)
ChildrenAgeFormSet = formset_factory(ChildrenAgeForm, extra = 1, max_num = 20)
if request.method == 'POST':
booking_form = HotelForm(request.POST, prefix='booking_form')
tourists_formset = TouristsFormSet(request.POST, prefix='tourists')
childrenage_formset = ChildrenAgeFormSet(request.POST, prefix='childrenage')
if booking_form.is_valid() and tourists_formset.is_valid() and childrenage_formset.is_valid():
rooms = booking_form.cleaned_data['rooms']
for i in range(0, tourists_formset.total_form_count()):
tourists_form = tourists_formset.forms[i]
tourists = tourists_form.cleaned_data
for n in range(0, childrenage_formset.total_form_count()):
childrenage_form = childrenage_formset.forms[n]
childrenage = childrenage_form.cleaned_data
template = get_template("booking/result.html")
context = Context({'tourists_formset':tourists_formset, 'childrenage_formset':childrenage_formset })
html = template.render(context)
return HttpResponse( html )
else:
booking_form = HotelForm()
tourists_formset = TouristsFormSet(prefix='tourists')
childrenage_formset = ChildrenAgeFormSet(prefix='childrenage')
return render(request, 'booking/booking.html', { 'booking_form' : booking_form, 'tourists_formset' : tourists_formset, 'childrenage_formset' : childrenage_formset })
And this is how i realize html file:
{{ tourists_formset.management_form }}
{% for tourists in tourists_formset %}
{{ tourists }}
{% endfor %}
{{ childrenage_formset.management_form }}
{% for childrenage in childrenage_formset %}
{{ childrenage }}
{% endfor %}
Every time when i fill all fields in the form i have an error 'This field is required' for the HotelForm form. I can't understand why it is happen. Thanks for help
You are using a prefix when handling the POST request.
booking_form = HotelForm(request.POST, prefix='booking_form')
You need to use the same prefix for the GET request.
booking_form = HotelForm(prefix='booking_form')