How to create dropdown box with forms.ModelForm in Django? - python

I'm trying to add a dropdown box of choices in my form. What I tried:
from django import forms
from .models import Car
class CarForm(forms.ModelForm):
owner_name = forms.CharField(max_length=100, label='Owner Name', required=True)
car_type = forms.ChoiceField(choices=Car.CarTypes.choices, label='Car Type', required=True)
class Meta:
model = Car
fields = ('owner_name', 'car_type')
It adds the car_type field to the form but it's not a dropdown for some reason (it's a regular empty box you could fill). The CarTypes looks like:
class CarTypes(models.TextChoices):
X = 'some_x'
Y = 'somy_y'
# ...
What could be the reason?
In my Car class I have: car_type = models.CharField(max_length=24, choices=CarTypes.choices, default=CarTypes.X). Could be it? I think CharField makes it empty box. But changing it to ChoiceField ends with: module 'django.db.models' has no attribute 'ChoiceField'. How to solve it?
I tried this approach but it uses a tuple instead of a class. From the example there I see they use tuple of two-value-tuples like (('a','A'),('b','B')) and I'm using class. Could it be the reason?
In my html I have:
<form method="post" class="form-group">
{% csrf_token %}
{% for field in form %}
<div class="form-group col-md-12 mb-3">
<label for="{{ field.label }}">{{ field.label }}</label>
<input type="text" class="form-control" id="{{ field.label }}" name="{{ field.name }}">
</div>
{% endfor %}
<hr class="mb-4">
<button type="submit" class="btn btn-secondary btn-lg btn-block">Submit New Car</button>
</form>
And the Car looks like:
class Car(models.Model):
class CarTypes(models.TextChoices):
X = 'some_x'
Y = 'somy_y'
# ...
owner_name = models.CharField(max_length=100,unique=True)
car_type = models.CharField(max_length=24, choices=CarTypes.choices, default=CarTypes.X)
def __str__(self):
return self.owner_name

