I have a problem in my project.
I want to create a ChoiceField to be categories and subcategories in the models.py file. Well, it must be that I chose the first choice and the second according to the index of the place. For example, like the car and its models, according to the brand I chose in the First ChoiceField, you look at its models in the second field. İn Django.
Welcome to Stack Overflow! I've done this in the past by creating my own widget, a CategorizedSelect, with some jQuery. Here's the code and comments:
my_app/widgets.py:
from django.forms.widgets import Select
class CategorizedSelect(Select):
"""
This widget takes a dict of categories and a list of dicts for items.
It renders two drop downs, the first of the categories, which then
populates the second drop down with the items in that category via
the jQuery chained library.
For example:
# Prepare data for chained dropdown field for security
intl_stocks = Security.objects.filter(
active=True,
security_type__short_name="INT",
).select_related(
'exchange',
).order_by(
'exchange__name', 'name',
)
exchanges, securities = OrderedDict(), []
for intl_stock in intl_stocks:
exchanges[intl_stock.exchange.id] = intl_stock.exchange.name
securities += [{'category_id': intl_stock.exchange.id, 'id': intl_stock.id, 'name': intl_stock.name}]
security = forms.ModelChoiceField(
queryset=Security.objects.none(),
widget=CategorizedSelect(
categories=exchanges,
items=securities,
),
)
"""
template_name = 'my_app/widgets/categorized_select.html'
def __init__(self, attrs=None, categories=None, items=None):
super().__init__(attrs)
self.categories = categories
self.items = items
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['categories'] = self.categories
context['items'] = self.items
return context
my_app/templates/my_app/widgets/categorized_select.html:
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-chained/1.0.1/jquery.chained.min.js"></script>
<select id="chain_select_{{ widget.name }}_id" name="chain_select_{{ widget.name }}" class="form-control">
<option value="">--</option>
{% for id, name in categories.items %}
<option value="{{ id }}">{{ name }}</option>
{% endfor %}
</select>
<br>
<select name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
<option value="">--</option>
{% for item in items %}
{% comment %}
If we match the initial value set by the Django form, we need to
set SELECTED on the child, and use jQuery to force the category parent
drop down to the proper selection as well. We will force item.id to a
string since the widget.value.0 is always a string.
{% endcomment %}
{% if item.id|stringformat:"i" == widget.value.0 %}
<option value="{{ item.id }}" class="{{ item.category_id }}" SELECTED>{{ item.name }}</option>
<script>
var selected_category_id = {{ item.category_id }}
</script>
{% else %}
<option value="{{ item.id }}" class="{{ item.category_id }}">{{ item.name }}</option>
{% endif %}
{% endfor %}
</select>
<script>
$("#{{ widget.attrs.id }}").chained("#chain_select_{{ widget.name }}_id");
if(selected_category_id) {
$('#chain_select_{{ widget.name }}_id').val(selected_category_id).change();
$('#chain_select_{{ widget.name }}_id').prop("disabled", true);
$('#{{ widget.attrs.id }}').prop("disabled", true);
}
</script>
The example in the comment shows how this was done for a set of international stocks; the international exchanges were the categories, with the stocks themselves being the items. Good luck!
Related
I'm doing a search form where an user selects the category and the type of product which is a choice field of the Product model, product_type = models.CharField(max_length=30, choices=TYPE, default='Physical'). I can get the categories but not the choices in TYPE to add to the form.
choices.py
TYPE = [
('PHYSICAL', _('Physical')),
('DIGITAL', _('Digital')),
]
views.py
from .choices import TYPE
def is_valid_queryparam(param):
return param != '' and param is not None
def FilterView(request):
qs = Product.objects.all()
categories = Category.objects.all()
ptypes = Product.product_type.all() # The problem line
category = request.GET.get('category')
ptype = request.GET.get('ptype')
if is_valid_queryparam(category) and category != 'Choose...':
qs = qs.filter(category__name=category)
if is_valid_queryparam(ptype) and ptype != 'Choose...':
qs = qs.filter(product_type=ptype)
context = {
'queryset': qs,
'categories' : categories,
'ptypes' : ptypes,
}
return render(request, 'categories/display_query.html', context)
base.html
<div>
<h3>Advanced Search</h3>
<form method="GET" action="{% url 'filter-view' %}">
<div>
<label for="category">Category</label>
<select name="category">
<option selected>Choose...</option>
{% for cat in categories %}
<option value="{{ cat }}">{{ cat }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="ptype">Product type</label>
<select name="ptype">
<option selected>Choose...</option>
{% for type in ptype %}
<option value="{{ type }}">{{ type }}</option>
{% endfor %}
</select>
</div>
<button type="submit">Search</button>
</form>
</div>
display_query.html
{% extends 'products/base.html' %}
{% block content %}
<div>
<ul>
{% for product in queryset %}
<li>
{{ product.title }}
<span>Author: {{ product.author.username }}</span>
</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
You can use the given code:
p_types_keys = [i[0] for i in TYPE]
p_types_values = [i[1] for i in TYPE]
ptype_dict = dict(zip(p_types_keys, p_types_values))
In the line will return all the keys of your "TYPE"
The second line will return all the keys of your "TYPE"
The Third line will return the key-value pair of your types to add form.
I am currently doing a search using forms
This is my views.py
class HomeView(generic.ListView):
model = Consultant
template_name = 'sogeti/home.html'
def get_queryset(self):
query = self.request.GET.get('q')
if query:
return Consultant.objects.filter(
Q(first_name__icontains=query) |
Q(last_name__icontains=query) |
Q(group__title_techGroup__contains=query) |
Q(practices__title_practice__contains=query)
)
else:
return Consultant.objects.all()
and this is my home.html
<form action="" method="get" class="form-inline">
<input type="text" name="q" placeholder="Enter Keyword" value="{{ request.GET.q }}" class="form-control">
<select name="filter" class="form-control">
<option value="all">All</option>
<option value="people">People</option>
<option value="certification">Certification</option>
<option value="skillset">Skillset</option>
</select>
<input type="submit" value="Search" class="btn btn-default">
</form>
<ol style="padding-left: 15px">
{% for consultant in object_list %}
<li>
{{ consultant.first_name }}, {{ consultant.last_name }} </br>
Technology Group: {{ consultant.group }} </br>
Primary Practice: {{ consultant.practices }}
<hr style="margin-left: 0">
</li>
{% endfor %}
</ol>
My first problem is that when it tries to search something (Eg: bla) that is not in my database, it returns a blank screen. Nothing at all. Tried searching but could not get any answers.
My second problem is how am I able to specify my search using HTML select and options to filter. As you can see from my home.html I have the tag with option value but no idea how to utilize it for Django.
Thank you so much for your help! Really appriciate it.
About first issue, you actually can double check the object_list before iterate over it, e.g:
{% if object_list %}
<ul>
{% for item in object_list %}
<p>{{item.value}}</p>
{% endfor %}
</ul>
{% else %}
<p>Empty!</p>
{% endif %}
If you're unable to search, then double check your query by using some tools like django-debug-toolbar to take a look at the queries.
About the second question, I recommend you to use Django Form instead, like so:
Create forms.py:
from django.forms import Form, ChoiceField, CharField
class FilterForm(Form):
FILTER_CHOICES = (
('all', 'All'),
('people', 'People'),
('certification', 'Certification'),
('skillset', 'Skillset'),
)
search = CharField(required=False)
filter_field = ChoiceField(choices=FILTER_CHOICES)
Then your view (views.py):
class HomeView(ListView):
model = Consultant
template_name = 'home.html'
def get_queryset(self):
query = self.request.GET.get('search')
filter_field = self.request.GET.get('filter_field')
# Do your filter and search here
return Consultant.objects.all()
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = FilterForm(initial={
'search': self.request.GET.get('search', ''),
'filter_field': self.request.GET.get('filter_field', ''),
})
return context
and finally your template (templates/home.html):
<form class="row">
{{ form.search }}
{{ form.filter_field }}
<input type="submit" class="btn btn-default" value="Search">
</form>
Hope that would be helpful!
For the first issue, if there is nothing in the database it is returning an empty queryset so if you include
{% if object_list %}
do stuff
{% else %}
No results match your search
{% endif %}
For the second it depends on what you're trying to do, but let's suppose it is a
specific field you're searching.
search_choice = self.request.GET.get('filter')
choice_query = Q(search_field=search_choice)
Then simply add this to your query
views.py
class HomeView(generic.ListView):
model = Consultant
template_name = 'sogeti/home.html'
def get_queryset(self):
queryset = super().get_queryset()
q = self.request.GET.get('q')
if q:
queryset = queryset.filter(
Q(first_name__icontains=q) |
Q(last_name__icontains=q) |
Q(group__title_techGroup__contains=q) |
Q(practices__title_practice__contains=q)
)
filter = self.request.GET.get('filter')
if filter == 'people':
pass
elif filter == '...':
# do sth
return queryset
html
<form action="" method="get" class="form-inline">
<input type="text" name="q" placeholder="Enter Keyword" value="{{ request.GET.q }}" class="form-control">
<select name="filter" class="form-control">
<option value="all">All</option>
{% if request.GET.filter == 'people' %}
<option value="people" selected>People</option>
{% else %}
<option value="people">People</option>
{% endif %}
{% if request.GET.filter == 'certification' %}
<option value="certification" selected>Certification</option>
{% else %}
<option value="certification">Certification</option>
{% endif %}
{% if request.GET.filter == 'skillset' %}
<option value="skillset" selected>Skillset</option>
{% else %}
<option value="skillset">Skillset</option>
{% endif %}
</select>
<input type="submit" value="Search" class="btn btn-default">
</form>
<ol style="padding-left: 15px">
{% for consultant in object_list %}
<li>
{{ consultant.first_name }}, {{ consultant.last_name }} </br>
Technology Group: {{ consultant.group }} </br>
Primary Practice: {{ consultant.practices }}
<hr style="margin-left: 0">
</li>
{% empty %}
No result match q=`<b>{{ request.GET.q }}</b>` & filter=`{{ request.GET.filter }}`
{% endfor %}
</ol>
I want to setup filter in templates
I have filtered objects that I wanted, and now how can I setup the template?
So I can filter objects through dropdown?
What I'have done so far
#login_required
def list_jobs(request):
assigned_jobs = Job.objects.filter(assign_to=request.user).order_by('-date_created')
created_jobs = Job.objects.filter(user=request.user).order_by('-date_created')
completed_jobs = Job.objects.filter(completed=False).order_by('-date_completed')
return render(request, 'jobs/list.html', {'created_jobs': created_jobs, 'assigned_jobs': assigned_jobs,
'completed_jobs': completed_jobs})
Example Something like this:
<select id="id">
{% for i in assigned_jobs %}
<option value="{{ i.id }}">{{ i.name }}</option>
{% endfor %}
</select>
I'm learning Python. I would like display results based on multiple selections from dropdowns.
The following code is working and returning the correct results for one dropdown. I would like to add at least one more dropdown, hopefully multiple, but I cannot fathom how to achieve passing and returning the multiple vars.
Form code
<p> <form method="get" action="">
<select name="search3">
{% for cs in course_list %}
<option value="{{ cs.pk }}">{{ cs.name }}</option>
{% endfor %}
</select>
<br/><br/>
<input type=submit value="Find staff" />
</form>
</p>
{% if employee_list %}
{{ employee_list.count }} employees matching : <strong>{{ course }}</strong><br/><br/>
{% for e in employee_list %}
{% if not e.img1 %}
<img src="/media/images/null_image.png" width="35" title="{{ e.first_name}} {{e.surname }}" />
{% else %}
<img src="{{ e.img1.url }}" width="50" title="{{ e.first_name}} {{e.surname }}"/>
{% endif %}
<a href="/employee/{{ e.slug }}" target="_blank" rel="popup" title="Details...<br />
{% for det in employee_list.employeedynamic_set.all %}
{{ det.mobile }}<br />
{{ det.depot }}<br />
{% endfor %}
"> {{ e.first_name }} {{ e.surname }}</a><br/>
{% endfor %}
{% else %}
<p>No employees matched: <strong>{{ course }}</strong></p>
{% endif %}
views.py code
# function to list training courses and participating uindividuals.
def CourseView(request):
course_list = Course.objects.all().order_by('name')
if 'search3' in request.GET:
search3 = request.GET['search3']
course = Course.objects.get(pk=search3)
else:
search3 = None
course = None
return render_to_response("course_display.html", {'course':course, 'course_list': course_list, 'search3': search3 })
Is it possible to add an another dropdown option or even multiple dropdown options and get a result. Or am I barking up the wrong tree?
UPDATE.
In order to pass one or multiple variables back to my function the code is simply the following
def CompetencyCheck(request):
course_list = Course.objects.all()
if 'search3' in request.GET:
search3 = request.GET.getlist('search3')
course = Course.objects.filter(id__in=search3).distinct().order_by()
.get() allows only the last variable passed from the web page form to be read/passed back to the function.
On the other hand, the .getlist() allows one or multiple vars to be passed back. Great, part the of problem solved.
The list of names being returned is unique in that if var1 returns 4 names and var2 returns 8 names and both vars have one common name, a list of 11 names in returned.
I want only the common names to both vars returned, i.e. 1 name to be returned.
Any ideas?
request.GET in your view is a dictionary; by doing request.GET['search3'] you're just accessing the value of its search3 key. So in your template, you should be able to just add other <select> elements with their own names and retrieve their submitted values from request.GET in the same way.
you can query using course_list only.
for example:
course_list = Course.objects.all().order_by('name')
var1 = request.POST.get('var1')
if var1:
course_list = course_list.objects.filter(some = var1)
var2 = request.POST.get('var2')
if var2:
course_list = course_list.objects.filter(some = var2)
Have a form where user can change name on attribut Name and change which attribut a is connected to (attribut b).
Template:
"<form id="form1" name="form1" method="post" action="/define_a/{{c.id}}/edit/">
{% csrf_token %}
{% endblock %}
{% block buttons %}
<p><input type="submit" value="Save" /> Cancel
</p>
{% endblock %}
{% block a_rows %}
{% for a, a_form in a_list %}
<tr><td><img class="icon" src="{{images_dir}}/delete-icon.png"
onclick="javascript: return confirmDelete_name('Are you sure?
This will delete the stuff and all associated information.
The removal happens immediately and cannot be undone.', '{{a.id}}', 'delete');" />
</td><td>{{a_form.name}}</td>
<td>
<select name= "test">
<option value = "Null">None</option>
<option value = "{{a_form.id}}" selected>{{a.b}}</option>
{% for a, a_form in a_list %}
<option value = "{{a_form.id}}">{{a.name}}</option>
{% endfor %}"
View:
Checking that it is a post and that it is valid.
post = [myForm(request.POST, instance = test) for a in a's];
for p in post :
if not new_t.b == p:
if p == 'None':
new_t.b = None;
else:
new_t.b = p;
But i can't get all the values from the dropdown in the post.
I get all a.name in but only one value from the dropdown, sometimes I don't get any value at all.
Any ideas?