I cannot save my form because the payload is not formatted correctly.. I'm using HTMX for rendering the form in a modal
here's the code:
forms.py
class RecipeForm(forms.ModelForm):
title = forms.CharField(
label="Titolo",
)
method = forms.CharField(
widget=forms.Textarea(),
label="Procedimento",
)
tags = CustomTagsMultipleChoice(
queryset=Tag.objects.all(),
widget=forms.CheckboxSelectMultiple,
label="Tag",
)
hero_image = forms.ImageField(
label="Immagine",
)
class Meta:
model = Recipe
fields = ["title", "method", "tags", "hero_image"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.attrs = {
"hx-post": reverse_lazy("recipes:recipe_create"),
"hx-target": "#dialog",
}
self.fields["tags"].required = False
self.fields["method"].required = False
self.fields["hero_image"].required = False
self.fields["tags"].label = False
self.helper.layout = Layout(
FloatingField("title"),
FloatingField("method", css_class="fl-textarea"),
"hero_image",
InlineCheckboxes("tags"),
Div(
Submit("submit", "Salva"),
Submit("button", "Cancella", css_class="btn btn-danger", data_bs_dismiss="modal"),
css_class="text-end",
),
)
views.py
#login_required
def recipe_create(request):
if request.method == "POST":
form = RecipeForm(request.POST,request.FILES)
if form.is_valid():
form.save()
return HttpResponse(status=204, headers={"HX-Trigger": "recipeSaved"})
form = RecipeForm(request.POST,request.FILES)
context = {"recipe_form": form}
return render(request, "recipes/partials/_recipe_create.html", context)
form = RecipeForm()
context = {"recipe_form": form}
return render(request, "recipes/partials/_recipe_create.html", context)
_recipe_create.html
{% load crispy_forms_tags %}
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Aggiungi una ricetta</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
</div>
<div class="modal-body">
{% crispy recipe_form %}
</div>
</div>
if I print request.POST I get this badly formatted payload
<QueryDict: {'------WebKitFormBoundaryIbg12MlAkITtzU0g\r\nContent-Disposition: form-data; name': ['"submit"\r\n\r\nSalva\r\n------WebKitFormBoundaryIbg12MlAkITtzU0g\r\nContent-Disposition: form-data; name="csrfmiddlewaretoken"\r\n\r\n0DWNbShUV0r8mFSPUNQaSQapC38FQFrMeOtmqFY64H4x3ReOuG7suKqUip7sSpqv\r\n------WebKitFormBoundaryIbg12MlAkITtzU0g\r\nContent-Disposition: form-data; name="title"\r\n\r\ntest\r\n------WebKitFormBoundaryIbg12MlAkITtzU0g\r\nContent-Disposition: form-data; name="method"\r\n\r\n\r\n------WebKitFormBoundaryIbg12MlAkITtzU0g--\r\n']}>
the form is not valid and re-rendered with the title field empty with errors as is a required field
any ideas? I've build another form with the same logic and it's working fine. The only difference is that this one includes an imagefield and so include a enctype="multipart/form-data"
Please help as I'm going mad.. thanks
I expect the form to be validated and saved to the DB
Related
I have a project where one books a room online then is directed to the checkout page where you input a phone which is required in the backend. Code is as below:
forms.py
from django import forms
class AvailabilityForm(forms.Form):
check_in = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M'], required=True)
check_out = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M'], required=True)
class PhoneNoForm(forms.Form):
phone_no = forms.IntegerField(required=True)
views.py
class RoomDetailView(View):
def get(self, request, *args, **kwargs):
category = self.kwargs.get('category', None)
got_category = get_room_category(category)
print(category)
print(got_category)
form = AvailabilityForm()
if got_category is not None:
context = {
'category':got_category,
'form':form
}
return render(request, 'detail.html', context)
else:
return HttpResponse("Category Nyet!")
def post(self, request, *args, **kwargs):
category = self.kwargs.get('category', None)
form = AvailabilityForm(request.POST)
if form.is_valid():
data = form.cleaned_data
available_rooms = get_available_rooms(category, data['check_in'], data['check_out'] )
if available_rooms is not None:
room = available_rooms[0]
context = {
'room':room
}
return render(request, 'booking/checkout.html', context)
else:
return HttpResponse("We're out of those rooms" )
else:
return HttpResponse('Form invlid')
class CheckoutView(TemplateView):
template_name = 'booking/checkout.html'
def get_phone(request):
if request.POST:
phone_no = request.POST.get('PhoneNo', None)
print(phone_no)
cl = MpesaClient()
# Use a Safaricom phone number that you have access to, for you to be able to view the prompt
phone_number = phone_no
amount = 1
account_reference = 'reference'
transaction_desc = 'Description'
callback_url = 'https://darajambili.herokuapp.com/express-payment'
# callback_url = request.build_absolute_uri(reverse('booking:mpesa_stk_push_callback'))
response = cl.stk_push(phone_number, amount, account_reference, transaction_desc, callback_url)
return HttpResponse(response.text)
else:
return HttpResponse('This is else')
checkout.html
<div>
<form action="" method="POST">
{% csrf_token %}
<label for="Input No.">Input Phone No. :</label>
<input type="number" name="id_PhoneNo" id="PhoneNo">
<input type="submit" value="Proceed">
</form>
</div>
detail.html
<form id="booking-form" action="" method="POST">
{% csrf_token %}
<div class="input-div">
<label for="id_check_in">Check In : </label>
<input type="datetime-local" id="id_check_in" name="check_in">
</div>
<div class="input-div">
<label for="id_check_out">Check Out : </label>
<input type="datetime-local" id="id_check_out" name="check_out">
</div>
<div class="input-div">
<button type="submit">Book the Room</button>
</div>
</form>
form.is_valid returns False. I have tried using GET method but i need the phone number for more processing hence my insistence on using POST. If possible, help me fast. Where is the problem? Sorry if it's too much code.
You need to return the errors to the HTML pages to see why the error is occurring. So, if the form is not valid, send that to HTML page by:
def post(self, request, *args, **kwargs):
category = self.kwargs.get('category', None)
form = AvailabilityForm(request.POST)
if form.is_valid():
...
else:
category = self.kwargs.get('category', None)
got_category = get_room_category(category)
context = {
'category':got_category,
'form':form
}
return render(request, 'detail.html', context)
And render the errors like this(as per documentation):
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
FYI, rather than directly subclassing the View, you should use FormView. Here is an example code on how to use:
class RoomDetailView(FormView):
form_class = AvailabilityForm
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
category = self.kwargs.get('category', None)
got_category = get_room_category(category)
if got_category is not None:
context.update({
'category':got_category,
})
return context
else:
raise Http404("Category does not exist")
def form_valid(self, form):
data = form.cleaned_data
available_rooms = get_available_rooms(category, data['check_in'], data['check_out'] )
if available_rooms is not None: # this part should be in a separate view
room = available_rooms[0]
context = {
'room':room
}
return render(request, 'booking/checkout.html', context)
else:
return HttpResponse("We're out of those rooms" )
And thanks in advance for any suggestions. I have been playing around with how to properly implement formsets with a CreateView for a couple of days and I'm stuck. Here is my code.
My Models:
class Team(models.Model):
team_name = models.CharField(max_length=264,null=False,blank=False)
class Player(models.Model):
player_name = models.CharField(max_length=264,null=False,blank=False)
team = models.ForeignKey(Team,null=True,on_delete=models.CASCADE)
My View:
class CreateTeamView(LoginRequiredMixin,CreateView):
model = Team
form_class = CreateTeamForm
template_name = 'create_team.html'
def get_context_data(self, **kwargs):
context = super(CreateTeamView, self).get_context_data(**kwargs)
if self.request.POST:
context['new_player'] = NewPlayerFormSet(self.request.POST)
else:
context['nwe_player'] = NewPlayerFormSet()
return context
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(CreateTeamView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
context = self.get_context_data()
new_player_form = context['new_player']
if new_player_form.is_valid():
self.object = form.save()
new_player_form.instance = self.object
new_player_form.save()
instance = form.save()
else:
return self.render_to_response(self.get_context_data(form=form))
My Forms:
class CreateTeamForm(forms.ModelForm):
class Meta:
model = Team
exclude = []
NewPlayerFormSet = inlineformset_factory(Team, Player, extra=1, fields=['player_name',])
My HTML:
<div="players>
<div="add_players">
{{ new_player.management_form }}
{% for form in new_player %}
{{ form.id }}
{{ form.player_name }}
</div>
</div>
My form is saving one player, but when I try to update the code to save more than one player with the initial CreateView, it only recognizes the first contact. I have overridden the BaseInlineFormset to do validation as shown below....
class NewPlayerFormSet(NewPlayerFormSet,BaseInlineFormSet):
player_name = forms.CharField(required=True,widget=forms.TextInput)
def add_fields(self, form, index):
super(NewPlayerFormSet,self).add_fields(form,index)
form.fields['player_name'].required = False
def clean(self):
super(NewPlayerFormSet, self).clean()
for form in self.forms:
if form.cleaned_data.get('player_name'):
pass
else:
form.add_error('player_name','Player Name is required.')
pass
I'm trying to get the code to save a second contact. I have used tried various JQuery attempts....but am unclear if my problem is with JQuery or mayby my HTML templates? That's where I'm stuck.
I tried to do something like...
$(document).ready(function() {
// Watch for the 'add player' click
$('#add_player').click(function(e) {
e.preventDefault();
$('div.add_player:last').clone().each(function(i) {
$(this).find('input,select').each(function(i) {
// Remove any existing values
$(this).val('');
}).appendTo('div#players');
});
});
And while this works to duplicate the form, the players beyond number 1 are not being saved. Not sure what I'm doing incorrectly.
It would appear there is a JQuery plugin for this, but I'm trying to avoid using it for a number of reasons. Thanks again for any help to point me in the right direction.
This was not easy. I spent about a week trying to piece this all together. Here are all of the parts that I used to finally make it work. I ultimately did wind up using jquery.formset.js from GitHub in my solution. Hope I save someone a week.
class Team(models.Model):
team_name = models.CharField(max_length=264,null=False,blank=False)
class Player(models.Model):
player_name = models.CharField(max_length=264,null=False,blank=False)
team = models.ForeignKey(Team,null=True,on_delete=models.CASCADE)
My Views.py
class CreateTeamView(LoginRequiredMixin,CreateView):
model = Team
form_class = TeamForm
template_name = 'create_team.html'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
player_form = CreatePlayerFormSet()
return self.render_to_response(
self.get_context_data(form=form,
player_form=player_form,
))
def form_valid(self, form, player_form):
self.object = form.save()
player_form.instance = self.object
player_form.save()
instance = form.save()
def form_invalid(self, form, player_form):
return self.render_to_response(
self.get_context_data(form=form,
player_form=player_form,
))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
player_form = CreatePlayerFormSet(self.request.POST)
if (form.is_valid() and player_form.is_valid()):
return self.form_valid(form, player_form)
else:
return self.form_invalid(form, player_form)
My Forms.py
class CreateTeamForm(forms.ModelForm):
class Meta:
model = Team
exclude = [ ]
CreatePlayerFormSet = inlineformset_factory(Team, Player, extra=1, fields=(['player_name'])
My HTML Template: ( Using jquery/jquery.formset.js )
<script src="{% static 'jquery/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$(".inline.{{ player_form.prefix }}").formset({
prefix: "{{ player_form.prefix }}",
})
})
</script>
<form method="POST" enctype="multipart/form-data" id="forms">
{% csrf_token %}
{{ player_form.management_form }}
{{ player_form.non_form_errors }}
{% for form in player_form %}
{{ form.id }}
<div class="inline {{ player_form.prefix }}">
<div class="leftwidth22">
<div class="width52">
<h2 class="floatright23">Player Name - </h2>
</div>
</div>
<div class="rightwidth53">
<h2 class="width70">
{{ form.player_name }}
</h2>
</div>
{% endfor %}
My issue is if I upload from admin an image it works fine but if I want to add an image from my template, it seems that my image does not updload.
It is weird as in the request.Post form, path of my image is present.
I've no message of error...
this is my model:
class Listing(models.Model):
title = models.CharField('Title',max_length=64, blank=False)
description = models.TextField('Descritpion', blank=False)
Creation_date = models.DateField(auto_now_add=True)
enddate= models.DateField('Ending Date', blank=False)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category")
initialBid = models.DecimalField('Bid', max_digits=12, decimal_places=2, blank=False)
photo = ResizedImageField(size=[300, 150], upload_to='images/', default='images/default.jpg')
active = models.BooleanField('Active', default=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="author")
def __str__(self):
return f"{self.id} : {self.title} {self.initialBid} {self.Creation_date}"
def save(self, *args, **kwargs):
# Check how the current ending date is after created date,
d1 = self.Creation_date
d2 = self.enddate
if d2 <= d1:
raise ValueError("End date must be a day after today")
super(Listing, self).save(*args, **kwargs)
my form:
class NewListingForm(forms.ModelForm):
enddate = forms.DateField(label='Date of auction End', widget=forms.DateInput(format = '%d/%m/%Y'),
input_formats=('%d/%m/%Y',))
def __init__(self, *args, **kwargs):
self._newly_created = kwargs.get('instance') is None
self.Creation_date = datetime.now()
super().__init__(*args, **kwargs)
class Meta:
model = Listing
fields = ('title','description','enddate','category','initialBid','photo','active')
my template:
<h2>{% if not form.instance.pk %}Create listing {% else %} Edit {% endif %}</h2>
<form id="create-edit-client" class="form-horizontal" action="" method="post" accept-charset="utf-8" enctype="multipart/form-data">
{% csrf_token %}
{{ form.title|as_crispy_field }}
{{ form.description|as_crispy_field }}
<br>
<div class="row">
<div class="col-3">
{{ form.category|as_crispy_field }}
</div>
<div class="col-2">
{{ form.initialBid|as_crispy_field }}
</div>
</div>
<br>
<div class="row">
<div class="col-3">
{{ form.enddate|as_crispy_field }}
</div>
<div class="col-2">
{{ form.active|as_crispy_field }}
</div>
</div>
<br>
{{ form.photo|as_crispy_field }}
<br>
<br>
</form>
in settings:
STATIC_URL = '/static/'
# gestion ds media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
in urls:
urlpatterns = [
......
]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
my views:
#login_required
def editlisting(request, listing_id):
obj = Listing.objects.filter(pk=listing_id).first()
if obj is None:
return render(request, "errors/404.html", {
"entryTitle": listing_id
})
else:
form = NewListingForm(instance=Listing.objects.get(pk=listing_id))
modification=True # used in template to check if modification are allowed
# read only form if not author
user = request.user.id
if obj.author.id != user:
modification=False
# manage save
if request.method == "POST":
form = NewListingForm(request.POST, instance=Listing.objects.get(pk=listing_id))
if form.is_valid():
form.save()
messages.success(request, 'listing saved') # message for inform user of success - See messages in html file
return HttpResponseRedirect(reverse("index"))
# check if in wishes on get
wishbool=False
wish = Wishing.objects.filter(item=listing_id, follower=request.user.id).first()
if wish is not None:
wishbool = True
return render(request, 'auctions/newListing.html', {
"form": form,
"existing": True,
'title': "Edit Listing",
"wishing": wishbool,
"modification":modification
})
thanks for help
form = NewListingForm(request.POST, request.FILES,instance=Listing.objects.get(pk=listing_id))
for getting the images or files you need pass another request that it's called request.FILES, so:
#login_required
def editlisting(request, listing_id):
obj = Listing.objects.filter(pk=listing_id).first()
if obj is None:
return render(request, "errors/404.html", {
"entryTitle": listing_id
})
else:
form = NewListingForm(instance=Listing.objects.get(pk=listing_id))
modification=True # used in template to check if modification are
allowed
# read only form if not author
user = request.user.id
if obj.author.id != user:
modification=False
# manage save
if request.method == "POST":
form = NewListingForm(request.POST, request.FILES,
instance=Listing.objects.get(pk=listing_id))
if form.is_valid():
form.save()
messages.success(request, 'listing saved') # message for
inform user of success - See messages in html file
return HttpResponseRedirect(reverse("index"))
# check if in wishes on get
wishbool=False
wish = Wishing.objects.filter(item=listing_id,
follower=request.user.id).first()
if wish is not None:
wishbool = True
return render(request, 'auctions/newListing.html', {
"form": form,
"existing": True,
'title': "Edit Listing",
"wishing": wishbool,
"modification":modification
})
I an not sure where is the problem as I press submit button, it went through with no error shown. It was supposed to update the database with the data filled in form then redirect them back to 'search' page (option.html).
models.py:
OptionChoice = (
('A','A'),
('B','B'),
('C','C'),
)
class OptionPlan(models.Model):
option = models.CharField(max_length=200, choices=OptionChoice, default="DEFAULT", blank=True)
...
updated = models.DateField(max_length=20, null=True)
updatedBy = models.CharField(max_length=10, null=True)
urls.py:
app_name = 'Benefits'
urlpatterns = [
path('simple_upload', views.simple_upload, name='simple_upload'),
#path('search', views.search, name='search'),
path('search/', FilterView.as_view(filterset_class=BenefitsFilter, template_name='Benefits/option.html'), name='search'),
path('OptionUpdate/<int:id>', views.OptionUpdate.as_view(), name='OptionUpdate')
]
views.py:
def search(request):
option = OptionPlan.objects.get_queryset()
option_filter = BenefitsFilter(request.GET, queryset=option)
return render(request, 'Benefits/option.html', {'filter':option_filter})
class OptionUpdate(UpdateView):
model = OptionPlan
fields =[
'option',
...
'cb_updatedBy',
'cb_updated',
]
template_name = 'Benefits/OptionUpdate.html'
slug_field = 'id'
slug_url_kwarg = 'id'
def form_valid(self, request, obj, form, change):
OptionPlan = form.save(commit=False)
if OptionPlan.option and 'option' in form.changed_data:
OptionPlan.updatedBy = str(self.request.user)
OptionPlan.updated = timezone.localtime(timezone.now())
OptionPlan.save()
return redirect('Benefits:search')
optionUpdate.html:
{% if user.is_authenticated %}
<div style="margin-left:100px;margin-right:100px;">
<form method="POST">
<div class="row">
<div class="col">
<h2 class="alert alert-primary">...</h2>
{% csrf_token %}
<div class="row">
<div class="col-4" style="font-size:30px;">
{{ form.option|as_crispy_field }}
</div>
</div>
...
<div class="col">
<h2 class="alert alert-success">...</h2>
...
<div class="col" style="font-size:30px;">
{{ form.cb_remarks|as_crispy_field }}
</div>
</div>
<button type="submit" class="btn btn-primary btn-block">2020 Option Form Update</button>
</div>
</form>
</div>
{% else %}
Thank you so much for helping!
Edit:
What version of Django are you using? A cursory look at generic UpdateView* from Django v1.3 through v2.2 use this same post method:
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid(): <--- you passed here so your form is valid
return self.form_valid(form) <-- you overwrote this method (problem area)
else:
return self.form_invalid(form)
*get familiar with that website if you are using class based views, its a life saver.
I am not sure how your form_valid method is being called with four variables: request, obj, form, change when it only expects one variable.
I typically split this logic apart and would make a separate form:
forms.py
class OptionPlanUpdateForm(forms.ModelForm):
class Meta:
model = OptionPlan
fields = [
'option',
'cb_updatedBy',
...
'cb_updated',
]
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
def save(self, **kwargs):
option_plan = super().save(commit=False)
if 'option' in self.changed_data:
option_plan.updatedBy = str(self.user)
option_plan.updated = timezone.localtime(timezone.now())
option_plan.save()
return option_plan
Since we have a form we have to update our view to handle the form. We pass the user to the form through the get_form_kwargs method.
class OptionUpdate(UpdateView):
form_class = OptionPlanUpdateForm
model = OptionPlan
slug_field = 'id'
slug_url_kwarg = 'id'
template_name = 'Benefits/OptionUpdate.html'
success_url = reverse_lazy('Benefits:search')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
***To test for errors in your form you can do the following:
def post(self, request, *args, **kwargs):
form = self.form_class(self.request.POST)
print(form.errors)
return super().post(request, *args, **kwargs)
If I were to assume, the form is throwing some type of error that you aren't printing nor handling. Try checking if any form errors exists to begin with.
Good Evening,
Im having trouble with a crispy forms inlineformset. I have followed guides as per:
https://github.com/timhughes/django-cbv-inline-formset/blob/master/music/views.py
https://django-crispy-forms.readthedocs.io/en/latest/crispy_tag_formsets.html#formsets
EDIT
I think the issue is something to do with the dual submit buttons. the devicemodel form has a button that when pressed produces this error. but there is also a save button as part of the resource helper, when that's submitted I get an empty model form error.
I've added screenshots of what happens when you action each button
and I must be missing something as am getting the error:
['ManagementForm data is missing or has been tampered with']
here is my update view:
class EditDeviceModel(PermissionRequiredMixin, SuccessMessageMixin, UpdateView):
model = DeviceModel
form_class = DeviceModelForm
template_name = "app_settings/base_formset.html"
permission_required = 'config.change_devicemodel'
success_message = 'Device Type "%(model)s" saved successfully'
def get_success_url(self, **kwargs):
return '{}#device_models'.format(reverse("config:config_settings"))
def get_success_message(self, cleaned_data):
return self.success_message % dict(
cleaned_data,
model=self.object.model,
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title']='Edit Device Model'
if self.request.POST:
context['formset'] = DeviceFormSet(self.request.POST, instance=self.object)
else:
context['formset'] = DeviceFormSet(instance=self.object)
context['helper'] = DeviceFormSetHelper()
return context
def form_valid(self, form):
context = self.get_context_data()
formset = context['formset']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
formset.save()
return redirect(self.success_url)
else:
return self.render_to_response(self.get_context_data(form=form))
Here are my forms:
class MonitoredResourceForm(forms.ModelForm):
class Meta:
model = MonitoredResource
fields = ['resource','model']
def __init__(self, *args, **kwargs):
self.is_add = kwargs.pop("is_add", False)
super(MonitoredResourceForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'snmp_resource_form'
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Div(
Div(
Field('model'),
Field('resource', placeholder="Resource"),
css_class='col-lg-3'
),
css_class='row'
),
Div(
Div(
HTML("""<input type="submit" name="submit" value="""),
HTML('"Add' if self.is_add else '"Update' ),
HTML(""" monitored resource" class="btn btn-primary"/>"""),
HTML("""Cancel"""),
HTML("""{% if object %}
<a href="{% url 'config:delete_monitoredresource' object.id %}"
class="btn btn-danger">
Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}"""),
css_class='col-lg-12'
),
css_class='row'
),
)
class DeviceModelForm(forms.ModelForm):
class Meta:
model = DeviceModel
fields = ['model','vendor','device_type','ports','uplink_speed']
def __init__(self, *args, **kwargs):
self.is_add = kwargs.pop("is_add", False)
super(DeviceModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'device_type_form'
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Div(
Div(
Field('model', placeholder="Model"),
Field('vendor',),
Field('device_type',),
Field('ports', placeholder="Ports"),
Field('uplink_speed', placeholder="Uplink Speed"),
css_class='col-lg-6'
),
css_class='row'
),
Div(
Div(
HTML("""<input type="submit" name="submit" value="""),
HTML('"Add' if self.is_add else '"Update' ),
HTML(""" Device Model" class="btn btn-primary"/>"""),
HTML("""Cancel"""),
HTML("""{% if object %}
<a href="{% url 'config:delete_device_model' object.id %}"
class="btn btn-danger">
Delete <i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}"""),
css_class='col-lg-12'
),
css_class='row'
),
)
DeviceFormSet = inlineformset_factory(DeviceModel, MonitoredResource, form=MonitoredResourceForm, extra=1)
class DeviceFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DeviceFormSetHelper, self).__init__(*args, **kwargs)
self.form_method = 'post'
self.render_required_fields = True
self.form_id = 'snmp_resource_form'
self.form_method = 'POST'
self.add_input(Submit("submit", "Save"))
self.layout = Layout(
Div(
Div(
Field('model'),
Field('resource', placeholder="Resource"),
css_class='col-lg-6'
),
css_class='row'
),
)
and in the templates I render:
{% block content %}
{% include "home/form_errors.html" %}
<div class="col-lg-6">
{% crispy form %}
</div>
<div class="col-lg-6">
{% crispy formset helper %}
</div>
<!-- /.row -->
{% endblock %}
is anyone able to see what im missing?
I think you have to render management form in your template, explained here why you need that
Management Form is used by the formset to manage the collection of forms contained in the formset. If you don’t provide this management data, an exception will be raised
add this in view html
{{ DeviceFormSet.management_form }}
You are missing a tag and also {{format.management_form|crispy}}
I guess
Your problem is that each form in a formset has its own management_form. I haven't dealt with this specifically in crispy, but in the general formsets, that was the problem that I had. You have to manually spell out each piece of the formset, either by iteration or hardcoding, and make sure that each has its management_form.
I had the same problem and found the answer in the documentation:
{{ formset.management_form|crispy }}
{% for form in formset %}
{% crispy form %}
{% endfor %}