Django Forms - DateInput not populating from instance - python

I'm trying to set up an edit form for a Django model which includes a DateField. I've set this field as a forms.DateInput in forms.py. This works fine for creating a new instance of the model, but when I try to populate the form with an existing instance the DateInput field remains blank even though all of the other fields are populated correctly.
If I revert to the default TextField input then the data is recalled correctly. I've also tried to set a format in the DateInput widget.
models.py
class Rider(models.Model):
first_name = models.CharField(max_length=40)
surname = models.CharField(max_length=40)
MALE = 'M'
FEMALE = 'F'
GENDER_CHOICES = [
(MALE, 'Male'),
(FEMALE, 'Female'),
]
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
dob = models.DateField("Date of Birth", auto_now = False, auto_now_add = False)
club = models.CharField(max_length=50,blank=True, null=True)
bc_number = models.IntegerField("British Cycling Membership Number", blank=True, null=True)
linked_account = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
views.py
def rider_edit(request, pk):
rider = get_object_or_404(Rider, pk=pk)
if request.method == "POST":
form = RiderForm(request.POST, prefix='rider', instance=rider)
if form.is_valid():
rider = form.save(commit=False)
rider.linked_account = request.user
rider.save()
return redirect('rider_list')
else:
form = RiderForm(prefix='rider', instance=rider)
return render(request, 'riders/rider_new.html', {'riderform': form})
form.py
from django import forms
from .models import Rider, MedicalInfo
class RiderForm(forms.ModelForm):
class Meta:
model = Rider
fields = ('first_name', 'surname', 'gender', 'dob', 'club', 'bc_number')
widgets= {
'dob': forms.DateInput(attrs={'type': 'date'}, format='%d/%m/%Y')
}
rider_new.html
<h2>New Rider</h2>
<form method="POST" class="post-form">
{% csrf_token %}
{{ riderform.as_p }}
<button type="submit" class="save btn btn-default">Add Rider</button>
</form>
The dob field is the only field that doesn't populate correctly from the database, it should show a date in the format dd/mm/YYYY e.g. "10/09/2010". It's actually showing the default "dd/mm/YYYY".

I found a solution to the problem, the date from the database was being returned in "%d/%m/%Y" format but the input on the form was of the "date" type which expects an input in the format "%Y-%m-%d", by changing:
widgets= {
'dob': forms.DateInput(format=('%d/%m/%Y'), attrs={'class':'form-control', 'placeholder':'Select Date','type': 'date'})
}
to:
widgets= {
'dob': forms.DateInput(format=('%Y-%m-%d'), attrs={'class':'form-control', 'placeholder':'Select Date','type': 'date'})
}
Therefore, the generated HTML form changed from:
<input type="date" name="rider-dob" value="10/09/2010" class="form-control" placeholder="Select Date" required="" id="id_rider-dob">
to:
<input type="date" name="rider-dob" value="2010-09-10" class="form-control" placeholder="Select Date" required="" id="id_rider-dob">

Related

Django form not saving - cannot identify problem with code

