I am sorry if it question might turn to be little broad, but since I am just learning django (and I am just hobbyist developer) I need some guidance which, I hope, will help someone like me in the future since I could not find any clear and easily comprehensible guide on using this widget. With your answers and help I will try to make this question thread at least guide-ish.
Material I found somewhat helpful for this topic:
Django multi-select widget?
Django: Replacement for the default ManyToMany Widget of Forms
Django's FilteredSelectMultiple widget only works when logged in
Django FilteredSelectMultiple not rendering on page
Use the Django admin app's FilteredSelectMultiple widget in form
Get the chosen values from FilteredSelectMultiple widget in Django
Django FilteredSelectMultiple Right Half Does Not Render
There were few others links, but they did not made anything clearer or added new information so I won't mention them.
Here is what I managed to understand (please correct me if I am wrong or add anything that I missed):
To create FilteredSelectMultiple
widget firs I need to amend forms.py
(as in any other widget creation process). Amended forms.py
should have from django.contrib.admin.widgets import FilteredSelectMultiple
import and Media
class. forms.py
code should look like this (please correct me, because probably it is wrong somewhere):
from django import forms
from catalog.models import DrgCode
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.conf import settings #without it I get an error that settings not defined
class CalculatorForm(forms.Form):
drg_choice = forms.ModelMultipleChoiceField(queryset=DrgCode.objects.all(), widget=FilteredSelectMultiple("Somethings", is_stacked=False), required=True)
class Media:
css = {
'all': (os.path.join(settings.BASE_DIR, '/static/admin/css/widgets.css'),),
}
js = ('/admin/jsi18n',)
Questions about this part:
django.conf
import? Since I did not see it
imported in any material I found. Answer: during my test I determined that django.conf
import is necessary if using settings.BASE_DIR
part. In various sources there was two ways of writing css
path, this one worked for me.
widgets.css
and corresponding directory? Or
django will find it itself? Since there is no such file or directory
generated after I created skeleton-website using django-admin
startproject
cmd? Answer: No. There is no need to create widgets.css
or any of the files since django finds them itself.
jsi18n
part. Also what is this? I
assume its javascript file, but it has no extension for some reason.
Also I cannot find it anywhere? Should I create it? How to do that?
Or I can copy it from somewhere? Partial answer: no need to create it. Just point at it in urls.py
. Still do not know exactly that kind of file it is (or where it is)
After amending forms.py
I should ammend urls.py
by adding url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', name='jsi18n')
So urls.py
now look like this:
from django.urls import path
from . import views
from django.conf.urls import url
urlpatterns = [
path('', views.index, name='index'),
'django.views.i18n.javascript_catalog',
name='jsi18n'),
]
Questions:
urlpatterns
? Answer: This method if fine.
Now I need to set HTML
template file for form to render (like in any other case). Code for it (file named DrgCalculator.html
):
{% extends "base_generic.html" %}
<script type="text/javascript" src="{% url 'jsi18n' %}" > </script>
{{ form.media }}
<form enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Submit</button>
</form>
Lastly I need to adjust views.py
to set where this form and everything related with it happens.
From what I understand code in this part is more or less is not directly related with widget, but to complete everything and make working example I will use code I leaned/got in this (very good) django tutorial:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from catalog.models import DrgCode
from catalog.forms import CalculatorForm
def DrgCalculator(request):
if request.method == 'POST':
form = CalculatorForm(request.POST)
if form.is_valid():
return render(request, 'DrgCalculator.html')
context = {
'form': form,
}
return render(request, 'DrgCalculator.html', context)
Questions:
else: form = DrgCalculator()
. Will write amended code below.
How I will access values which user choose using
FilteredSelectMultiple
? I imagine I should clean data in
forms.py
like with other widgets. So I should add nested function
below to my class CalculatorForm
in forms.py
am I right? Answer: Yes, data should be cleaned like in other cases. Function is correct.
def clean_CalculatorForm(self):
drg_choice = self.cleaned_data['drg_choice']
return drg_choice
Data I will get after cleaning will be list or dictionary? Am I right? Answer: No, from this widget user input received as QuerySet
That is all my questions, sorry for long thread, I tried to make it as clear as possible. If I need to clarify something, please let me know. I will try to edit and update this, to make it friendly for people who will read it in future.
EDIT1: Answered some of my questions.
EDIT2: Answered rest of my questions.
After spending few days of research and testing I managed to get FilteredSelectMultiple
widget working outside admin page in user form. As promised in question, I will try to synthesize my accumulated knowledge into some sort of guide which, I hope, will help someone like me in the future. Please note that I am far from professional (just a hobbyist with no computer engineering background to be precise) so my observations might not be exactly correct or the way I done it might not the best one. Despite that it might help you to get on the right track.
So to begin with FilteredSelectMultiple
widget first of all - forms.py
have to be amended by importing widget, creating field for it (similar like with regular widgets) and adding nested Media
class. Code example:
from django.contrib.admin.widgets import FilteredSelectMultiple
class DrgCalculator(forms.Form):
drg_choise = forms.ModelMultipleChoiceField(queryset=DrgCode.objects.all(),
label="Something",
widget=FilteredSelectMultiple("Title", is_stacked=False),
required=True)
class Media:
css = {
'all': ('/static/admin/css/widgets.css',),
}
js = ('/admin/jsi18n',)
def clean_drg_choise(self):
drg_choise = self.cleaned_data['drg_choise']
return drg_choise
As I determined during my test and research class Media
should be copied as written and require no changes for widget to work. Files mentioned in this class will be found by django itself so no need to search and copy them (like told in some of the material I read).
After creating form urls.py
should be amended. During testing I found out, that in newer django versions (or at lest one I used) javascript_catalog
is renamed and url provided cannot be string. So code should look like this:
from django.urls import path
from . import views
from django.conf.urls import url
from django import views as django_views
urlpatterns = [
url(r'^jsi18n/$', django_views.i18n.JavaScriptCatalog.as_view(), name='jsi18n'),
]
Now for the htlm
template I am sure that there is more ways of doing it so I just provide example:
{% extends "base_generic.html" %}
{% block content %}
<div id='frame'>
<form action="" method="post">
<div id='sk_body'>
<fieldset>
<legend>Fill required fields</legend>
<form>
{% csrf_token %}
<table>
{{ form.media }}
{{ form.as_table }}
<script type="text/javascript" src="{% url 'jsi18n' %}"></script>
</table>
<input type="submit" value="Count">
</form>
</fieldset>
</div>
</form>
</div>
{% endblock %}
To receive data from this widget fairly standard code in views.py
should be used, example code I used:
def DRG_calcualtor(request):
if request.method == 'POST':
form = DrgCalculator(request.POST)
if form.is_valid():
choosen_drg = form.cleaned_data['drg_choise'] #result as QuerySet
choosen_drg_list = list([str(i) for i in choosen_drg]) #you can convert it to list or anything you need
return render(request, 'DRGcalculator_valid.html')
context = {
'form': form,
}
return render(request, 'DRGcalculator.html', context)
else:
form = DrgCalculator()
context = {
'form': form,
}
return render(request, 'DRGcalculator.html', context)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With