Django forms extra empty radio button - python

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)

Related

Changing the default class for Bootstrap Crispy Form

I am using Crispy Forms with Bootstrap4. Currently, all the input fields are rendered with form-control class. I want to add form-control-lg to the input fields.
I have tried the following but it is not working
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'first_name', 'last_name']
help_texts = {
'username': None,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password1'].help_text = None
self.fields['password2'].help_text = None
self.fields['password2'].label = "Confirm Password"
self.helper = FormHelper()
self.helper.layout = Layout(
Field(
'username', css_class="form-control form-control-lg my-custom-class"
)
)
Template
<div class="content-section">
<form method="POST" action="">
<h3>Sign Up</h3>
<hr>
{% csrf_token %}
{{ form | crispy }}
</br>
<button type="submit" class="btn btn-primary">Sign Up</button>
</form>
</div>
Also, can I modify the class globally for all input fields?
Following the pattern you already have. Let's start with this method:
1.
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2', 'first_name', 'last_name']
help_texts = {
'username': None,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password1'].help_text = None
self.fields['password2'].help_text = None
self.fields['password2'].label = "Confirm Password"
self.helper = FormHelper()
# Attach to helper
self.helper.form_class = 'form-control-lg'
FormHelper has a list of attributes that can be set, that affect mainly form attributes.
Let’s see how to render the form in a template. Supposing we have the form in the template context as example_form, we would render it doing:
{% load crispy_forms_tags %}
{% crispy example_form example_form.helper %}
Notice that the {% crispy %} tags expects two parameters: first the form variable and then the helper. In this case we use the FormHelper attached to the form, but you could also create a FormHelper instance and pass it as a context variable. Most of the time, you will want to use the helper attached. Note that if you name your FormHelper attribute helper you will only need to do:
{% crispy form %}
Inside project settings.py add this line
CRISPY_CLASS_CONVERTERS = {'textinput': "textinput inputtext"}
For example this setting would generate <input class"textinput inputtext" .... The key of the dictionary textinput is the Django’s default class, the value is what you want it to be substituted with, in this case we are keeping textinput.
So from above you could substitute this way:
CRISPY_CLASS_CONVERTERS = {'input-control': "input-control-lg inputtext"}
References and quotes from Django Crispy Form Doc
Edit
Check this answer

Pass Foreignkey from User Input to DB

I'm trying to have user input the data and store into DB and map with the other data.
Model:
class Code(models.Model):
name = models.CharField(max_length=4, default=None, blank=True, unique=True)
Within the Model, there is another class
class Pull(models.Model):
code_pull = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
How to display to call in the Form and View, so that data is pass when user input the data in the input field.
Form
class Code_Form(forms.ModelForm):
class Meta:
model = Code
fields = ('name',)
class Pull_Form(forms.ModelForm):
class Meta:
model = Pull
fields = ('code_pull', 'data1', 'prefix',)
#Inital Value is NULL
def __init__(self, *args, **kwargs):
super(Pull_Form, self).__init__(*args, **kwargs)
self.fields['code_pull'].queryset = CODE.objects.none()
if 'code_pull' in self.data:
c = self.data.get('code_pull')
self.fields['code_pull'].queryset = CODE.objects.filter(name=c)
#print(self.fields['code_pull'].queryset)
I updated the code for the FORM, so that it initial the value from the CODE_form, Still Error, as the code field is empty
Here is the VIEW:
def InputData(request, *args, **kwargs):
form = Pull_Form(request.POST or None)
if request.method == 'POST':
if form.is_valid():
data_add = form.save(commit=False)
data_add.code = form.cleaned_data['code_pull']
data_add.save()
messages.success(request, 'Successfully')
else:
messages.error(request, form.errors)
return render(request, template_name, {'form': form })
ERROR: Not able to add the data as the field for the code is not selected when submitting the form.
ERROR CODE: code - Select a valid choice. That choice is not one of the available choices.
{{ messages }}
<form id="form1" class="post-form" role=form method="POST" action=".">{% csrf_token %}
<input id="code_pull" class="form-control" type="text" maxlength="4" required></input>
<label for="code_pull">Code</label>
<button type="submit" class="btn">Save</button>
</form>
Thank you for the help in advance.
Django forms use the name attribute in HTML controls to capture form data.
<input id="code" name="code" class="form-control" type="text" maxlength="4" required></input>
I only added name="code". this should make it work.

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.

Boolean field not saving in Django form

I have a form with radio buttons and text fields. When I submit the form, the boolean field does not get created in the record. The boolean field is supposed to be updated via the radio buttons. What could be the issue here?
Here is the relevant part of my forms.py file:
CHOICES = (
(1,'yes'),
(0,'no')
)
class ServiceForm(forms.ModelForm):
one_time_service = forms.ChoiceField(required = True, choices = CHOICES, widget=forms.RadioSelect())
class Meta:
model = Service
fields = ('one_time_service')
This is my models.py one_time_service field
one_time_service = models.BooleanField(default=False)
This is my views.py:
def create(request):
if request.POST:
form= ServiceForm(request.POST)
if form.is_valid():
service_obj = form.save(commit=False)
service_obj.user_id = request.user.id
service_obj.save()
return render_to_response('services/service_created.html',
{'service': Service.objects.get(id=service_obj.id)})
else:
form = ServiceForm()
args= {}
args.update(csrf(request))
args['form'] = form
return render_to_response('services/create_service.html', args )
Edit: Here is my create_service.html
<form action="/services/create" method="post" enctype="multipart/form-data">{% csrf_token %}
<ul>
{{form.as_p}}
</ul>
<input type="submit" name="submit" value="Create Service">
</form>
I have no idea if this is the problem, but the line:
fields = ('one_time_service')
is wrong. That's not a single element tuple, that's a string with parens around it. Add a comma to make it a tuple:
fields = ('one_time_service',)
Edit: also, form.save() does not update any database records -- it creates a new one! That may be your problem.

Categories