I am trying to create a page with formset in it. So far I've gotten the following:
forms.py
class ContractorForm(forms.ModelForm):
class Meta:
model = Contractor
fields = [
'first_name', 'last_name', 'email', 'company_name',
]
views.py
class ContractorUpdateView(SimpleLoginRequiredMixin, TemplateView):
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
ContractorFormSet = formset_factory(
Contractor)
contractor_formset = ContractorFormSet()
context = {"contractor_formset": contractor_formset}
return render(
request, "accounts/contractor_form.html", context)
contractor_form.html
<div class="card-body">
<form class="" method="post" action="" id="facility_contractor_form">
{% for form in contractor_formset %}
{% csrf_token %}
{{ form.as_p }}
{% endfor %}
</form>
</div>
<!-- /.card-body -->
<div class="card-footer">
Cancel
<input type="submit" form="facility_contractor_form" value="Save Changes"
class="btn btn-success float-right">
</div>
But when I try to open the page, I'll get the following error:
TypeError at /facility/2/contractor/
Contractor() got an unexpected keyword argument 'auto_id'
Any ideas? I think my using for maybe the wrong idea
Your ContractorUpdateView references the model, while it should reference the form, so it gotta be:
ContractorFormSet = formset_factory(ContractorForm)
Related
I have a form so an user can ask for a loan and it will tell them if it´s approved or not. The problem is not the logic, it´s the submit input that doesn't work. It will not save the form in the database or show me the errors because of the submit input. Maybe is something wrong with the succes_url? I don't know, but here's my code:
views.py:
#don't worry about the logic part of the form, it's just to show how it´s supposed to work
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('Prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
def form_valid(self, form):
user = self.request.user
cliente = Cliente.objects.get(user_id = user.id)
if not cliente.approve_loan(form.cleaned_data.get('loan_total')):
form.add_error(field=None, error='loan not approved')
return self.form_invalid(form)
else:
form.instance.customer_id = cliente
super(LoanRequest, self).form_valid(form)
return render(self.request, 'Prestamos/template/Prestamos/prestamos.html', context={'form': form, 'success_msg': 'loan approved!'})
urls.py:
urlpatterns = [
path('prestamos/', views.LoanRequest.as_view(), name = 'prestamos'),
]
forms.py:
class LoanForm(forms.ModelForm):
class Meta:
model = Prestamo #loan in English
fields = ['loan_type', 'loan_total', 'loan_date']
and the template:
<div class="container">
{%if success_msg%}
<p class="alert alert-success">{{success_msg}}</p>
{%endif%}
<form action="" method="POST">
{%csrf_token%}
{%for field in form%}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{%for error in field.errors%}
<p>{{error}}</p>
{%endfor%}
{%endfor%}
<input type="submit" value="request"></input>
</form>
</div>
models.py:
class Prestamo(models.Model):
loan_id = models.AutoField(primary_key=True)
loan_type = models.CharField(max_length=20,
choices = [('PERSONAL', 'PERSONAL'), ('HIPOTECARIO', 'HIPOTECARIO'), ('PRENDARIO', 'PRENDARIO')])
loan_date = models.DateField()
loan_total = models.IntegerField()
customer_id = models.IntegerField()
class Meta:
db_table = 'prestamo'
Well, <input> is an empty tag, it does not contain anything, so don't close it.
Additionally, I'd recommend you to make gaps between template tags, like it should be {% endfor %} not {%endfor%}.
Also, remove the empty action attribute from form, as Django always take current page route if not mentioned or empty string.
Also use novalidate on form for rendering custom errors.
Try this template:
<div class="container">
{% if success_msg %}
<p class="alert alert-success">{{success_msg}}</p>
{% endif %}
<form method="POST" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{% for error in field.errors %}
<p>{{error}}</p>
{% endfor %}
{% endfor %}
<input type="submit" value="request">
</form>
</div>
Edit:
One mistake I could see the name for the view is prestamos and you have mentioned it as Prestamos, which is wrong.
So:
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
I have a form to perform searching. As I am using the primary key to search, The searching process is completed successfully BUT I used to get an error below the text field saying Invoice number already exists. I did some tweaks and stopped the form from showing errors but the text field still has a red outline whenever I perform the searching operation. How can I stop the form from doing that?
The code in the forms.py that disabled the form to show field errors:
class InvoiceSearchForm(forms.ModelForm):
generate_invoice = forms.BooleanField(required=False)
class Meta:
model = Invoice
fields = ['invoice_number', 'name','generate_invoice']
def __init__(self, *args, **kwargs):
super(InvoiceSearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_show_errors = False
self.helper.error_text_inline = False
self.form_error_title=False
The HTML code that deals with the search operation:
<div class="myForm">
<form method='POST' action=''>{% csrf_token %}
<div class="row">
<div class='col-sm-12'>
<div class="form-row">
<div class="form-group col-md-3">
{{ form.invoice_number|as_crispy_field }}
</div>
<div class="form-group col-md-3">
{{ form.name|as_crispy_field }}
</div>
<div class="form-group col-md-3">
{{ form.generate_invoice|as_crispy_field }}
</div>
<div class="form-group col-md-3">
<br>
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
</div>
</div>
</form>
</div>
The views.py related to the search operation:
#login_required
def list_invoice(request):
title = 'List of Invoices'
queryset = Invoice.objects.all()
form = InvoiceSearchForm(request.POST or None)
context = {
"title": title,
"queryset": queryset,
"form":form,
}
if request.method == 'POST':
queryset = Invoice.objects.filter(invoice_number__icontains=form['invoice_number'].value(),name__icontains=form['name'].value())
context = {
"form": form,
"title": title,
"queryset": queryset,
}
return render(request, "list_invoice.html", context)
The red outline of the textbox that I get after performing search operation->
I think you need to use forms.Form instead of forms.ModelForm which is designed for creating and updating Model instances.
I'm trying to create a frontend data entry page for an existing model. However, when clicking the link, I get an error:
crispy_forms.exceptions.CrispyError: |as_crispy_field got passed an invalid or inexistent field
Just to be clear, adding the data from Django Admin works with no issues at all.
Having looked through a number of answered questions here, one did highlight what I believe could be problem, but it was out of context and did not provide much of an explanation.
I am trying to create a frontend entry form for users that corresponds with a foreign key.
models.py
class NewHandoff(models.Model):
handoff_pk = models.AutoField(primary_key=True)
handoff_date = models.DateField(auto_now_add=True,verbose_name="Handoff Date")
shift1_pri = models.ForeignKey(Engineer,on_delete=models.CASCADE,verbose_name="Shift 1 Primary")
shift1_sec = models.ForeignKey(Engineer,on_delete=models.CASCADE,verbose_name="Shift 1 Secondary")
def __str__(self):
return f"{self.handoff_date}"
class Meta:
verbose_name_plural = 'Handoffs'
# New Handoff Form
class NewHandoffForm(forms.ModelForm):
class Meta:
model = NewHandoff
fields = ['shift1_pri','shift1_sec']
views.py
from django.shortcuts import redirect, render
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http.response import HttpResponse
from django.contrib import messages
from .models import AttentionForm, NewHandoffForm
# Handoff View Page
class NewHandoffView(LoginRequiredMixin,View):
def get(self, request):
greeting = {}
greeting['heading'] = "New Handoff"
greeting['pageview'] = "Handoff"
return render (request,'handoff/handoff-new.html')
def post(self, request):
if request.method == "POST":
if "add-new-handoff-button" in request.POST:
create_new_handoff_form = NewHandoffForm(request.POST)
create_new_handoff_form.save()
return redirect("/handoff/handoff-create")
handoff-new.html
{% extends 'partials/base.html' %}
{% load static %}
{% load humanize %}
{% load crispy_forms_tags %}
{% block extra_css %}
<link href="{% static 'libs/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet">
{% endblock %}
{% block contents %}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<!-- New Form -->
<form method="POST">
{% csrf_token %}
<div class="row">
<div class="row-fluid pb-1">
<!-- Field 1 -->
<div class="mb-3">
{{ form.shift1_pri|as_crispy_field }}
</div>
<!-- End of Field 1 -->
</div>
</div>
<div class="d-flex flex-wrap gap-2">
<button type="submit" class="btn btn-primary waves-effect waves-light" name="add-new-handoff-button">Create New Handoff</button>
</div>
</form>
<!-- End of New Form -->
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_javascript %}
{% endblock %}
Someone mentioned in another post that forms should correlate with the declared form name {{ form.shift1_mod|as_crispy_field }} so it should actually be {{ create_new_handoff_form.shift1_mod|as_crispy_field }} but I have tried changing this and still get the same problem, plus, another model form works fine with just form despite the name of the form being attention_form.
Does anyone have any idea or can point me in the right direction? :)
You are not passing the form through the context in the template. As you are inheriting View, Add the following line in the get() and afterwards in the post() method appropriately:
form = NewHandoffForm()
# and then change return
return render(request,'handoff/handoff-new.html', {'form': form })
Also, you have a space after render in the get function. I hope this is a typo here, but not in your code.
I don't know how to pass values from url to the UpdateView. Then I need to pass that value to my html page.
Corresponding line in urls.py,
url(r'^poi/edit/(?P<pk>\d+)', POIFormUpdateView.as_view(), name='POIform-edit')
views.py
class POIFormUpdateView(UpdateView):
model = PointOfInterest
fields = ['name', 'vip', 'category', 'place', 'latitude', 'longitude', 'picture', 'website', 'description', 'phone', 'email']
template_name_suffix = '_update_form'
def get(self, request, *args, **kwargs):
print 'On POI get method'
print kwargs
super(POIFormUpdateView, self).get(request, *args, **kwargs)
Here I tried overriding the get method, and kwargs prints the expected dictionary but I don't know how to pass that dict to my update_form.html file.
update_form.html,
<form role="form" method="POST" action="/poi/edit/{{ pk }}" class="post-form form-horizontal">{% csrf_token %}
<!-- customizing form -->
{{ form|crispy }}
<!-- End of customization -->
<button type="submit" class="save btn btn-default btn-primary center-block">Save</button>
</form>
I have tried also by getting url from name like,
<form role="form" method="POST" action="{% url 'POIform-edit' %}"
But this also won't work.
Add argument object ID to the url:
<form role="form" method="POST" action="/poi/edit/{{ object.pk }}/" class="post-form form-horizontal">
ClassBasedView passes model_name as context_object, it should become:
<form role="form" method="POST" action="/poi/edit/{{ pointofinterest.pk }}/" class="post-form form-horizontal">
I use <input type='hidden' name='***' value="/poi/edit/{{ pk }}" /> to pass this value to the request. Then in def get(): you can get this using something like myurl=request.POST.get('***')
Form.is_valid() always returns false. Here's my code.
#urls.py
url(r'^create/', "app.views.createpost", name="createpost"),
My models.py
class Post(models.Model):
"""docstring for Post"""
post_image = AjaxImageField(upload_to='posts', max_width=200, max_height=200, crop=True, null= False, default='site_media/media/BobMarley/bob1.jpg')
poster = models.ForeignKey(User, null= False, default=User.objects.get(username="admin")
Here's my forms.py
#forms.py
class AjaxImageUploadForm(forms.Form):
image = forms.URLField(widget=AjaxImageWidget(upload_to='posts'))
view.py
#views.py
def createpost(request):
if request.method == 'POST':
form = AjaxImageUploadForm(request.POST, request.FILES)
if form.is_valid():
newpost = Post(post_image = request.FILES['image'])
newpost.poster = request.user
newpost.save()
return HttpResponseRedirect('/create/')
else:
form = AjaxImageUploadForm() # An empty, unbound form
posts = Post.objects.all()
return render_to_response('create.html',{'posts': posts, 'form': form},context_instance=RequestContext(request))
The Template
#create.html
{% block extra_head %}
{{ form.media }}
{% endblock %}
{% block body %}
<form method="POST" enctype="multipart/form-data" action="{% url "createpost" %}">
{% csrf_token %}
{{ form.errors}}
{{ form.as_p }}
<button type="submit" name='image'>Upload</button>
</form>
{% endblock %}
The form is never valid and the error printed is "This field is required."
And this is the form widget begin created
<tr><th><label for="id_image">Image:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul>
<div class="ajaximage"><a class="file-link" target="_blank" href=""> <img class="file-img" src=""></a> <a class="file-remove" href="#remove">Remove</a>
<input class="file-path" type="hidden" value="" id="id_image" name="image" /> <input type="file" class="file-input" name="image"/> <input class="file-dest" type="hidden" name="image" value="/ajaximage/upload/posts/0/0/0"> <div class="progress progress-striped active"> <div class="bar"></div></div>
</div>
</td></tr>
I am using the django package called django-ajaximage and their custom widget (AjaxImageWidget) is probably a custom text widget
class AjaxImageWidget(widgets.TextInput):
Thank you for you help
You do not need the init on your model class. Also, null=False is the default. I'm not sure the text 'admin' will default to the user with username admin. You would need to do:
default=User.objects.get(username="admin")
You can also include the poster assignment in the creation of the object like so:
newpost = Post(post_image=request.FILES['image'], poster=request.user)
To make the form validate you need to put blank=True in the field like so:
poster = models.ForeignKey(User, blank=True, default=User.objects.get(username="admin")
I would also make the form be a ModelForm:
class AjaxImageUploadForm(forms.ModelForm):
class Meta:
model=Post
exclude=['poster',]