Django - dropdown form with multiple select - python

I need guidance building a django dropdown forms.Form field in which I can select multiple choices. I need to select multiple locations on the office field of the form.
When submitted, the form needs to return a list of the chosen offices (e.g. ["New York", "Los Angeles"] or ["Austin"]). Returning a tuple is also acceptable.
The best I can do right now is build a multipleChoiceField for office with the following:
from django import forms
class my_Form(forms.Form):
OPTIONS = [
("0", "*ALL"),
("1", "New York"),
("2", "Los Angeles"),
]
office = forms.MultipleChoiceField(
choices=OPTIONS,
initial='0',
widget=forms.SelectMultiple(),
required=True,
label='Office',
)
resulting in this form field layout:
However, I would like this field to be a dropdown, to take up less space on the page.
I found this djangosnippet but (1) a few people have mentioned it appears out of date (I can't confirm), and (2) I first want to check if a built-in django form/widget setup can fulfill this task before using custom code.

"Dropdown" boxes don't support multiple selection in HTML; browsers will always render it as a flat box as your image shows.
You probably want to use some kind of JS widget - Select2 is a popular one. There are a couple of Django projects - django-select2, django-easy-select - that aim to make it simple to integrate that into your form; I have no experience with either of them.
(And yes, that snippet - like many things on Djangosnippets - is massively out of date; "newforms" was renamed to "forms" even before version 1.0 of Django.)

You can choose multiple choices by using Django select2. Include below code in your respective HTML file.
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<select class="select_field_class" multiple="multiple" name="option" id="option">
<option value="">Enter the option</option>
{% for option in options %}
<option value="{{ option.id }}">{{ option.name }}</option>
{% endfor %}
</select>
$('.select_field_class').select2( { placeholder: "Select here", maximumSelectionSize: 100 } );

This is late but hope it helps someone else.
You can also do it a combination of django forms and a select2 widget Select2MultipleWidget to make it look cleaner.
class YourCreateForm(forms.ModelForm):
CHOICES = (("address1","address1"), ("address2","address2"))
address=forms.MultipleChoiceField(choices=CHOICES,widget=Select2MultipleWidget)
class Meta:
model = YourModel
fields = ("field1","address",)
Do not forget to install and import the django select2 widgets

As I said in a similar question, one suggestion is use Bootstrap with Python.
forms.py
(...)
class yourForm(forms.Form):
options = forms.MultipleChoiceField(
choices=OPTIONS, widget=forms.CheckboxSelectMultiple(),
label="myLabel", required=True, error_messages={'required': 'myRequiredMessage'})
view.py
def anything(...):
(...)
form = yourForm( )
(...)
return render(request, "myPage.html", {'form': form})
myPage.html
(...)
{% csrf_token %}
{% for field in form %}
<div class="col-md-12 dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{{ field.label_tag }}
<span class="caret"></span>
</button>
<div class="dropdown-menu">
<div>{{ field }}</div>
</div>
</div>
{% endfor %}
(...)

Related

How to save multiple model object instances using one single form in django?

I have the three following models:
class AnswerItem(models.Model):
item_name = models.CharField(max_length=320)
item_number = models.PositiveIntegerField()
class AnswerMenu(models.Model):
menu_name = models.CharField(max_length=320)
answer_item = models.ForeignKey(AnswerItem)
class QuestionAnswer(models.Model):
answer = models.ForeignKey(AnswerMenu)
answer_item = models.ForeignKey(AnswerItem)
Answer menus are currently displayed on a single page using a list view, with the following template:
{% for answer_menu in answer_menus %}
<div>{{ answer_menu.menu_name }}</div>
{% for answer_item in answer_menu %}
<p>
<label>
<input id="{{ answer_item.pk }}" name="{{ answer_menu.pkĀ }}" type="radio">
</label>
</p>
Now, the trouble I have is I would like to create a single form to save all selected answers using the radio buttons on the page. Since there are multiple answer menus shown on the page, posting through this form would create several QuestionAnswer items.
How would you approach this?

Change layout of django-filter form

I am currently working on my first bigger django project and I am facing issues regarding the layout of my data filters made with django-filters.
The default layout of the django-filter form is a vertical list (see image), but I need to make it horizontal, consisting of two rows (labels/fields).
What is the (best practice) way to edit the layout?
Is there a way I can access every Label/Field-Item of the form from within the template, so I can use Bootstrap5 Grid?
One restriction is, that my template will be used by differents models/filters, so I need to configure the layout dynamically.
Every hint is very much appreciated :)
Thank you!
My template (relevant section)
<form method="get" class="form">
<button type="submit" class ="btn btn-primary">Filtern</button>
{% crispy filter.form %}
</form>
my django-filter filter class
class EquipmentFilter(FilterSet):
class Meta:
model = Equipment
fields = {'name': ['icontains'], 'description': ['icontains']}
my model
class Equipment(models.Model):
"""Device that can execute a measurement"""
name = models.CharField("Name", max_length=50)
description = models.CharField("Description", max_length=100)
configuration = models.JSONField("Configuration", default=dict)
equipment_category = models.ForeignKey("EquipmentCategory", on_delete=models.CASCADE, verbose_name="Equipment Category")
filter.form is a normal Django form, so you can layout it the way you want.
I see that you use crispy forms. But you can style it anyway you want, boostrap-way included.
Here is an example from the official documentation
You can use the .as_p method to get them in <p><label><input></label></p> format or you can generate them by hand:
<form method="get" class="form">
<button type="submit" class ="btn btn-primary">Filtern</button>
{{ filter.form.non_field_errors }}
<div class="boostrap-class">
{{ filter.form.name.errors }}
<label for="{{ filter.form.name.id_for_label }}" class="another-boostrap-class">Name</label>
{{ filter.form.name }}
</div>
<div class="boostrap-class">
{{ filter.form.description.errors }}
<label for="{{ filter.form.description.id_for_label }}" class="another-boostrap-class">Description</label>
{{ filter.form.description }}
</div>
</form>
You can also specify a custom Form class in your filterset meta (EquipmentFilter.Meta) and use techniques presented in this other SO question

