How to add Search bar for django template? - django-models

I need a search bar in my template on top of my table. The search bar should search based on any table parameter, and filter the enteries accordingly.
I implemented the search bar using CSS classes and I get it as I wanted.
Now here's the views.py code.
def jobs(request):
jobs = Jobb.objects.all()
search_term = ''
if 'search' in request.GET:
search_term = request.GET['search']
jobs = jobs.filter(position__icontains=search_term)
context = {
'jobs': jobs, 'search_term': search_term, 'job': 'active'
}
return render(request, 'Job_openings/jobb.html',context)
This code does the job for me , but the problem is that it only searches the enteries based on my model return value.
def __str__(self):
return self.position
Thus, I'm only able to search all enteries having some specific 'position'.
My model has other fields like 'date posted', 'company name'..
I want the search bar to work for all such fields.
Lets say I enter a company name, and I get all the results from the list.
How can I achieve this? Thanks in advance.

you can use Q objects to perform "OR" queries.
from django.db.models import Q
jobs = jobs.filter(
Q(position__icontains=search_term) |
Q(your_other_field__icontains=search_term) |
Q(....)
)
then, in your template, you can access it with loop, and use dot "." to access the fields like:
{% for job_item in jobs %}
{{ job_item.position }}, {{ job_item.your_other_field }}
{% endfor %}

Related

How can I exclude drafts from showing up in collections? Liquid/Eleventy

I'm building a website with Eleventy for the first time, and even though I have worked with Liquid for a while now, I just can't crack this one.
I want to simplify the architecture as much as possible. This is why I have assigned my collections to variables:
{% assign blogposts = collections.posts %}
so later on in the site I can just write a really short and sweet:
{% for post in blogposts %}...{% endfor %}
instead of assigning the collections just above it in a file.
Now here's my problem:
I would love to filter away posts marked as draft (using draft: true in the frontmatter). I thought I'd be able to do it like one of these (I've tried...)
{% assign blogposts = collections.posts | draft: false %}
{% assign blogposts = collections.posts | where: draft, false %}
{% assign blogposts = collections.posts | where: data.draft, false %}
{% assign blogposts = collections.posts_nl | where: "draft", "false" %}
Does anyone know how I can accomplish this? Unfortunately it feels to me like the version of liquid used in Eleventy is an older one, and the documentation I can find never seems to address how I can do this. I would really appreciate some help! :)
Instead of filtering the collection in your template, you can use the configuration API to create a filtered collection in your .eleventy.js file. This should work:
// .eleventy.js
eleventyConfig.addCollection('posts', collections => {
// get all posts by tag 'post'
return collections.getFilteredByTag('post')
// exclude all drafts
.filter(post => !Boolean(post.data.draft))
});
If you need both both the filtered list of posts as well as the complete list, you can also use different aliases:
// .eleventy.js
eleventyConfig.addCollection('posts', collections => {
return collections.getFilteredByTag('post');
});
eleventyConfig.addCollection('published_posts', collections => {
// get all posts by tag 'post'
return collections.getFilteredByTag('post')
// exclude all drafts
.filter(post => !Boolean(post.data.draft))
});

How to prevent duplicates when using ModelChoiceFilter in Django Filter and Wagtail