You don't need to override your model fields. See Field types - Django Docs
If the model field has choices set, then the form field’s widget will
be set to Select, with choices coming from the model field’s choices.
The following code should do what you want:
forms.py
from django import forms
from .models import Car
class CarForm(forms.ModelForm):
class Meta:
model = Car
fields = ['owner_name', 'car_type']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['owner_name'].widget.attrs.update({"class": "form-control"})
# or iterate over field to add class for each field
for field in self.fields:
self.fields[field].widget.attrs.update({'class':"form-control"})
You can add any html attribute in __init__ method.
html:
<form method="post" class="form-group">
{% csrf_token %}
<div class="form-group col-md-12 mb-3">
<label for="{{ form.owner_name.label }}">{{ form.owner_name.label }}</label>
{{ form.owner_name }}
</div>
<div class="form-group col-md-12 mb-3">
<label for="{{ form.car_type.label }}">{{ form.car_type.label }}</label>
{{ form.car_type }}
</div>
<hr class="mb-4">
<button type="submit" class="btn btn-secondary btn-lg btn-block">Submit New Car</button>
</form>
Or You can use {{ form.as_p }} - Django Docs will render them wrapped in <p> tags
UPDATE
Render form manually:
<form method="post" class="form-group">
{% csrf_token %}
<div class="form-group col-md-12 mb-3">
<label for="{{ form.owner_name.label }}">{{ form.owner_name.label }}</label>
<input type="text" class="form-control" id="{{ form.owner_name.auto_id }}" name="{{ form.owner_name.name }}">
</div>
<div class="form-group col-md-12 mb-3">
<label for="{{ form.car_type.label }}">{{ form.car_type.label }}</label>
<select id="{{ form.car_type.auto_id }}" name="{{ form.car_type.name }}">
{% for value, label in form.fields.car_type.choices %}
<option value="{{ value }}"{% if form.car_type.value == value %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<hr class="mb-4">
<button type="submit" class="btn btn-secondary btn-lg btn-block">Submit New Car</button>
</form>

Try to use this method of Choices :-
models.py
choices = [
('choice1', 'choice1'),
('choice2',"choice2"),
('choice3',"choice3")
]
class Car(models.Model):
owner_name = models.CharField(max_length=100,unique=True)
car_type = models.CharField(max_length=24, choices=choices, default='choice1')
def __str__(self):
return self.owner_name
And then don't forget to make migrations
Refer this ---> https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.Field.choices

For Django 3.1, you can achieve this with widgets. Here's how you can get it done:
from django.forms import ModelForm, Select, TextInput
from .models import Car
class CarForm(ModelForm):
class Meta:
model = Car
fields = ('owner_name', 'car_type')
widgets = {
'owner_name': TextInput(),
'car_type': Select(),
}
And Model should look like this:
class Car(models.Model):
choices = [('X':'some_x'), ('Y': 'some_y']
owner_name = models.CharField(max_length=100,unique=True)
car_type = models.CharField(max_length=24, choices=choices, default='some_y')
def __str__(self):
return self.owner_name```

Related

Django ModelForm initial not working for textarea

I have a django ModelForm, I'm trying to populate it with initial values from my views, all fields are populated except for the textarea field. I have no idea why. Any help is appreciated. Thanks!
views.py
name = 'Testing123'
private = True
notes = 'JUST CHECKING'
ctx['header_form'] = NewTemplateForm(initial={'name': name, 'private': private,'notes:': notes})
private is a BooleanField, initial works even for that
name is populated
notes is the only field that isn't being populated.
template.html
I'm rendering the form like this:
{% load widget_tweaks %}
<div class="form-group">
<div class="row" style="margin-bottom: 15px">
<div class="col-lg-6">
<label>{{form.name.label}}</label>
{% render_field form.name class="form-control" required="true" %}
</div>
<div class="col-lg-6" style="margin-top: 23px">
<div class="i-checks"><label> {% render_field form.private type="checkbox" %} <i></i> Make this Template Private </label></div>
</div>
</div>
</div>
<div class="row" style="margin-bottom: 15px">
<div class="col-lg-12">
<label data-error="wrong" data-success="right" for="id_notes">{{form.notes.label}}</label>
{% render_field form.notes class="form-control" %}
</div>
</div>
forms.py
class NewTemplateForm(ModelForm):
class Meta:
model = SalesTemplate
fields = {'name', 'notes', 'private'}
def __init__(self, *args, **kwargs):
super(NewTemplateForm, self).__init__(*args, **kwargs)
self.fields['notes'].widget = forms.Textarea(attrs={'class': 'md-textarea', 'style': 'height: 75px'})
Because there is a typo in your code and you are passing 'notes:' as key instead of 'notes' in this line
ctx['header_form'] = NewTemplateForm(initial={'name': name, 'private': private,
'notes:': notes}) # HERE!

Django-Filter: {{ field.value }} is Empty when rendering form fields

I'm using the Django-Filter library !important. For tags, I'm using Django-taggit.
I built the following filter.py:
class TaskFilter(django_filters.FilterSet):
tags = django_filters.ModelMultipleChoiceFilter(widget=forms.CheckboxSelectMultiple, queryset=Task.tags.most_common())
class Meta:
model = Task
fields = ['tags']
However, when I pass this filter to the template, it doesn't render the tags properly. In particular {{ field.value }} is empty. Let's look at the following cases:
CASE 1.
# template.html
{{ filter.form.tags.errors }}
{% for field in filter.form.tags %}
<label for="{{ field.id_for_label }}"></label>
{{ field.value }}
{% endfor %}
# out
<label for="id_tags_0"></label>
<label for="id_tags_1"></label>
<label for="id_tags_2"></label>
CASE 2.
# template.html
{{ filter.form.tags.errors }}
{% for field in filter.form.tags %}
<label for="{{ field.id_for_label }}"></label>
{{ field }}
{% endfor %}
# out
<label for="id_tags_0"></label>
<label for="id_tags_0"><input type="checkbox" name="tags" value="4" id="id_tags_0">Tag 1</label>
<label for="id_tags_1"></label>
<label for="id_tags_1"><input type="checkbox" name="tags" value="1" id="id_tags_1">Tag 2</label>
<label for="id_tags_2"></label>
<label for="id_tags_2"><input type="checkbox" name="tags" value="2" id="id_tags_2">Tag 3</label>
CASE 3.
# template.html
{{ filter.form.tags.errors }}
{% for field in filter.form.tags %}
{{ field }}
{{ field.label_tag }}
{% endfor %}
#out
<label for="id_tags_0"><input type="checkbox" name="tags" value="4" id="id_tags_0">Tag 1</label>
<label for="id_tags_1"><input type="checkbox" name="tags" value="1" id="id_tags_1">Tag 2</label>
<label for="id_tags_2"><input type="checkbox" name="tags" value="2" id="id_tags_2">Tag 3</label>
I'm trying to understand why this happens. Why can't I get the values as stated in the docs
STEPS TO REPRODUCE
pip install django-filter + add 'django_filters' to APPS
pip install django-taggit + add 'taggit' to APPS
# models.py
class Task(models.Model):
title = models.CharField(max_length=100, blank=False)
tags = TaggableManager()
# Use the API to create an object.
t = Task.objects.create(title="Title")
t.tags.add("Tag 1","Tag 2")
# views.py
def view(request):
f = TaskFilter(request.GET, queryset=Task.objects.all())
return render(request, 'template.html', {'filter': f}
When you iterate over filter.form.tags you're not iterating over a set of form fields, but instead over a set of individual choices for the tags field. This is why field.value doesn't work.
This should work instead:
{{ filter.form.tags.errors }}
{% for choice in filter.form.tags %}
<label for="{{ choice.id_for_label }}"></label>
{{ choice.tag }}
{% endfor %}
Where tag is an attribute that exists on each choice, which will render the checkbox input for that choice.
This is documented in the documentation for RadioSelect:
To get more granular, you can use each radio button’s tag, choice_label and id_for_label attributes.
Further down, the documentation for CheckBoxSelectMultiple says that the same logic applies for it too.

Django Form Field Custom attribute assignment and use in template

I am trying to generate a form dynamically and want to assign indentation of form fields. I am trying to assign an custom attribute offset to forms.CharField in subclass. I plan to use this logic to create a form dynamically from an xml file, where the fields would be indented based on the depth of the node.
I am unable to retrieve the value of offset while rendering the template and hence unable to assign the margin-left style parameter. The final html output is also shown.
Can someone please help. I have searched some other answers on this site where it appears that arbitrary attributes can be assigned and retrieved in template. e.g.as in thread here where an arbitrary label_class attribute is assigned
My forms.py file :
class MyCharField(forms.CharField):
def __init__(self, *args, **kwargs):
self.offset = kwargs.pop('offset', 0)
super(MyCharField, self).__init__(*args, **kwargs)
class MyDynamicForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyDynamicForm, self).__init__(*args, **kwargs)
self.fields["Field_A"] = MyCharField(label="Input A", offset="5")
self.fields["Offset_Field_B"] = MyCharField(label="Input B", offset="50")
My Views.py looks like this:
class MyDynamicView(View):
template_name = 'demo/myform.html'
form_class = MyDynamicForm
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
My template file using bootstrap looks like this:
{% extends 'demo/base.html' %}
{% load bootstrap3 %}
{% block content %}
<form role="form" method="post">
{% csrf_token %}
{% for field in form %}
<div class="form-group bootstrap3-required">
<label class="col-md-3 control-label " style = "margin-left: {{field.offset}}px" for="{{ field.name }}">{{ field.label}}</label>
<div class="col-md-9">
<input class="form-control" id="id_{{field.name}}" name="{{ field.name }}" placeholder="{{field.label}}" style="margin-left:{{field.offset}}px" title="" required="" type="text"/>
</div>
</div>
{% endfor %}
{% buttons submit='OK' reset='Cancel' layout='horizontal' %}{% endbuttons %}
</form>
{% endblock %}
The html output is:
<form role="form" method="post">
<input type='hidden' name='csrfmiddlewaretoken' value='lTy0rc2r9KNiNNPosUoriUlNzYBpgoVpael1MYLOczFECO7H7LXdES6EGBhUoXx0' />
<div class="form-group bootstrap3-required">
<label class="col-md-3 control-label " style = "margin-left: px" for="Field_A">Input A</label>
<div class="col-md-9">
<input class="form-control" id="id_Field_A" name="Field_A" placeholder="Input A" style="margin-left:px" title="" required="" type="text"/>
</div>
</div>
<div class="form-group bootstrap3-required">
<label class="col-md-3 control-label " style = "margin-left: px" for="Offset_Field_B">Input B</label>
<div class="col-md-9">
<input class="form-control" id="id_Offset_Field_B" name="Offset_Field_B" placeholder="Input B" style="margin-left:px" title="" required="" type="text"/>
</div>
</div>
<div class="form-group"><label class="col-md-3 control-label"> </label><div class="col-md-9"><button class="btn btn-default" type="submit">OK</button> <button class="btn btn-default" type="reset">Cancel</button></div></div>
</form>
It not necessary to instantiate from CharField for that. Probably such initialization of the field in form will be enough for you:
field_a = forms.CharField('Input_A',
widget=forms.TextInput(attrs={'placeholder': 'Input_A', 'style': 'margin-left: 50px'}))

Django rendering display name of choice field in form

tl;dr: How do I make a form output the ‘nice’ name of the choices in a model?
I have a Django model with choices, defined like this:
class City(models.Model):
AMSTERDAM = 'AMS'
ROTTERDAM = 'ROT'
THE_HAGUE = 'THE'
UTRECHT = 'UTR'
CITY_CHOICES = (
(AMSTERDAM, 'Amsterdam'),
(ROTTERDAM, 'Rotterdam'),
(THE_HAGUE, 'The Hague'),
(UTRECHT, 'Utrecht'),
)
name = models.CharField(
max_length=3,
choices=CITY_CHOICES,
default=AMSTERDAM,
blank=False,
)
In forms.py, the widgets are defined like this:
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'text', 'categories', 'city']
widgets = {'title': forms.TextInput(attrs={
'placeholder': 'Enter a descriptive title'}),
'text': forms.Textarea(attrs={'placeholder': 'The article'}),
'categories': forms.CheckboxSelectMultiple(),
'city': forms.RadioSelect(),
}
The form itself is rendered manually (although the same effect happens with just {{form}}:
<form action="{% url 'article-submit' %}" method="POST">
{% csrf_token %}
{{% for field in form %}
<fieldset class="article-form__field">
{% if field.name = "categories"%}
{{ field.label_tag }}
<ul id={{ field.auto_id }}>
{% for checkbox in field %}
<li>
<label for="{{ checkbox.id_for_label }}">
{{ checkbox.choice_label }}
</label>
{{ checkbox.tag }}
</li>
{% endfor %}
</ul>
{% elif field.name = "city" %}
{{ field.label_tag }}
<ul id={{ field.auto_id }}>
{% for radio in field %}
<li>
<label for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
</label>
{{ radio.tag }}
</li>
{% endfor %}
</ul>
{% else %}
{{ field.label_tag }} {{ field }}
{% endif %}
</fieldset>
{% endfor %}}
<fieldset class="article-form__field">
<input class="button" type="submit" value="submit">
</fieldset>
</form>
However the output is not Amsterdam, but AMS:
<fieldset class="article-form__field">
<label for="city_0">City:</label>
<ul id=city>
<li>
<label for="city_0">
---------
</label>
<input checked="checked" id="city_0" name="city" type="radio" value="" />
</li>
<li>
<label for="city_1">
AMS
</label>
<input id="city_1" name="city" type="radio" value="1" />
</li>
etc.
In other templates, I could do this: {{city.get_name_display}}, to get the full name. How do I do this in the form?
full code is here
I got the following answer from reddit user roambe:
Since city is a foreign key, Django will use the __str__ (or __unicode__) method of the City model for the choice label.
This should make it start behaving like you want to (substitute for unicode if needed):
def __str__(self):
return self.get_name_display()
In your line 'city': forms.RadioSelect(),, you need to define the choices for it. It should look like:
from blah.models import City
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'text', 'categories', 'city']
widgets = {'title': forms.TextInput(attrs={
'placeholder': 'Enter a descriptive title'}),
'text': forms.Textarea(attrs={'placeholder': 'The article'}),
'categories': forms.CheckboxSelectMultiple(),
'city': forms.RadioSelect(choices=City.CITY_CHOICES),
}
The radio select widget inherits from the select widget, which has a brief mention of the choices argument in the docs here: https://docs.djangoproject.com/en/1.10/ref/forms/widgets/#django.forms.Select
I'd like to extend the argument a little, with a 'work-around' solution for an inherit problem.
In my situation, the returned value never changes from 'value' to 'human-r'. I believe it depends on my db-django config: on my db the field is a FK, but on Django is a simple CharField. In this way I can't define a str method for it without make changes to the model.
I decided, I had only 2-3 choices, to override the get_status function in order to evaluates the status and return a 'constant' output (a simply IF construct).

how to populate dropdown list in Django from a foreignkey model

I have two models.
class ArticleCategory(models.Model):
category = models.CharField(max_length=255,blank=False)
class Article(models.Model):
title = models.CharField(max_length=255,blank=False)
body = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(ArticleCategory,default=1)
Now I have to render a template and save the form for Article model. I have a foreignKey field in my Article Model and because of that I'm not able to save my article form. I want to select a category from dropdown list and save it in my Article model.
How should I code my template for this ?
My views.py function for this is:
def create(request):
if request.POST:
form = ArticleForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/articles/all/')
else:
form = ArticleForm()
args={}
args.update(csrf(request))
args['categories'] = ArticleCategory.objects.all()
args['form'] = form
return render_to_response('create_article.html', args)
My template create_article.html currently looks like this:
<form role="form" action="/articles/create/" method="POST" enctype="multipart/form-data">{% csrf_token %}
<div class="row">
<div class="form-group col-lg-3">
<label></label>
<p>{{form.title}}</p>
</div>
<div class="form-group col-lg-3">
<label>Category</label>
<p>
<select id="id_category">
{% for category in categories %}
<option value="{{ category }}">{{ category.category }}</option>
{% endfor %}
</select>
</p>
</div>
<div class="clearfix"></div>
<div class="form-group col-lg-12">
<label>Body</label>
{{form.body}}
</div>
<div class="form-group col-lg-12">
<button type="submit" class="btn btn-default">Save Article</button>
</div>
</div>
</form>
You don't need to do this manually. If your ArticleForm is ModelForm and doesn't exclude category field then you can just write {{ form.category }} and get dropdown created by django automatically. It uses ModelChoiceField underneath the hood.
replace
<select id="id_category">
{% for category in categories %}
<option value="{{ category }}">{{ category.category }}</option>
{% endfor %}
</select>
with
{{ form.category }}

Categories

Resources