I'm having difficulty settings up the forms.py file to include a radio or select button. I looked at the documentation but was having no luck applying the correct syntax.
Here is what I currently have in forms.py--
from django import forms
class PictureForm(forms.Form):
like = forms.ChoiceField(???)
name = forms.CharField()
email = forms.EmailField()
message = forms.CharField()
And in my views.py --
from app.forms import PictureForm
def index2(request):
if request.method == 'POST':
form = PictureForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
Picture.objects.create(like=cd['like'], name=cd['name'], email=cd['email'], message=cd['message'])
return HttpResponseRedirect ('/thanks/')
else:
form = PictureForm()
return render_to_response('index2.html', {'form':form},)
How can I set up a set of radio buttons of 'value1', 'value2', 'value3'?
How to do this with a select dropdown?
Thank you.
Look at setting the field's widget and choices when writing the form class.
from django import forms
class PictureForm(forms.Form):
CHOICES = [
('1', 'Option 1'),
('2', 'Option 2'),
]
like = forms.ChoiceField(
widget=forms.RadioSelect,
choices=CHOICES,
)
The default widget of ChoiceField is a drop down select.
The choices argument has the same format as the one of a model field.
Remember to remove all the external styling, and then add the code below:
CHOICES = [('M','Male'),('F','Female')]
Gender=forms.CharField(label='Gender', widget=forms.RadioSelect(choices=CHOICES))
The above code generates Radio buttons.
In my case, I was using a style.css file which was forcing the radio button to render as a list.
Related
I have a Django input slider defined as follows:
#widgets.py
from django.forms.widgets import NumberInput
class RangeInput(NumberInput):
input_type = 'range'
#forms.py
from polls.widgets import RangeInput
class VoteForm(forms.ModelForm):
class Meta:
#model = CC_Responses
model = CC_Resp_NoFK
fields = ['Person_ID', 'Test_date', 'Response_value']
# following added from SliderModelForm
widgets ={
'Response_value':RangeInput
}
which is “processed” by the view
def p2vote(request,q_id):
CC_question = get_object_or_404(CC_Questions, pk=q_id)
#
if request.method == 'POST':
form = VoteForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.Q_ID = q_id
item.save()
return redirect('/polls/p2')
else:
formV = VoteForm()
return render(request, 'pollapp2/vote.html', {'var_name':CC_question,'form' : VoteForm()})
and in the template I have the inline CSS
<style>
/* Following CSS is used by the input slider (range) since Django assigns id value of id_Response_value */
/*See: https://stackoverflow.com/questions/110378/change-the-width-of-form-elements-created-with-modelform-in-django */
#id_Response_value{width:300px;}
</style>
which is associated with the slider/range
<label for="Response_value">Response value</label>
{% render_field form.Response_value rows="1" class="form-control" %}
I obtained id_Response_value by looking at the source of the rendered HTML in the browser – I guess I could explicitly set an ID. All the above works exactly as I want. I can control the width of the range slider using CSS.
Now, I believe, inline CSS is a “bad thing”, so as a step to improve my code I’m trying to associate the attribute “more directly” with the slider.
In Django documentation in the section: https://docs.djangoproject.com/en/4.0/topics/forms/modelforms/#overriding-the-default-fields
there is the following example:
from django.forms import ModelForm, Textarea
from myapp.models import Author
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title', 'birth_date')
widgets = {
'name': Textarea(attrs={'cols': 80, 'rows': 20}),
}
The dictionary attrs created looks like it has CSS “stuff” in it, so I coded in widgets in the model form in forms.py
from polls.widgets import RangeInput
class VoteForm(forms.ModelForm):
class Meta:
model = CC_Resp_NoFK
fields = ['Person_ID', 'Test_date', 'Response_value']
widgets ={
'Response_value':RangeInput(attrs={'width': '1000px'}),
}
I tried both:
attrs={'width': '1000px'}
and
attrs={'width': 1000}
Neither changed the width of the range slider. Is what I am doing possible in the (model)form? Have I just got a problem with what I am coding in attrs?
I may have misunderstood but I see something like the following promoted as what needs to go in the ModelForm
def __init__(self, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs) # Call to ModelForm constructor
self.fields['long_desc'].widget.attrs['cols'] = 10
self.fields['long_desc'].widget.attrs['rows'] = 20
No. Really? To change the attributes?
I have an extended admin model that creates action buttons. I have created a view to do pretty much the same thing. I have used tables2 and everything is just fine except for the actions column. I cannot find a way to generate the same button in the table. Is there a way to do this at all?
tables.py
from .models import Ticket
import django_tables2 as tables
'''from .admin import river_actions, create_river_button'''
class TicketTable(tables.Table):
class Meta:
model=Ticket
template_name='django_tables2/table.html'
fields = ('id','subject','request_type','material_type','productline','business','measurement_system',
'created_at','updated_at','status','river_action','project') # fields to display
attrs = {'class': 'mytable'}
'''attrs = {"class": "table-striped table-bordered"}'''
empty_text = "There are no tickets matching the search criteria..."
admin.py (the part that includes the model etc)
# Define a new User admin to get client info too while defining users
class UserAdmin(BaseUserAdmin):
inlines = (UserExtendInline, )
def create_river_button(obj,proceeding):
return '''
<input
type="button"
style=margin:2px;2px;2px;2px;"
value="%s"
onclick="location.href=\'%s\'"
/>
'''%(proceeding.meta.transition,
reverse('proceed_ticket',kwargs={'ticket_id':obj.pk, 'next_state_id':proceeding.meta.transition.destination_state.pk})
)
class TicketAdmin(admin.ModelAdmin):
list_display=('id','client','subject','request_type','material_type','productline','business','measurement_system', \
'created_at','updated_at','created_by','status','river_actions')
#list_display_links=None if has_model_permissions(request.user,Ticket,['view_ticket'],'mmrapp')==True else list_display
#list_display_links=list_display #use None to remove all links, or use a list to make some fields clickable
#search_fields = ('subject','material_type')
list_filter=[item for item in list_display if item!='river_actions'] #exclude river_actions since it is not related to a field and cannot be filtered
#Using fieldset, we can control which fields should be filled by the user in the ADD method. This way, created_by will be the
#logged in user and not a drop down choice on the admin site
fieldsets = [
(None, {
'fields': ('client','subject','description','request_type','material_type', \
'productline','business','measurement_system', 'project')
} ), #to make some field appear horizontal, put them into a []
]
formfield_overrides = {
models.CharField: {'widget': TextInput (attrs={'size':'40'})},
models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':80})},}
def get_list_display(self, request):
self.user=request.user
return super(TicketAdmin,self).get_list_display(request)
def river_actions(self,obj):
content=""
for proceeding in obj.get_available_proceedings(self.user):
content+=create_river_button(obj, proceeding)
return content
river_actions.allow_tags=True
#override save_model method to save current user since it is not on the admin page form anymore
def save_model(self, request, obj, form, change):
if not change:
# the object is being created and not changed, so set the user
obj.created_by = request.user
obj.save()
views.py
def display_tickets(request):
table = TicketTable(Ticket.objects.all())
RequestConfig(request).configure(table)
'''table = CustomerTable(Customer.objects.filter(self.kwargs['company']).order_by('-pk'))'''
return render(request,'mmrapp/ticket_display.html',{'table':table})
buttons created in admin page:
table created using tables2 in views missing buttons:
You must pass empty_values=() to the column, because by default, django-tables2 only renders the column if the value is not contained in empty_values for that column.
import django_tables2 as tables
from .admin import river_actions, create_river_button
from .models import Ticket
class TicketTable(tables.Table):
river_action = tables.Column(empty_values=())
class Meta:
model=Ticket
template_name='django_tables2/table.html'
fields = (
'id', 'subject', 'request_type', 'material_type', 'productline', 'business', 'measurement_system',
'created_at', 'updated_at', 'status', 'river_action', 'project'
) # fields to display
attrs = {'class': 'mytable'}
empty_text = "There are no tickets matching the search criteria..."
def render_river_action(self, record):
return create_river_button(record, ...)
This is also documented as Table.render_foo methods
In my settings.py I have set
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
now in can add my own templates in:
<project>/templates/django/forms/widgets/
or
<app>/templates/django/forms/widgets/
this works great! However, what I can't find is where do I override the default html (form) label?
class TestForm(forms.Form):
first_name = forms.CharField(label="First name", max_length=50)
last_name = forms.CharField(label="Last name")
nick_name = forms.CharField(required=False)
The above form would render the labels like this:
<label for="id_first_name">First name:</label>
I want to render the label differently. So, I thought it would easy as adding a html label template: templates/django/forms/widgets/label.html
This doesn't work. Was going through the Django docs, but I can't find how to do this for labels. Apparently a label is not a widget.
https://docs.djangoproject.com/en/1.11/ref/forms/widgets/#built-in-widgets
My question, where/how do I change the default label?
Here is a working solution (tested on Django 3.1) :
Create a child class of Django's Form, and set LABEL_TEMPLATE to the template your want.
myapp/base_form.py
from django import forms
from django.forms.utils import flatatt
from django.utils.html import conditional_escape, format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
LABEL_TEMPLATE = '<p{}>{}</p>'
class CustomLabelBoundField(forms.boundfield.BoundField):
def label_tag(self, contents=None, attrs=None, label_suffix=None):
"""
Wrap the given contents in a <label>, if the field has an ID attribute.
contents should be mark_safe'd to avoid HTML escaping. If contents
aren't given, use the field's HTML-escaped label.
If attrs are given, use them as HTML attributes on the <label> tag.
label_suffix overrides the form's label_suffix.
"""
contents = contents or self.label
if label_suffix is None:
label_suffix = (self.field.label_suffix if self.field.label_suffix is not None
else self.form.label_suffix)
# Only add the suffix if the label does not end in punctuation.
# Translators: If found as last label character, these punctuation
# characters will prevent the default label_suffix to be appended to the label
if label_suffix and contents and contents[-1] not in _(':?.!'):
contents = format_html('{}{}', contents, label_suffix)
widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id
if id_:
id_for_label = widget.id_for_label(id_)
if id_for_label:
attrs = {**(attrs or {}), 'for': id_for_label}
if self.field.required and hasattr(self.form, 'required_css_class'):
attrs = attrs or {}
if 'class' in attrs:
attrs['class'] += ' ' + self.form.required_css_class
else:
attrs['class'] = self.form.required_css_class
attrs = flatatt(attrs) if attrs else ''
contents = format_html(LABEL_TEMPLATE, attrs, contents)
else:
contents = conditional_escape(contents)
return mark_safe(contents)
def get_bound_field(field, form, field_name):
"""
Return a BoundField instance that will be used when accessing the form
field in a template.
"""
return CustomLabelBoundField(form, field, field_name)
class CustomLabelForm(forms.Form):
def __getitem__(self, name):
"""Return a BoundField with the given name."""
try:
field = self.fields[name]
except KeyError:
raise KeyError(
"Key '%s' not found in '%s'. Choices are: %s." % (
name,
self.__class__.__name__,
', '.join(sorted(self.fields)),
)
)
if name not in self._bound_fields_cache:
self._bound_fields_cache[name] = get_bound_field(field, self, name)
return self._bound_fields_cache[name]
And inherit your form from CustomLabelForm :
myapp/forms.py
from django import forms
from myapp.base_form import CustomLabelForm
class TestForm(CustomLabelForm):
first_name = forms.CharField(label="First name", max_length=50)
last_name = forms.CharField(label="Last name")
nick_name = forms.CharField(required=False)
This produces <p for="id_first_name">First name:</p>
You could override __init__() method of the form:
def __init__(self, *args, **kwargs):
self.fields['field_name'].label = 'Your new label here'
Overriding existing form widgets in Django can actually be done.
First you need to add 'django.forms' to your INSTALLED_APPS to treat it as an app.
INSTALLED_APPS = [
...
"django.forms",
...
]
Then append FORM_RENDERER to your settings.py
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
More info on TemplatesSetting can be found on the Django docs.
After that, copy any widget files you'd like to override from the django/forms/widgets/ folder to your own projectname/templates/django/forms/widgets folder. Customize away!
I hope this helps.
My model
class Member(models.Model):
dob = models.DateField('dob')
form
class MemberForm(ModelForm):
dob = forms.DateField(required=False, input_formats='%Y-%m-%d')
class Meta:
model = Member
exclude = ('profile',)
some view code
if request.method == 'POST': # If the form has been submitted...
signup_form = SignupForm(request.POST) # A form bound to the POST data
# Create a formset from the submitted data
member_formset = MemberFormSet(request.POST, request.FILES)
# import pdb; pdb.set_trace()
if signup_form.is_valid() and member_formset.is_valid():
print 'in valid'
signup = signup_form.save(request.POST)
for form in member_formset.forms:
member = form.save(commit=False)
member.profile = signup
# import pdb; pdb.set_trace()
# print member.objects.all()
member.save()
return redirect("/main") # Redirect to a 'success' page
when m submitting the form the error message coming is
Enter a valid date.
What should i do to solve this validation .
input_formats needs to be a list, see
example:
['%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y', # '10/25/2006'
'%m/%d/%y'] # '10/25/06'
https://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.DateField
I prefer to set **DATE_INPUT_FORMATS
in settings.py and then define the field like:
dob = forms.DateField(input_formats=settings.DATE_INPUT_FORMATS)
This more DRY than setting it on a per form field basis.
If it's a text field input, I almost always put the accepted format(s) in the field's help_text so that the user can know what format(s) is(are) accepted.
You do not need to add a new field once you already have a DateField in your Model.
You just have to set the input_formats for this field:
self.fields[ 'dob' ].input_formats = [ '%Y-%m-%d' ]
References: DateField and Format Localization
The input_formats is your friend.
If you accept javascript, then you could make the field use something like jQuery calendar (i.e. a read-only text field and clicking on it will call the jquery code and pop up the calendar widget). You can have this calendar widget as a starting point.
I try customize django comments module (delete field url)
i create empty class VSComments and form
from django import forms
from django.contrib.comments.forms import CommentForm
from vs_comments.models import VSComment
class VSCommentForm(CommentForm):
"""
No url Form
"""
VSCommentForm.base_fields.pop('url')
__init__
from vs_comments.models import VSComment
from vs_comments.forms import VSCommentForm
def get_model():
return VSComment
def get_form():
return VSCommentForm
also url(r'^comments/', include('django.contrib.comments.urls')),
include 'vs_comments' and 'django.contrib.comments' into INSTALLED_APPS and COMMENTS_APP = 'vs_comments'
As result, I have right form, without url field, but posting comments doesn't work
soution add to the form class
def get_comment_create_data(self):
# Use the data of the superclass, and remove extra fields
return dict(
content_type = ContentType.objects.get_for_model(self.target_object),
object_pk = force_unicode(self.target_object._get_pk_val()),
comment = self.cleaned_data["comment"],
name = self.cleaned_data["name"],
submit_date = datetime.datetime.now(),
site_id = settings.SITE_ID,
is_public = True,
is_removed = False,
)
For admin panel
class VSCommentAdmin(CommentsAdmin):
"""
all like native comments
"""
admin.site.register(Comment, CommentsAdmin)
But now didn't work tags render_comment_list and other. No any errors, only empty result
How can I fix it?
Do you have any error trace ?
I think it doesn't work because the validation of your VSCommentForm uses the validator of CommentForm, and the "url" field is missing in your customized form.
You should read more on customized form that inherits from another form and its validation :
https://docs.djangoproject.com/en/dev/topics/forms/#processing-the-data-from-a-form
https://code.djangoproject.com/wiki/CustomFormFields
https://docs.djangoproject.com/en/dev/ref/forms/validation/