I am trying to use filters with a Wagtail Page model and a Orderable model. But I get duplicates in my filter now. How can I solve something like this?
My code:
class FieldPosition(Orderable):
page = ParentalKey('PlayerDetailPage', on_delete=models.CASCADE, related_name='field_position_relationship')
field_position = models.CharField(max_length=3, choices=FIELD_POSITION_CHOICES, null=True)
panels = [
FieldPanel('field_position')
]
def __str__(self):
return self.get_field_position_display()
class PlayerDetailPage(Page):
content_panels = Page.content_panels + [
InlinePanel('field_position_relationship', label="Field position", max_num=3),
]
class PlayerDetailPageFilter(FilterSet):
field_position_relationship = filters.ModelChoiceFilter(queryset=FieldPosition.objects.all())
class Meta:
model = PlayerDetailPage
fields = []
So what I am trying to do is create a filter which uses the entries from FIELD_POSITION_CHOICES to filter out any page that has this position declared in the inline panel in Wagtail.
As you can see in the picture down below, the filters are coming through and the page is being rendered. (These are 2 pages with a list of 3 field positions).
So Page 1 and Page 2 both have a "Left Winger" entry, so this is double in the dropdown. The filtering works perfectly fine.
What can I do to prevent this?
The solution should be something like this (Credits to Harris for this):
I basically have one FieldPosition object per page-field position, so it's listing all of the objects correctly. I suspect I should not use the model chooser there, but a list of the hard coded values in FIELD_POSITION_CHOICES and then a filter to execute a query that looks something like PlayerDetailPage.objects.filter(field_position_relationship__field_position=str_field_position_choice). But what is the Django Filter way of doing this?
Raf
In my limited simplistic view it looks like
class PlayerDetailPageFilter(FilterSet):
field_position_relationship = filters.ModelChoiceFilter(queryset=FieldPosition.objects.all())
is going to return all the objects from FieldPosition and if you have 2 entries for 'left wing' in here (one for page 1 and one for page 2) then it would make sense that this is duplicating in your list. So have you tried to filter this list queryset with a .distinct? Perhaps something like
class PlayerDetailPageFilter(FilterSet):
field_position_relationship = filters.ModelChoiceFilter(queryset=FieldPosition.objects.values('field_position').distinct())
I found the solution after some trial and error:
The filter:
class PlayerDetailPageFilter(FilterSet):
field_position_relationship__field_position = filters.ChoiceFilter(choices=FIELD_POSITION_CHOICES)
class Meta:
model = PlayerDetailPage
fields = []
And then the view just like this:
context['filter_page'] = PlayerDetailPageFilter(request.GET, queryset=PlayerDetailPage.objects.all()
By accessing the field_position through the related name of the ParentalKey field_position_relationship with __.
Then using the Django Filter ChoiceFilter I get all the hard-coded entries now from the choice list and compare them against the entries inside the PlayerDetailPage query set.
In the template I can get the list using the Django Filter method and then just looping through the query set:
<form action="" method="get">
{{ filter_page.form.as_p }}
<input type="submit" />
</form>
{% for obj in filter_page.qs %}
{{ obj }} >
{% endfor %}

Django Custom field's attributes making database queries

I am facing a very weird problem in one of my django projects. In my project I have a custom field class that handles foreign keys, one to one and many 2 many model fields. The class is some thing like the following.
from django import forms
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.model = model
self.limit = limit
self.multiple = multiple
self.create_objects = create_objects
super(CustomRelatedField, self).__init__(*args, **kwargs)
def clean(self, value):
""" Calls self.get_objects to get the actual model object instance(s)
from the given unicode value.
"""
# Do some value processing here
return self.get_objects(value)
def get_objects(self, values):
""" Returns the model object instances for the given unicode values.
"""
results = []
for value in values:
try:
obj = self.model.object.get_or_create(name=value)[0]
results.append(obj)
except Exception, err:
# Log the error here.
return results
def prepare_value(self, value):
""" Returns the value to be sent to the UI. The value
passed to this method is generally the object id or
a list of object id's (in case it is a many to many object).
So we need to make a database query to get the object and
then return the name attribute.
"""
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
return result
Recently while I was playing with the django-toolbar, I found out one of the pages that has a form with the above mentioned fields was ridiculously making multiple queries for the same objects again and again.
While debugging, I found out the prepare_value method was being called again and again. After some more debugging, I realized the culprit was the template. I have a generic template that I use for forms, It looks something like the following:
{% for field in form %}
{% if field.is_hidden %}
<!-- Do something here -->
{% endif %}
{% if field.field.required %}
<!-- Do something here -->
{% endif %}
<label>{{ field.label }}</label>
<div class="form-field">{{ field }}</div>
{% if field.field.widget.attrs.help_text %}
<!-- Do something here -->
{% elif field.errors %}
<!-- Do something here -->
{% endif %}
{% endfor %}
In the above code, each if statement calls the field class which calls the prepare_value method which then makes the database queries. Each of the following listed is making a database query, I am totally lost to why this is happening and have no clue about any solutions. Any help, suggestions would be really appreciated. Thanks.
field.is_hidden
field.field.required
field.label
field.label_tag
field
field.field.widget.attrs.help_text
field.errors
Also, why does this happen with my custom field class only, other fields (FKs, O2Os, M2M's) in the application and the application admin, just make one query, even though they are using a similar template.
Problem is with your prepare_value() method which does explicit queries. .get() does not get cached and always hits the db while iterating on .filter() queryset will evaluate that.
This might be causing you multiple queries.
This is not seen in default fields because they do not do any queries in prepare_value().
To resolve this, you can try to cache the value and result. If value hasn't changed, return cached result. Something like:
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.cached_result = None
self.cached_value = None
...
def prepare_value(self, value):
#check we have cached result
if value == self.cached_value:
return self.cached_result
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
#cache the result and value
self.cached_result = result
self.cached_value = value
return result
Not sure how good/bad this work around though!

How to query the number of elements stored in a db.ListProperty in google app engine

The entity class is defined like:
class Item(db.Model):
list = db.ListProperty(db.Key)
What's the attribute or function to return the number of elements stored in a ListProperty, so that I can use something like
{{ item.list.... }}
to display that in a html.
It's a list so you can use len() on it.
total = len(item.list)
or if you are using jinja on your html you can use the count filter
{{ item.list|count }}

Django ForeignKey on a View

i'm building a django app from an existing database. for better or worse, i have a couple of views that i would like to base my models off. they look something like this:
class Device(Model):
id = models.IntegerField( primary_key=True, db_column='node_id' )
name = models.CharField(max_length=127, db_column='node' )
class Meta:
db_table = 'node' # db view
managed = False
class Entity(Model):
id = models.IntegerField( primary_key=True, db_column='_id' )
device = models.ForeignKey(Device, db_column='node_id' )
class Meta:
db_table = 'entity' # db view
managed = Fase
so things work okay. however, when i try to use a template that utilises the ForeignKey it is very slow:
{% for e in entities %}
{{ e.device.name }}
{% endfor %}
looking at the logs, it appears to be repeating queries for each 'node_id', and ultimately timeout out.
(of course, if i do not include e.device.name is all quick)
is there a way i can optimise this?
to be fair, the 'entity' view already has the name of the device as another field (node), so i could use this instead but i would like the relation to exist.
have you try .select_related() in your view?
entities = Entity.objects.select_related('device').filter(...)
in the other hand, if it is and old database, and no autogenerated by django, probably node_id may not be an index in the database, this will slow down a lot any JOIN.

Resources