How to use a DatePicker in a ModelForm in django?

I am using django 3.0 and I am trying to display a datepicker widget in my ModelForm, but I can't figure out how (all I can get is text field). I have tried looking for some solutions, but couldn't find any. This is how my Model and my ModelForm look like:
class Membership(models.Model):
start_date = models.DateField(default=datetime.today, null=True)
owner = models.ForeignKey(Client, on_delete=models.CASCADE, null=True)
type = models.ForeignKey(MembershipType, on_delete=models.CASCADE, null=True)
class MembershipForm(ModelForm):
class Meta:
model = Membership
fields = ['owner', 'start_date', 'type']
widgets = {
'start_date': forms.DateInput
}
And this is my html:
<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Although #willem-van-onsem's answer is great, there are a few alternatives that do not require additional dependencies.
A few options, in order of increasing effort:
Use a SelectDateWidget instead of the default DateInput (no JavaScript required):
class MyForm(forms.Form):
date = forms.DateField(widget=forms.SelectDateWidget())
Use the browser's built-in date picker, by implementing a customized widget that uses the HTML <input type="date"> element (no JavaScript required):
class MyDateInput(forms.widgets.DateInput):
input_type = 'date'
class MyForm(forms.Form):
date = forms.DateField(widget=MyDateInput())
or, alternatively:
class MyForm(forms.Form):
date = forms.DateField(widget=forms.DateInput(attrs=dict(type='date')))
Use the date picker from django.contrib.admin, as described here in detail. In short, there are a few things you would need:
from django.contrib.admin.widgets import AdminDateWidget
...
class MyForm(forms.Form):
date = forms.DateField(widget=AdminDateWidget())
then, to make this work, add the following dependencies to your template <head>:
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
<script src="{% static 'admin/js/core.js' %}"></script>
<script src="{% url 'admin:jsi18n' %}"></script> {# see note below #}
{{ form.media }} {# this adds 'calendar.js' and 'DateTimeShortcuts.js' #}
Now there's one catch: the admin:jsi18n url only works for users with admin access, so you may need to replace this and define an alternative path in your urls.py, e.g.:
from django.views import i18n
...
urlpatterns = [
...,
path('jsi18n/', i18n.JavaScriptCatalog.as_view(), name='jsi18n'),
]
Finally, here's what the widgets look like (on firefox):
Personally I like the second option best. It also allows us to specify initial, minimum and maximum values (in django you can do this e.g. using the attrs argument). Here's a quick snippet to show the HTML element in action:
<input type="date" value="2021-09-09" min="2021-09-09">
Django 4.0. Leaving this here incase it helps someone else.
This will set the minimum date and default value to today's date and should be used in forms.py. In my case I use crispy forms in my .html to render the field.
from datetime import date
today = date.today()
class DateForm(forms.ModelForm):
target_Date = forms.DateField(widget=forms.TextInput(attrs={'min': today, 'value': today, 'type': 'date'}), required=True)
class Meta:
model = DateForm
fields = ['target_Date']
This is the expected behavior. A DateInput widget [Django-doc] is just a <input type="text"> element with an optional format parameter.
You can make use of a package, like for example django-bootstrap-datepicker-plus [pypi]
, and then define a form with the DatePickerInput:
from bootstrap_datepicker_plus import DatePickerInput
class MembershipForm(ModelForm):
class Meta:
model = Membership
fields = ['owner', 'start_date', 'type']
widgets = {
'start_date': DatePickerInput
}
In the template you will need to render the media of the form and load the bootstrap css and javascript:
{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{{ form.media }}
<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
As other have said, this is expected since its just a special text field.
An alternative I prefer is using django-widget-tweaks as this pushes front-end customizations back to your template instead of editing forms.py on the backend. Saving/testing is also faster since the app doesn't have to reload with each save.
Install to your environment:
pip install django-widget-tweaks
Add to installed apps:
INSTALLED_APPS = [
...
"widget_tweaks",
]
Add to your template:
{% extends 'app/base.html' %}
{% load widget_tweaks %}
Use render_field with input tag attributes to customize your field. Eg below using bootstrap 5. Notice how we can specify attributes such as type and class within the template tag:
<div class="col-2">
<label for="{{ form.date.id_for_label }}" class="col-form-label">{{ form.year.label }}</label>
</div>
<div class="col-4">
{% render_field form.year type="date" class="form-control" placeholder="mm/dd/yyyy" %}
<div>

what is the best way to use less Django template syntax as possible with Django Form?

I am just beginner of Django.
At first, It seemed very convenient to use Django Form. I can get all which i already I defined in Model with ModelForm class. I can just put { form } in template. There are already pre-defined Field type and I can use it conveniently.
But it is tricky if you need to work with one who doesn't know Django or Python and are charge of making front-end part.
For example, I make template like this.
example.html
<form action="." method="post"> {% csrf_token %}
{{ form }}
<input type="submit" value="ok">
views.py
class TestView(FormView):
template_name = 'example.html'
form_class = TestForm
success_url = '/success/'
def form_valid(self, form):
print('data is validated! you can do work with cleaned data!')
return super(TestView, self).form_valid(form)
Q1 : A front-developer can't work with {{ form }}. right?
So I can change just example.html because it works properly if you set name attribute and type attribute correctly.
example.html
<form action="." method="post"> {% csrf_token %}
<input type="text" name="name">
<input type="text" name="calorie">
<input type="submit" value="Ok">
</form>
models.py
class Cereal(models.Model):
name = models.CharField(max_length=50)
calorie = models.CharField(max_length=50)
Q2 : I think it would be fine for a front developer to work with it if I change example.html above? right?
Well, if my questions are all fine, It seems fine to use ModelForm and write html code instead of {{ form }} django template syntax. But if you use ModelChoiceField, it seems nasty. You set queryset argument and it shows with select widget on template. for example queryset reqsult is { A, B, C }. it will turn to like below
<select id="id_something" name="name of something">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
As i told you earlier, I don't want to use Django template syntax like {{ form }} as much as I can. so I am going to write like below instead.
views.py
render(request, "example.html", {queryset : something.objects.all()})
example.html
<select id="id_something" name="name of something">
{% for element in queryset %}
<option>{{ element }}</option>
{% endfor %}
</select>
Q3: does this approach seem alright? Isn't it better way to use less Django template syntax when you use Django Form?
well, I concluded that using DRF(Django RestFramework) is the best way to separate tasks for front-end and back-end developers independently. But In current situation I am facing, Template(html, css, js) is already made. I need to change it to use with Django framework. Front end developer will change little bit with the Template which is already made.
When I watched some pycon video in Youtube, some people said that Djang framework is kind of too much coupled.
I hope I can get Idea more efficiently to use Django Model form with front-end developer.
Thanks for reading. My question could be ridiculous for an expert. Please ask me again if it is not clear.

A help in concept and a database query question (Django/Python)

I am trying to build a kind of news website for learning purposes.
class NewsCategory(models.Model):
category = models.CharField(max_length=50)
Note: A category can be Soccer, Tennis, business ... User can register to different news category. This choice will be saved in their preferences.
class Profile(models.Model):
user = models.ForeignKey(User, unique=True)
gender = models.CharField(max_length=1,blank=True)
preference = models.ManyToManyField(NewsCategory)
I am currently stuck on how to update the preference list of each user (the list that specifies in which categories he is interested in.)
View:
category = [(item.category) for item in NewsCategory.objects.all()]
and then I am sending the category to the template below
template:
<div id="c_b">
{% for c in category %}
<input type="checkbox" name="category[]" value="{{c}}">
<label for="{{c}}">{{c}}</label>
{% endfor %}
</div>
Questions:
What is the best way to add the checked tag next to the checkboxes that are already saved in the user's preference when I display the template.
I though of getting all the preferences users are registered for: saved_preference = user.preference.all() and then checking for each item in category if it is in saved_preference
I am also blanking out on the way to actually write that into code, and whether this should be done in the view or the template.
What is a good way to update the user preference list?
I was planning on running user.preference.clear() and then going through every item in the submitted form and running user.preference.add(the_new_preference)
You'll need to pass the complete list of categories and also an index of user-selected categories to your template. You don't need to convert the NewsCategory queryset into a list in your view, by the way:
View
categories = NewsCategory.objects.all()
user_preferences = [item.id for item in Profile.preference.all()]
The user_preferences variable will act as a lookup index for our template.
Then you loop through all the categories in the template, and check to see if it exists in the list of user preferences:
Template
<div id="c_b">
{% for c in categories %}
<input type="checkbox" name="category[]" id="id_{{ c.category }}" value="{{ c.id }}" {% if c.id in user_preferences %}checked="checked"{% endif %} />
<label for="id_{{ c.id }}">{{ c.category }}</label>
{% endfor %}
</div>
Update - saving user preferences
There is no hard and fast rule here. The main consideration, as far as I am concerned, would be minimising database hits. You can just clear the user preferences, like you say, and add the new ones - in fact, this is how Django's admin handles it. Just make use of Django's transaction management:
from django.db import transaction
#transaction.commit_manually
def add_preferences(user, preferences):
user.preference.clear()
for pref in preferences:
user.preference.add(pref)
transaction.commit()
You should learn about forms and model forms in django. It would be the best way for both adding and changing. Forms will do the most of job for you.

Categories