Trying to save the below form, but it's not saving.
The traceback doesn't identify any problem, and print(form.errors) doesnt not return any issue.
I have the exact same code working for another page. So I am not sure what I am missing here. There has to be a typo, I cannot find it.
You will notice that I have an autocomplete function in my template. I initially thought autocomplete was not returning data in all the fields, so also tried to input data manually but not luck either.
Also tried with and without is_ajax but same result.
models.py:
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, null=True, blank=True)
address = models.CharField(verbose_name="Address",max_length=100, null=True, blank=True)
town = models.CharField(verbose_name="Town",max_length=100, null=True, blank=True)
county = models.CharField(verbose_name="County",max_length=100, null=True, blank=True)
post_code = models.CharField(verbose_name="Post Code",max_length=8, null=True, blank=True)
country_1 = models.CharField(verbose_name="Country1",max_length=100, null=True, blank=True)
country_2 = models.CharField(verbose_name="Country2",max_length=100, null=True, blank=True)
longitude = models.CharField(verbose_name="Longitude",max_length=50, null=True, blank=True)
latitude = models.CharField(verbose_name="Latitude",max_length=50, null=True, blank=True)
phone = models.CharField(max_length=120)
web = models.URLField('Website Address')
email_address = models.EmailField('Venue Email Address')
def __str__(self):
return str(self.name) if self.name else ''
forms.py:
class VenueForm(ModelForm):
name = forms.CharField(max_length=100, required=True, widget = forms.TextInput(attrs={'id':"name"}))
address = forms.CharField(max_length=100, widget = forms.TextInput(attrs={'id':"address"}))
town = forms.CharField(max_length=100, required=True, widget = forms.TextInput(attrs={'id':"town"}))
county = forms.CharField(max_length=100, required=True, widget = forms.TextInput(attrs={'id':"county"}))
post_code = forms.CharField(max_length=8, required=True, widget = forms.TextInput(attrs={'id':"post_code"}))
country_1 = forms.CharField(max_length=40, required=True, widget = forms.TextInput(attrs={'id':"country_1"}))
country_2 = forms.CharField(max_length=40, required=True, widget = forms.TextInput(attrs={'id':"country_2"}))
longitude = forms.CharField(max_length=50, required=False, widget = forms.TextInput(attrs={'id':"longitude"}))
latitude = forms.CharField(max_length=50, required=False, widget = forms.TextInput(attrs={'id':"latitude"}))
phone = forms.CharField(max_length=120,required=False)
web = forms.URLField(max_length=120,required=False)
email_address = forms.CharField(max_length=120, required=False)
class Meta:
model = Venue
fields = ['name', 'address', 'town', 'county', 'post_code','country_1','country_2','longitude','latitude','web', 'phone', 'email_address']
views.py:
def google_api_test(request):
submitted = False
form = VenueForm()
if is_ajax(request) and request.method =="POST":
form = VenueForm(data = request.POST)
if form.is_valid():
form.save()
messages.success(request,("Success!"))
return redirect('home')
else :
print(form.errors)
context = {
'form' : form,
'submitted' : submitted,
'google_api_key' : settings.GOOGLE_API_KEY,
'base_country' : settings.BASE_COUNTRY,
}
return render(request,"main/google_api_test.html", context)
html file:
<div class = "form-group">
<input id="autocomplete" style ="width: 500px "placeholder="Enter your address">
<form id="venueform" method="POST" action="">
{% csrf_token %}
{{ form|as_crispy_errors }}
<td class="label">Name (Name)</td>
{{form.name| as_crispy_field}}
</br>
<td class="label">Street Number + Street Name (Address)</td>
{{form.address| as_crispy_field}}
</br>
<td class="label">Town (town)</td>
{{form.town | as_crispy_field}}
</br>
<td class="label">Country(country_1)</td>
{{form.country_1| as_crispy_field}}
</br>
<td class="label">Post Code (post_code)</td>
{{form.post_code| as_crispy_field}}
</br>
<td class="label">Country(country_2)</td>
{{form.country_2| as_crispy_field}}
</br>
<td class="label">Country(longitude)</td>
{{form.longitude| as_crispy_field}}
</br>
<td class="label">Country(latitude)</td>
{{form.latitude| as_crispy_field}}
</br>
<td class="label">phone(phone)</td>
{{form.phone| as_crispy_field}}
</br>
<td class="label">web(web)</td>
{{form.web| as_crispy_field}}
</br>
<td class="label">email_address(email_address)</td>
{{form.email_address| as_crispy_field}}
</br>
<button type="submit" value="Submit" id="profile-btn">Validate</button>
</form>
</div>
Firstly, I'd recommend you to check whether the form even goes to the POST method of google_api_view or not since you mentioned form.errors prints nothing.
Remove the empty action="" from the html form, since Django always takes current page route or url, so it is unnecessary.
Also suggest not to use is_ajax(request) only for one time to check(I think it's depreciated).
So, try this view:
def google_api_test(request):
submitted = False
form="" # for the error - local variable 'form' referenced before assignment if occurs.
if request.method == 'POST':
print("POST method") # check it prints it or not.
form = VenueForm(request.POST)
if form.is_valid():
form.save()
messages.success(request,"Success!")
return redirect('home')
else:
print(form.errors)
else: # GET request
form = VenueForm()
context = {
'form' : form,
'submitted' : submitted,
'google_api_key' : settings.GOOGLE_API_KEY,
'base_country' : settings.BASE_COUNTRY,
}
return render(request,"main/google_api_test.html", context)
Also share Ajax and jQuery code.
county is missing in your template. It's weird that your print(form.errors) did not show up in your terminal - it should have
def google_api_test(request):
submitted = False
if is_ajax(request) and request.method == 'POST':
print(request.POST) # Check--Are you getting form data
form = VenueForm(data = request.POST)
if form.is_valid():
form.save()
messages.success(request,("Success!"))
return redirect('home')
else :
print(form.errors)
else:
form = VenueForm() #Changes in here.
context = {
'form' : form,
'submitted' : submitted,
'google_api_key' : settings.GOOGLE_API_KEY,
'base_country' : settings.BASE_COUNTRY,
}
return render(request,"main/google_api_test.html", context)

Django Models - Form saves but model is blank

Not sure my title is fully representing my problem.
I thought I would put a screenshot of the problem (admin panel), so it's clearer for everyone
It looks like the form is savings, but nothing goes inside.
Here is the models code:
class Venue(models.Model):
name = models.CharField(verbose_name="Name",max_length=100, null=True, blank=True)
address = models.CharField(verbose_name="Address",max_length=100, null=True, blank=True)
town = models.CharField(verbose_name="Town",max_length=100, null=True, blank=True)
county = models.CharField(verbose_name="County",max_length=100, null=True, blank=True)
post_code = models.CharField(verbose_name="Post Code",max_length=8, null=True, blank=True)
country = models.CharField(verbose_name="Country",max_length=100, null=True, blank=True)
longitude = models.CharField(verbose_name="Longitude",max_length=50, null=True, blank=True)
latitude = models.CharField(verbose_name="Latitude",max_length=50, null=True, blank=True)
city = models.CharField(max_length=120)
def __str__(self):
return str(self.name) if self.name else ''
Obviously, I am aware I have asked to return '' if self.name wasnt there. The reason why I did it, is because initially, the models was visible on the admin panel under "-" but was throwing an error message when clicking on it.
Considering I am working with a form, here is the form code:
class VenueForm(forms.ModelForm):
name = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
address = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
town = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
county = forms.CharField(max_length=100, required=True, widget = forms.HiddenInput())
post_code = forms.CharField(max_length=8, required=True, widget = forms.HiddenInput())
country = forms.CharField(max_length=40, required=True, widget = forms.HiddenInput())
longitude = forms.CharField(max_length=50, required=True, widget = forms.HiddenInput())
latitude = forms.CharField(max_length=50, required=True, widget = forms.HiddenInput())
phone = forms.CharField(max_length=120)
web = forms.URLField(max_length=120)
email_address = forms.CharField(max_length=120)
class Meta:
model = Venue
fields = ['name', 'address', 'town', 'county', 'post_code','country','post_code','latitude','city', 'web', 'phone', 'email_address']
the views
def add_venue(request):
submitted = False
form = VenueForm()
if is_ajax(request) and request.method =="POST":
form = VenueForm(data = request.POST)
if form.is_valid():
form.save()
messages.success(request,("Success!"))
return HttpResponseRedirect('/add_venue?submitted=True')
context = {
'form' : form,
'submitted' : submitted,
'google_api_key' : settings.GOOGLE_API_KEY,
'base_country' : settings.BASE_COUNTRY,
}
return render(request,"main/add_venue.html",context)
and finally the html file
<div class="form-group">
<input type="text" placeholder="*Begin typing address" id="id-google-address" name="google_address">
<form id="venueform" method="POST" action="{% url 'add_venue'%}">
{% csrf_token %}
<label for="name" class="hidden-el" hidden>Name</label>
{{form.name}}
<label for="address" class="hidden-el" hidden>Address</label>
{{form.address}}
<label for="town" class="hidden-el" hidden>Town/City</label>
{{form.town}}
<label for="county" class="hidden-el" hidden>County</label>
{{form.county}}
<label for="post_code" class="hidden-el" hidden>Postal Code</label>
{{form.post_code}}
<label for="country" class="hidden-el" hidden>Country</label>
{{form.country}}
<label for="longitude" class="hidden-el" hidden>Longitude</label>
{{form.longitude}}
<label for="latitude" class="hidden-el" hidden>Latitude</label>
{{form.latitude}}
<h4>Phone</h4>
<label for="phone" class="hidden-el" hidden>Phone</label>
{{form.phone}}
<h4>WebSite</h4>
<label for="web" class="hidden-el" hidden>Website</label>
{{form.web}}
<h4>Email Address</h4>
<label for="email_address" class="hidden-el" hidden>Email Address</label>
{{form.email_address}}
<button type = "submit" class="btn btn-secondary">Add Venue</button>
</form>
</div>
{% endblock %}
{% block extend_footer %}
<script type="text/javascript">
var google_api_key = "{{google_api_key|safe}}";
var base_country = "{{base_country|safe}}";
</script>
<script src="{% static 'google_places.js' %}"></script>
{% endblock %}
def add_venue(request):
submitted = False
form = VenueForm()
venue = Venue.objects.create() # <- This line is the issue.
You are creating an empty venue, and not going anything with the variable.
Then, you are saving the form
data = form.save(commit = False)
data.name = form.cleaned_data['name']
data.address = form.cleaned_data['address']
data.town = form.cleaned_data['town']
data.county = form.cleaned_data['county']
data.post_code = form.cleaned_data['post_code']
data.country = form.cleaned_data['country']
data.longitude = form.cleaned_data['longitude']
data.latitude = form.cleaned_data['latitude']
data.city = form.cleaned_data['city']
data.phone = form.cleaned_data['phone']
data.web = form.cleaned_data['web']
data.email_address = form.cleaned_data['email_address']
data.save()
Without having validated the form first.
You need to call form.is_valid() before any of this.
Then, you are validating the form, and saving the form again.
You are trying a single object, 3 times. 2 of which are executed wrong.
Then, you are not instantiating your form in the
else:
form = VenueForm
if 'submitted' in request.GET:
submitted = True
And on top of that, you should return the form POSTed, so you can send back the form.errors().
else:
form = VenueForm()
if 'submitted' in request.GET:
submitted = True
If this is a ModelForm, all that is needed is:
def add_venue(request):
submitted = False
form = VenueForm()
if is_ajax(request) and request.method =="POST":
form = VenueForm(data = request.POST)
if form.is_valid():
form.save()
messages.success(request,("Success!"))
return HttpResponseRedirect('/add_venue?submitted=True')
context = {
'form' : form,
'submitted' : submitted,
'google_api_key' : settings.GOOGLE_API_KEY,
'base_country' : settings.BASE_COUNTRY,
}
return render(request,"main/add_venue.html",context)

Automatically filled form in class-based view

I want to automatically fill some form in class-based view and save in database.
post function in my view
def post(self, request, *args, **kwargs):
form_2 = self.form_class_2(self.request.POST)
if form_2.is_valid():
keyword = form_2.cleaned_data['keyword']
books = self.search(keyword)
if books:
for book in books:
title = self.add_title(book)
form = self.form_class(initial={"title": title})
if form.is_valid():
form.save()
else:
return HttpResponseRedirect(reverse_lazy('add_books'))
return HttpResponseRedirect(reverse_lazy('import_books'))
else:
return HttpResponseRedirect(reverse_lazy('index_books'))
return reverse_lazy('import_books')
my form
class BookForm(forms.ModelForm):
class Meta:
model = Book
exclude = ()
my form_2
class SearchBookForm(forms.Form):
keyword = forms.CharField(max_length=100)
my model
class Book(models.Model):
title = models.CharField(
max_length=75,
verbose_name='Book title')
published_date = models.CharField(
max_length=10,
validators=[check_if_value_is_date, max_year_validator],
blank=True,
null=True,
verbose_name='Publishing date')
pages = models.IntegerField(
validators=[check_if_value_is_negative],
blank=True,
null=True,
verbose_name='Number of pages')
language = models.CharField(
max_length=2,
blank=True,
null=True,
verbose_name='Language')
And this is how my form looks before validation:
<tr><th><label for="id_title">Book title:</label></th><td><input type="text" name="title" value="Harry Potter i Kamień F
ilozoficzny" maxlength="75" required id="id_title"></td></tr>
<tr><th><label for="id_published_date">Publishing date:</label></th><td><input type="text" name="published_date" maxleng
th="10" id="id_published_date"></td></tr>
<tr><th><label for="id_pages">Number of pages:</label></th><td><input type="number" name="pages" id="id_pages"></td></tr
>
<tr><th><label for="id_language">Language:</label></th><td><input type="text" name="language" maxlength="2" id="id_langu
age"></td></tr>
Basically I have 2 forms. form 2 is used to input value which is used as argument in my search function. then this search function return .json, then i took some value from this .json and assign to "title" then this title is my initial data for form. And everything works fine until part with validation. My form isn't valid but when I print my form before validation part I see that my initial data is in form as expected.
Django by default requires Your fields in the form to be filled out. So BookForm requires that you have title, published_date, pages, and language filled out in the form. You instantiate form without passing actual input. Yes you pass in the initial, but you don't pass in request.POST to it (according to what's here). So your form instantiation should look like
form = self.form_class({'title': title, 'published_date': book.published_date, 'pages': book.pages, 'language': book.language})

