I'm trying to run a search on a model that has a many to many field, and I want to filter the search using this field.
here is my current code:
search_indexes.py
class ListingInex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
business_name = indexes.CharField(model_attr='business_name')
category = indexes.MultiValueField(indexed=True, stored=True)
city = indexes.CharField(model_attr='city')
neighborhood= indexes.CharField(model_attr='neighborhood')
state = indexes.CharField(model_attr='state')
address = indexes.CharField(model_attr='address')
zip_code = indexes.CharField(model_attr='zip_code')
phone_number = indexes.CharField(model_attr='phone_number')
def get_model(self):
return listing
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare_category(self, obj):
return [category.name for category in obj.category_set.all()]
listing_text.txt
{{ object.business_name }}
{{ object.state }}
{{object.city}}
{{object.zip_code}}
{{object.phone_number}}
{{object.neighborhood}}
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
I'm trying to do a simple search like:
search_results = SearchQuerySet().filter(category=query_text).filter
it returns an empty list. Thanks in advance.
Reading your code I think the problem is here:
{% for category in obj.category.all %}
{{ category.name }}
{% endfor %}
Category is not an object, is a MultiValueField. Maybe if you try something like:
{% for category in object.category.all %}
{{ category.name }}
{% endfor %}
object.category.all instead of obj.category.all. I'm not sure the solution is this, but I'm pretty sure the error could be in this 3 lines.
Related
I'm trying to display text and image on template from SQLite database.
Database model:
class NewProduct(db.Model):
id = db.Column(db.Integer, primary_key=True)
product_name = db.Column(db.String(100), nullable=False)
product_description = db.Column(db.String(200), nullable=False)
product_date = db.Column(db.DateTime, default=datetime.utcnow)
product_img = db.Column(db.BLOB())
def __repr__(self):
return '<NewProduct %r>' % self.id
Function to display information from db in .html:
#app.route('/products')
def products():
products = NewProduct.query.order_by(NewProduct.product_date.desc()).all()
product_img = b64encode(products.product_img).decode('utf-8')
return render_template('products.html', products=products, image=product_img)
HTML:
<body>
{% if products|length > 0 %}
{% for el in products %}
<p>Name: {{ el.product_name }}</p>
<p>Description: {{ el.product_description }}</p>
<p>Image: </p> <img src="data:;base64,{{ image }}">
<p>Date: {{ el.product_date }}</p>
{% endfor %}
{% else %}
<p>There are have no products</p>
{% endif %}
So, I'm get an error: AttributeError: 'list' object has no attribute 'product_img' in line product_img = b64encode(products.product_img).decode('utf-8')
I did it for this answer. But I anyway get this error. What I'm doing whong and how can I fix that?
products is a list so the line products.product_img raise the error you are facing. If you need to convert the image into something else you need to loop over the products and modify the image of each product.
You are trying to access product_img for a list of products, not on a single object. You need to iterate through the objects and update the value for each of them:
#app.route('/products')
def products():
products = NewProduct.query.order_by(NewProduct.product_date.desc()).all()
for product in products:
product.product_img = b64encode(product.product_img).decode('utf-8')
return render_template('products.html', products=products)
Then update your template to get the value from the product object:
<body>
{% if products|length > 0 %}
{% for el in products %}
<p>Name: {{ el.product_name }}</p>
<p>Description: {{ el.product_description }}</p>
<p>Image: </p> <img src="data:;base64,{{ el.product_img }}">
<p>Date: {{ el.product_date }}</p>
{% endfor %}
{% else %}
<p>There are have no products</p>
{% endif %}
I would also suggest to remove the prefix product_ from your variables, as it seems redundant and makes your code less readable.
I would like to fetch the names of the speakers in the template. I marked it with xxxx. How can I do this? Thank you so much for helping out. My files:
models.py
class City(models.Model):
name = models.CharField(max_length=100)
class Speaker(models.Model):
name = models.CharField(max_length=100)
url = models.URLField(max_length=100)
city = models.ManyToManyField(City, blank=True)
views.py
def home(request):
cities = City.objects.all().exclude(speaker__isnull=True)
return render(request, "index.html", {"cities":cities})
index.html
{% for i in cities %}
{{ i.name }},xxx{{ i.speaker.name }}xxx<br>
{% endfor %}
You can access the speakers for each city as speaker_set:
{% for cit in cities %}
{{ cit.name }}
{% for spkr in cit.speaker_set.all %}
{{ spkr.name }}
<br>
{% endfor %}
{% endfor %}
So I have these models:
excercises_choices = (('Bench Press', 'Bench press'),('Overhead Press', 'Overhead Press'), ('Squat', 'Squat'),
('Deadlift', 'Deadlift'))
unit_choices = (('kg','kg'), ('lbs', 'lbs'))
class Lifts(models.Model):
user = models.ForeignKey('auth.User', null=True)
excercises = models.CharField(max_length=200, choices=excercises_choices)
sets = models.IntegerField(null=True, blank=True)
reps = models.IntegerField(null=True, blank=True)
weight = models.FloatField()
unit = models.CharField(max_length=3, choices=unit_choices)
created_date = models.ForeignKey('Dates')
amrap_set = models.BooleanField(default=False)
amrap_rep = models.IntegerField(null=True, blank=True)
def __str__(self):
return self.excercises
class Dates(models.Model):
created_date = models.DateField(unique=True)
def __str__(self):
return str(self.created_date)
Let's say I have few lifts at different dates for admin and few lifts at different for xx user.
I want multiple lifts matching one date that's why I've made foreign key. (eg. 3 lifts to 2016-10-10 and 2 lifts to 2016-10-11).
Here is a view for showing it:
#login_required
def entries(request):
date = Dates.objects.all().order_by('-created_date')
lifts_by_user = Lifts.objects.filter(user=request.user)
return render(request, 'lift/entries.html', {'date': date,
'lifts_by_user': lifts_by_user})
And template:
{% extends 'lift/base.html' %}
{% block content %}
{{ user }}
{% if user.is_authenticated %}
{% for date in date %}
<p><strong>{{ date }}</strong>
{% for i in date.lifts_set.all %}
{{ i }}
{% endfor %}
add new lift</p>
{% endfor %}
{% endif %}
<p>
Delete lifts or dates
</p>
{% endblock %}
The problem is that I dont know how to separate it by dates AND by user.
This is how it looks like How do i keep this pattern date - lifts_to_that_date but for separate users? I dont want to see admin's entries while I am on test user
Have a look at the regroup template tag, it does exactly what you need.
You can do something like this in your view:
#login_required
def entries(request):
lifts_by_user = (Lifts.objects.filter(user=request.user)
.order_by('-created_date__created_date'))
return render(
request,
'lift/entries.html',
{'lifts_by_user': lifts_by_user}
)
And replace the for date in dates loop in your template with something like:
{% regroup lifts_by_user by created_date.created_date as lifts %}
<ul>
{% for day in lifts %}
<li>Date: {{ day.grouper }}
<ul>
{% for lift in day.list %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I've used a ul here so that it's easier to compare to the example in the docs, but obviously you can change the markup to whatever you need. It's important to know that regroup doesn't order its input, so you need to order by created_date in your view.
If you're using Django's dev version you can use this instead:
{% regroup lifts_by_user by created_date.created_date as lift_list %}
<ul>
{% for day, lifts in lift_list %}
<li>Date: {{ day }}
<ul>
{% for lift in lifts %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Which I think is a little clearer.
As an aside, none of this relies on having dates stored as a foreign key, but that's up to you.
Questions from comments:
order_by('-created_date__created_date') is joining Lifts to Dates through the Lifts.created_date foreign key and ordering by the Dates.created_date field. Have a look at https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships for details.
for day, lifts in lift_list is using tuple unpacking.
As a quick example:
t = (1, 2, 3)
# first, second, third will have values 1, 2, 3 respectively
first, second, third = t
{% regroup lifts_by_user by created_date.created_date as lifts_list %} produces a list of namedtuples (again, only in the dev version, if you're using 1.10 or earlier it's a list of dicts so you can't use this trick) so as you're iterating through lift_list you can unpack the date and list of lifts into separate variables.
If you have a Lift instance called lift, you can get the pk for its date by using lift.created_date_id. Accessing it where you have the date URL in your example template is a little trickier because you have to get a lift out of the regrouped date's list. Something like this:
{% regroup lifts_by_user by created_date.created_date as lifts %}
<ul>
{% for day in lifts %}
<li>Date: {{ day.grouper }}
{# day.list.0 gets the first lift for this day #}
Date PK: {{ day.list.0.created_date_id }}
<ul>
{% for lift in day.list %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I'm trying to understand the best way to display ForeignKey filtered data in a Django model.
I have three models reduced to this:
// models.py
class Publisher(models.Model)
def publisher_name = models.TextField()
def publisher_slug = models.SlugField()
def founded_year = models.IntegerField()
class Album(models.Model)
def album_name = models.TextField()
def publisher = models.ForeignKey('Publisher', related_name='albums')
class Song(models.Model)
def song_name = models.TextField()
def album = models.ForeignKey('Album', related_name='songs')
def published_year = models.IntegerField()
I have a URL that is composed of: /<publisher>/<published_year>/
The view I'm having trouble composing is supposed to be details like this:
Title of: Publisher.publisher_name
List of All Albums by the publisher: List of All songs from that album published the same year as the publisher__published_year: List of All songs from that album published as the url
The way, I've tried to do this that works right now is similar to this:
// views.py
class SongYearView(TemplateView):
def get_context_data(self, **kwargs):
context = super(SongYearView, self).get_context_data(**kwargs)
context['publisher'] = Publisher.objects.get(slug=kwargs['publisher_slug']
album_list=[]
for album in context['publisher'].albums.all():
single_album = dict()
single_album['album'] = album
single_album['publisher_year_song'] = album.songs.filter(published_year=context['publisher'].published_year)
single_album['filtered_year_song'] = album.songs.filter(published_year=kwargs['published_year']
album_list.append(single_album)
context['albums'] = album_list
return context
Then in the template I'm doing (with stripped out formatting)
// template.html
{{ publisher.name }}
{% for album in albums %}
{{ album.album.album_name }}
{% for song in album.publisher_year_song %}
{{ song.song_name }}
{% endfor %}
{% for song in album.filtered_year_song %}
{{ song.song_name }}
{% endfor %}
{% endfor %}
While this does work, it's not pretty and I'm pretty sure there are better ways of doing this.
This is an odd example, but just a basic example of my more detailed models. The way to think about it is Publisher -> Album -> Song or A -> B -> C. And I'm trying to get a view of all B items, that are only linked with a specific A item and then get two sets of C items for each B item, where one set is filtered on an A property and the other set is filtered on a passed argument from a URL.
I tried to get a custom model.Manager to help get this constructed, but didn't have much luck.
You could do add a custom template filter of_year:
#register.filter
def of_year(songs, year):
return songs.filter(published_year=year)
And change your template to
// template.html
{{ publisher.name }}
{% for album in publisher.albums %}
{{ album.album.album_name }}
{% for song in album.songs|of_year:publisher.founded_year %}
{{ song.song_name }}
{% endfor %}
{% for song in album.songs|of_year:filtered_year %}
{{ song.song_name }}
{% endfor %}
{% endfor %}
And clean your view:
// views.py
class SongYearView(TemplateView):
def get_context_data(self, **kwargs):
context = super(SongYearView, self).get_context_data(**kwargs)
context['publisher'] = Publisher.objects.get(slug=kwargs['publisher_slug'])
context['filtered_year'] = kwargs['published_year']
return context
Edit: rename the template filter
please help to get the file name.
I uploaded the images to the database. model:
class AvtoPark(models.Model):
title = models.CharField(max_length=100)
date = models.DateField()
image = models.FileField(upload_to='avtos/static/uploads/')
#classmethod
def get_all_entries(self):
return self.objects.all()
field "IMAGE" contains addresses like:
avtos/static/uploads/img1_b.png
template I bring them as follows:
{% for entrie in all_entries_avtopark %}
{{ entrie.image.url }}
{% endfor %}
the result is displayed:
avtos/static/uploads/img1_b.png
I need to display:
img1_b.png
In your model add this:
import os
...
class AVTPark(models.Model):
...
#property
def image_name(self):
return os.path.basename(self.image.name)
In your template:
{% for entrie in all_entries_avtopark %}
{{ entrie.image_name }}
{% endfor %}
Edited :
You can create your own method in your models.py class.
class AvtoPark(models.Model):
title = models.CharField(max_length=100)
date = models.DateField()
image = models.FileField(upload_to='avtos/static/uploads/')
#classmethod
def get_all_entries(self):
return self.objects.all()
def get_image_name(self):
# here you can apply your logic what you want to display
# try to do same like below i am doing
return str(self.image.url).split('/')[-1]
while in template you can call directly like :
{% for entrie in all_entries_avtopark %}
{{ entrie.get_image_name( }}
{% endfor %}