I'm trying to display all my model items but add some crispy elements. However it shows my html and buttons above the model form fields instead of being under the html and above the buttons.
Form
class BusinessForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BusinessForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_class = 'form-horizontal'
self.helper.form_action = 'update'
self.helper.form_method = 'post'
self.helper.layout = Layout(
HTML("<p class='alert-info alert'>Please confirm your business contact information is updated and correct.</p>"),
Div(
FormActions(
Submit('save_changes', 'Save changes', css_class="btn-primary"),
),
css_class='row-fluid'
)
)
# self.helper.add_input(Submit('save_changes', 'Save changes', css_class="btn-primary"))
class Meta:
model = Business
exclude = ('inactive',)
View
def index(request, token):
try:
business = Business.objects.get(token__token=token)
except Token.DoesNotExist:
business = None
except Business.DoesNotExist:
business = None
return render(request, 'business/index.html', {'form': BusinessForm(instance=business)})
I saw use Crispy form with ModelForm however it displays my model fields below the formactions and I even tried an add_input which did the same thing. How would I get my model form fields to be displayed between the alert-info and above the submit button?
Related
Whenever I submit the form, it is invalid and there is no error message attached to it when I try to read it with form.errors; it's empty. Here is what I have:
models.py
class Project(models.Model):
project = models.CharField(unique=True, max_length=50)
is_active = models.BooleanField(default=False)
forms.py
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Row, Column, Submit, Field
class SelectProjectForm(forms.Form):
def __init__(self, active_choices, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['is_active'] = forms.ChoiceField(choices=active_choices, widget=forms.Select)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.layout = Layout(
Row(
Column(Field('is_active'))
),
Row(
Column(FormActions(Submit('activate', 'Activate Project')))
),
)
views.py
class ProjectSettings(LoginRequiredMixin, TemplateView):
template_name = 'Home/project_settings.html'
def get(self, request, *args, **kwargs):
active_choices = []
for project in Project.objects.all():
active_choices.append((project.id, project.project),)
return render(request, self.template_name, {'form': SelectProjectForm(active_choices)})
def post(self, request, *args, **kwargs):
if 'activate' in request.POST:
form = SelectProjectForm(request.POST)
if form.is_valid():
....
messages.error(request, 'Something went wrong')
return redirect('project_settings')
project_settings.html:
<div>
{% load crispy_forms_tags %}
{% crispy form %}
</div>
I think the problem might be in the POST method in views where I initialize the form, but I don't know how to pass the active_choices parameter in post. If that is not the problem then I am lost.
I have looked at this (django variable of one view to another from session), but I believe the desired outcome is quite different.
I have two views in my views.py file: projectcreation and projectconfirm.
After the user fills out a form in the projectcreation view, I want them to be directed to a confirmation page that gives a read-only view of the variables before proceeding with the project creation.
My views.py file looks like this:
from django.shortcuts import render
from django.http import HttpResponse
from .projectform import ProjectForm
from .projectconfirm import ProjectConfirm
def projectcreation(request):
if request.method == 'POST':
form = ProjectForm(request.POST)
if form.is_valid():
request.session['projectname'] = form.cleaned_data['client'] + "-" + form.cleaned_data['stage'] + "-" + form.cleaned_data['purpose']
request.session['computeapi'] = form.cleaned_data['computeapi']
request.session['deploymentmanapi'] = form.cleaned_data['deploymentmanapi']
request.session['storagecompapi'] = form.cleaned_data['storagecompapi']
request.session['monitorapi'] = form.cleaned_data['monitorapi']
request.session['loggingapi'] = form.cleaned_data['loggingapi']
return render(request,'projectconfirm.html')
else:
form = ProjectForm()
return render(request, 'projectform.html', {'form': form})
def projectconfirm(request):
if request.method =='POST':
print("Now beginning deployment...")
else:
form = ProjectConfirm()
return render(request, 'projectconfirm.html', {'form': form})
The problem I'm facing and admittedly not understanding is how to load the session variables in the projectconfirm.py script.
I thought something like the following would work, but it's complaining that 'request' is an undefined variable:
from django import forms
from django.shortcuts import render
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column, Field, Fieldset
class ProjectConfirm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': request.session['projectname']}))
computeapi = forms.CharField(widget=forms.TextInput(attrs={'placeholder': request.session['computeapi']}))
deploymentmanapi = forms.CharField(widget=forms.TextInput(attrs={'placeholder': request.session['deploymentmanapi']}))
storagecompapi = forms.CharField(widget=forms.TextInput(attrs={'placeholder': request.session['storagecompapi']}))
monitorapi = forms.CharField(widget=forms.TextInput(attrs={'placeholder': request.session['monitorapi']}))
loggingapi = forms.CharField(widget=forms.TextInput(attrs={'placeholder': request.session['loggingapi']}))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'Project Name',
Row(
Column('name', css_class='form-group col-md-4 mb-0', readonly=True),
)
),
Fieldset(
'APIs To Enable',
Row(
Column('computeapi', css_class='form-group col-md-4 mb-0', readonly=True),
Column('deploymentmanapi', css_class='form-group col-md-4 mb-0', readonly=True),
Column('storagecompapi', css_class='form-group col-md-4 mb-0', readonly=True),
Column('monitorapi', css_class='form-group col-md-4 mb-0', readonly=True),
Column('loggingapi', css_class='form-group col-md-4 mb-0', readonly=True)
)
),
Submit('Deploy', 'Deploy', css_class='btn-success')
)
In constructor of Form request can be obtained by:
Passing it through **kwargs, so:
# in your view:
form = ProjectConfirm(request=request)
# in ProjectConfirm
class ProjectConfirm(forms.ModelForm):
name = forms.CharField(widget=forms.TextInput(attrs={}))
# etc
def __init__(self, *args, **kwargs):
request = kwargs.pop("request")
super().__init__(*args, **kwargs)
# ... and then define your widgets inside your __init__
self.fields['name'].widget.attrs['placeholder'] = request.session["projectname"]
# etc
By defining Form as a nested class of your view, but it has to be class-view instead of function.Then you can pass it to your form, still it's not an elegant solution as it's mixing views and forms in the same module. Anyway it will be something like that:
class YourView(FormView):
def get_form_class(self):
request = self.request
class ProjectConfirm(forms.Form):
# your existing form definition
return ProjectConfirm
Let me know if it's helpful for you.
I have a simple CategoryForm which has a hidden field that automatically gets added during save on the front-end. In the Admin panel I would like is_staff users to be able to add a Category while the field is hidden there as well. To superusers I would like the field to be shown. How do I get the excluded fields back in my Admin form?
Forms.py:
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ('category', 'company',)
exclude = ['company']
widgets = {'company': forms.HiddenInput()}
Admin.py:
class CustomCategoryAdmin(admin.ModelAdmin):
form = CategoryForm
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if not request.user.is_superuser:
self.fields = ('category',)
else:
self.fields = ('category', 'company',) # this throws a key error because company is excluded
return form
def save_related(self, request, form, formsets, change):
super(CustomCategoryAdmin, self).save_related(request, form, formsets, change)
company = request.user.company
form.instance.company.add(company) # add object to company field while saving
What I decided to do was create another form named AdminCategoryForm and set the form = AdminCategoryForm as an attribute of CustomCategoryAdmin.
So now my Forms.py:
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ('category', 'company',)
exclude = ['company']
widgets = {
'company': forms.HiddenInput()
}
class AdminCategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ('category', 'company',)
And my Admin.py:
class CustomCategoryAdmin(admin.ModelAdmin):
form = AdminCategoryForm
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if not request.user.is_superuser:
self.fields = ('category',)
else:
self.fields = ('category', 'company',)
self.filter_horizontal = ('company',)
return form
def save_related(self, request, form, formsets, change):
super(CustomCategoryAdmin, self).save_related(request, form, formsets, change)
company = request.user.company
form.instance.company.add(company)
Even though I am repeating my code I feel that this may be the more elegant solution than overriding a ModelAdmin method as it becomes much more clear to outside programmers what is going on here.
If anybody else has other solutions I would love to hear..
I have a cripsy django form.
What is the best way to hide the field label in the template when I use {% cripsy form %}?
I do not want the user to see MY_FIELD_1 and MY_FIELD_2.
class mYForm(forms.ModelForm):
MY_FIELD_1 = forms.BooleanField()
MY_FIELD_2 = forms.BooleanField()
def __init__(self, *args, **kwargs):
...
...
self.helper = FormHelper()
self.helper.layout = Layout(
Field('MY_FIELD_1',),
Field('MY_FIELD_2',),
)
...
If you want to remove all labels from your form when using the crispy forms FormHelper then you can use:
self.helper.form_show_labels = False
If you want to remove labels from certain fields then you can do
self.fields['some_field'].label = False
Where some_field is the name of the field whose label you want to remove.
I have a django form and I need to display unit of measurement.
For example, for a form field I have label and value. I also need to have another 'label' for unit measurement.
E.g. Weight [Textbox] Kg
How can I add Kg in a form in forms.py? I'm using crispy forms module to render my forms.
This is an example from forms.py .
class WeightForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(LifeEventsForm, self).__init__(*args, **kwargs)
self.helper=FormHelper(self)
self.helper.layout = Layout(
'weight',
FormActions(
Submit('submit', "Save changes"),
Submit('cancel',"Cancel")
),
)
self.helper.form_tag = False
self.helper.form_show_labels = True
class Meta:
model = myWeight
My models.py looks like:
class myWeight(models.Model):
id = models.IntegerField()
weight = models.IntegerField(null=True,blank=True)
def __str__(self):
return str(self.id)
Maybe not best idea, but pretty straightforward:
You can override from crispy_forms.layout import Field with custom template
class DoubleLabeledField(Field):
template = "your_custom_field.html"
copypaste to your_custom_field.html everything from .../site-packages/crispy_forms/templates/bootstrap3/field.html (replace bootstrap3 if another template package) and put some info near every entrance of {{ field.label|safe }}
Then in your form should be:
self.helper.layout = Layout(
DoubleLabeledField('weight'),
FormActions(
Submit('submit', "Save changes"),
Submit('cancel',"Cancel")
),
)