How do I add an option in django model choice through template

I am Learning Django and i need to allow users of the app to be able to add more options to the item_name field through the template but i don't have an idea on how to achieve that. Thanks for the help.
Here is my model
class ItStore(models.Model):
type_choice = (
('Printer Catridge', 'Printer Catridge'),
('UPS', 'UPS'),
('UPS Battery', 'UPS Battery'),
('Mouse', 'Mouse'),
('Keyboard', 'Keyboard'),
)
item_name = models.CharField(max_length='100', blank=True, null=False, choices=type_choice)
quantity = models.IntegerField(default='', blank=True, null=False)
Here is my View
def itstore_create(request):
form = ItStoreCreateForm(request.POST or None)
submit = "Create IT Store Items"
if form.is_valid():
instance = form.save(commit=False)
instance.save()
message = instance.item_name + " Successfully Created"
messages.success(request, message)
return redirect("items:itstore_list")
context = {
"form": form,
"title": "CREATE ITEM",
}
return render(request, "store_form.html", context)
Here is my form
class ItStoreCreateForm(forms.ModelForm):
class Meta:
model = ItStore
fields = ['item_name', 'quantity']
You could not define choices= on your model. But instead define a list of default choices outside of the model.
my_choices = (
"foo",
"bar",
"pop",
)
class MyModel(models.Model):
my_field = models.CharField(max_length=100)
Then in your view you'd want to import that tuple and pass it to you template:
from my_app.models import my_choices
def my_view(request, *a, **kw):
# view logic
return render(request, "path/to/my/template", choices=my_choices)
Then in your template you can have a select box with the default choices and string values. And also have an optional input type=text that will save to that field if populated.
Something like:
<select name="my_field">
<option value="" selected="selected">-----</option>
{% for choice in choices %}
<option value="{{ choice }}">{{ choice }}</option>
{% endfor %}
</select>
Will give you default choices. Then add an input with the same name, this will act as an optional new choice.
<input type="text" name="my_field"/>
Optionally you could write javascript logic that will ensure only the selectbox or the textfield gets submitted.

