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
Related
enter code hereI'm having some problems to solve a problem. I have a template, which allows the user to change some of their account settings. My goal is to initialize the form, with the user's default values, and he can keep or change them (by after submit form). However, until now the page does not render these values. I'm using a class based view, CreateView, for this purpose.
My code is listed below.
Here, is my CreateView.
class DetailUserInfoView(LoginRequiredMixin ,CreateView):
model = CustomUser.CustomUser
template_name = 'users/InfoUser.html'
login_url = settings.LOGOUT_REDIRECT_URL
context_object_name = 'user'
form_class = CustomUserChangeForm
def get_object(self):
self.model = self.request.user
return self.model
def get_initial(self):
initial = super(DetailUserInfoView, self).get_initial()
initial = initial.copy()
initial[config.USERNAME] = self.request.user.username
initial[config.FIRST_NAME] = self.request.user.first_name
initial[config.LAST_NAME] = self.request.user.last_name
return initial
def get_form_kwargs(self):
kwargs = {'initial': self.get_initial()}
return kwargs
def get_context_data(self, **kwargs): #GET OBJECT ACTS AFTER THAN GET_OBJECT --> EXAMPLE OF GET_CONTEXT_DATA, I DIDN'T NEED THIS
context = super(DetailUserInfoView, self).get_context_data(**kwargs)
context['username'] = self.request.user.username
return context
Here the form.
class CustomUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = CustomUser.CustomUser
fields = ('email', 'password', 'first_name', 'last_name', 'username', 'userType')
And finally an extract of template.
<div id="infoMayOverride">
<form class="getOverridedValues" method="post">
{% csrf_token %}
<div id="usernameData">
<label>{{ form.username.label_tag }}</label> <!--MODEL ON CREATEUSERVIEW IS CUSTOMUSER, AND NOW I NEED TO USE THIS FIELDS AND INHERITED FIELDS FROM USER CLASS-->
<input type="text" id="usernameInput" value="{{ form.username }}">
</div>
<div id="firstNameData">
<label>{{ form.first_name.label_tag }}</label>
<input type="text" id="firstNameInput" value="{{ form.first_name }}">
</div>
<div id="lastNameData">
<label>{{ form.last_name.label_tag }}</label>
<input type="text" id="lastNameInput" value="{{ form.last_name }}">
</div>
<div id="divBtnChangeProfile">
<input type="submit" class="btnChangeProfile" value="Atualizar Profile">
</div>
</form>
</div>
I'd appreciate it if you could help me. I am new to the Django environment, and have tried many approaches, and I have not yet been able to solve this problem.
--------------------------- Update ------------------------------------
Now, i can get initial values. But to view them i need to write on input form: form.username.initial, and with this i can't after submit form to update user values.
Anyone knows how to solve this type of problem??
I finally got this problem solved. I will make my solution available, since it can help other people.
I had to make some changes to the code I provided behind.
Below is the code of view.
class DetailUserInfoView(LoginRequiredMixin, UpdateView):
model = CustomUser.CustomUser
template_name = 'users/InfoUser.html'
login_url = settings.LOGOUT_REDIRECT_URL
context_object_name = 'user'
form_class = CustomUserChangeForm
def get_object(self, queryset=None):
return self.request.user
def get_form_kwargs(self):
kwargs = super(DetailUserInfoView, self).get_form_kwargs()
u = self.request.user
kwargs['username_initial'] = u.username
kwargs['fName_initial'] = u.first_name
kwargs['lName_initial'] = u.last_name
return kwargs
def get_context_data(self, **kwargs): #GET OBJECT ACTS AFTER THAN GET_OBJECT --> EXAMPLE OF GET_CONTEXT_DATA, I DIDN'T NEED THIS
context = super(DetailUserInfoView, self).get_context_data(**kwargs)
form_class = self.get_form_class()
form = self.get_form(form_class)
context['form'] = form
return context
My form (with init function, to set initial values on form, and is called by def get_form_kwargs(self)).
class CustomUserChangeForm(UserChangeForm):
def __init__(self, *args, **kwargs):
username_initial = kwargs.pop('username_initial', None)
fName_initial = kwargs.pop('fName_initial', None)
lName_initial = kwargs.pop('lName_initial', None)
super(CustomUserChangeForm, self).__init__(*args, **kwargs)
self.fields['username'].initial = username_initial
self.fields['first_name'].initial = fName_initial
self.fields['last_name'].initial = lName_initial
class Meta(UserChangeForm.Meta):
model = CustomUser.CustomUser
fields = ('username', 'email', 'first_name', 'last_name')
And finnaly, in template I replace the tag input with {{ form.username }}.
I hope it can help someone who has the same problem.
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.
I need to create a required checkbox option if no_new_item exists. I am using the model.NullBoolean field. According to Django docs the Boolean field should render the checkbox widget but NullBoolean renders Select. The reason for the switch to NullBoolean was due to the null error when migrating. So now I am getting a drop down list with 'Yes' and 'No.'
How would I go about creating the checkbox in the Base model.Models with NullBoolean or is there a better way?
(this is an edit as I miss spoke about the Django Docs. Thanks #Alasdair)
/models.py
class StoreNightlyReport(models.Model):
store_number = models.ForeignKey('Stores', verbose_name='Store Number', max_length=20, null=True)
date = models.DateField(null=True)
#managers = models.ManyToManyField('auth.User', blank=True, null=True)
def __str__(self):
return self.store_number.store_number
class StoreNightlyReportsBase(models.Model):
store_nightly_report = models.ForeignKey(StoreNightlyReport)
no_new_item = models.NullBooleanField(verbose_name='No New Items', default=True)
customer = models.CharField(verbose_name='Customer Name', max_length=20, null=True)
class Meta:
abstract = True
/forms.py
class StoreNightlyReportsForm(ModelForm):
class Meta:
model = StoreNightlyReport
widgets = {'date': SelectDateWidget()}
exclude = ()
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(StoreNightlyReportsForm, self).__init__(*args, **kwargs)
if 'store_number' in self.fields:
if not user.is_superuser:
self.fields['store_number'].queryset = Stores.objects.filter(managers=user)
/views.py
class StoresNightlyReportsNewLoansCreate(CreateView):
template_name = 'reports/storenightlyreport_form.html'
model = StoreNightlyReport
form_class = forms.StoreNightlyReportsForm
success_url = reverse_lazy('reports:storenightlyreports')
def get_form(self, form_class=None):
form_class = self.get_form_class()
form = form_class(self.request.POST or None, user=self.request.user)
self.formsets = {}
StoreNightlyReportsNewLoanFormSet = inlineformset_factory(StoreNightlyReport,
StoreNightlyReportsNewLoan,
exclude=[],
extra=1)
self.formsets['new_loan'] = StoreNightlyReportsNewLoanFormSet(self.request.POST or None)
def get_context_data(self, **kwargs):
data = super(StoresNightlyReportsNewLoansCreate, self).get_context_data(**kwargs)
data['formset_new_loan'] = self.formsets['new_loan']
return data
def get_context_data(self, **kwargs):
data = super(StoresNightlyReportsNewLoansCreate, self).get_context_data(**kwargs)
#formset_renewal = StoreNightlyReportsRenewalFormSet()
data['formset_new_loan'] = self.formsets['new_loan']
return super(StoresNightlyReportsNewLoansCreate, self).form_invalid(form)
/template.html
{% extends "reports/base.html" %}
{% load static %}
{% block body_block %}
<div class="container-fluid">
<form class="form-inline" action="" method="post">
{% csrf_token %}
{{ form }}
<table class="table">
{{ formset_new_loan.management_form }}
<div>
<br><h4><strong>New Loans</strong></h4><br>
{% include "reports/formsets.html" with formset=formset_new_loan formset_class_name='new_loan' %}
</div>
</table>
Fixed
After some thought I just went back to the model.Base and changed it to the BooleanField type and added a default value. This cleared the null error on migration and rendered a checkbox option.
class StoreNightlyReportsBase(models.Model):
store_nightly_report = models.ForeignKey(StoreNightlyReport)
no_new_item = models.BooleanField(verbose_name="Check if No New Items", default=False)
customer = models.CharField(verbose_name='Customer Name', max_length=20, null=True)
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)
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.