Django forms extra empty radio button

In rendering a model form an extra radio button is produced and I don't know where it's coming from:
>>> f = DocumentForm()
>>> print f['document_type']
<ul id="id_document_type">
<li><label for="id_document_type_0"><input checked="checked" id="id_document_type_0" name="document_type" type="radio" value="" /> ---------</label></li>
<li><label for="id_document_type_1"><input id="id_document_type_1" name="document_type" type="radio" value="1" /> Campus LAN</label></li>
<li><label for="id_document_type_2"><input id="id_document_type_2" name="document_type" type="radio" value="2" /> WAN</label></li>
<li><label for="id_document_type_3"><input id="id_document_type_3" name="document_type" type="radio" value="3" /> UC</label></li>
</ul>
That first radio button with value="" and the text as ---------, I've scoured my code and can't work out where it originates from?
models.py
class DocumentType(models.Model):
name = models.CharField("Document Type", max_length=240)
class Document(models.Model):
document_type = models.ForeignKey(DocumentType,
verbose_name="Document Type")
>>> DocumentType.objects.all()
[<DocumentType: Campus LAN>, <DocumentType: WAN>, <DocumentType: UC>]
>>> d = Document.objects.all()
>>> for x in d:
... print x.document_type
...
Campus LAN
Campus LAN
template:
<form role="form" action="" method="POST">{% csrf_token %}
{{ form.as_p}}
<input type="submit" value="Save" />
</form>
forms.py:
class DocumentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['sections'].queryset = Section.objects.filter(associated_document="Original Section")
self.fields['document_type'].queryset = DocumentType.objects.all()
class Meta:
model = Document
fields = ('customer', 'title', 'document_type', 'sections',)
widgets = {
'sections': forms.widgets.CheckboxSelectMultiple,
'document_type': forms.widgets.RadioSelect,
}
views.py
def new_lld(request):
if request.method == "POST":
form = DocumentForm(request.POST)
if form.is_valid():
document = form.save(commit=False)
document.author = request.user
document.save()
form.save_m2m()
return redirect('lld:index')
else:
form = DocumentForm()
return render(request, 'lld/new_lld.html', {'form': form})
admin.py
class DocumentAdmin(admin.ModelAdmin):
fieldsets = [
('Document Info', {'fields': ['author', 'customer', 'title',
'slug']}),
('Document Type', {'fields': ['document_type', 'sections']}),
]
inlines = [VersionInline]
prepopulated_fields = {"slug": ("customer", "title",)}
list_display = ('title', 'customer', 'author', 'document_type',
'date_created', 'date_updated')
list_filter = ['date_updated', 'author']
Here we go:
https://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.ModelChoiceField.empty_label
from here:
Django CheckboxSelectMultiple widget adds --------- value to query set
therefore:
self.fields['document_type'].empty_label = None
does the trick.
A work around is to hide it with css:
#id_document_type li:first-child {display:none}
As Agustin mentioned, ModelChoiceFields must be set to required in order to remove the blank choice.
def __init__(self, queryset, empty_label="---------",
required=True, widget=None, label=None, initial=None,
help_text='', to_field_name=None, limit_choices_to=None,
*args, **kwargs):
if required and (initial is not None):
self.empty_label = None
else:
self.empty_label = empty_label
Required is set to False by default, so you'll need to add the following to your init in Document Form
self.fields['document_type'].required=True
Django has to have a way to allow None values to be set for nullable fields (fields with required=False) and does so by appending an option with an empty value. The same thing happens with Select elements.
Now, for Django to add that option to your Form the document_type field must be nullable (indeed have required=False), and I can only assume that somewhere in the definition of the Form you're setting that option to the field.
PS: If the form is generated automatically for the Model (i.e. you're using Django's ModelForm) then the model should have said Field set with blank=True, null=True, yet that is clearly missing. ModelForm rocks, though, so if you're not familiar with it, try it out.
UPDATE:
TBH I can't work out why that's nullable either, but try setting required=True manually in the form in the same way that #Alistair specified.
self.fields['document_type'].required = True
Right under the line where you modified that field to set the queryset. I think that should work.
I solved this by adding these parameters to my declaration of my field in my model:
blank=False, default=None
So in this case, you model would look like this:
document_type = models.ForeignKey(DocumentType,
verbose_name="Document Type", blank=False, default=None)

Categories