GetStream (Django) - Cannot Enrich Notification Feed - python

I am using the GetStream Django package to interact with getstream.io. I have had success using the Enricher() class to enrich my activity feed with Django model information for the feed_manager.get_user_feed(), but cannot get similar results with feed_manager.get_notification_feed()
Here is a shortened version of my model.
class Task(models.Model, Activity):
title = models.CharField()
assigned_to = models.ForeignKey(User)
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User)
#property
def activity_object_attr(self):
return self
#property
def activity_actor_attr(self):
return self.assigned_to
#property
def activity_notify(self):
return [feed_manager.get_notification_feed(self.assigned_to.id),]
If my view grabs the user feed:
enricher = Enrich()
feed = feed_manager.get_user_feed(request.user.id)
# feed = feed_manager.get_notification_feed(request.user.id)
activities = feed.get(limit=25)['results']
enriched_activities = enricher.enrich_activities(activities)
My output works as expected, and each of these gets populated with the proper data in my template:
Actor: {{ activity.actor }}<br>
Title: {{ activity.title }}<br>
Time: {{ activity.time|timesince }}<br>
However, if I switch to the notification feed (note the change in commenting out of lines):
enricher = Enrich()
# feed = feed_manager.get_user_feed(request.user.id)
feed = feed_manager.get_notification_feed(request.user.id)
activities = feed.get(limit=25)['results']
enriched_activities = enricher.enrich_activities(activities)
Then, the only field I can get data from is activity.created_at.
The output of activity.keys shows the following:
[u'activities', u'group', u'activity_count', u'created_at', u'updated_at', u'actor_count', u'verb', u'is_seen', u'id', u'is_read']
It seems like perhaps for the notification feed, the actor and object are NOT being reported back to GetStream:
But, it is for the user feed:
I am stumped as to why. What am I missing?

Okay ... so I figured out my mistake. I was operating under the assumption all along that the notification_feed was a flat feed. Whoops, by bad. It is actually aggregated. Therefore, I was able to make fixes as follows:
Instead of:
activities = enricher.enrich_activities(activities)
I used:
enriched_activities = enricher.enrich_aggregated_activities(activities)
Then, in my base template:
{% for enriched_activity in enriched_activities %}
{% render_activity enriched_activity %}
{% endfor %}
Which looks in /activity/aggregated/task.html
{% for activity in enriched_activity.activities %}
{% render_activity activity %}
{% endfor %}
And finally inside /activity/task.html I see the output as expected from these items.
Actor: {{ activity.actor.first_name }}<br>
Title: {{ activity.object.title }}<br>
Time: {{ activity.time|timesince }}<br>

Related

Filtering a reverse lookup in Django template

I have a model that looks like this
class Invoice(models.Model):
inv_number = models.CharField(max_length=10, primary_key=True)
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
inv_date = models.DateField()
class Txn(models.Model):
invoice = models.ForeignKey(Invoice, on_delete=models.PROTECT)
transaction_date = models.DateField()
reference = models.CharField(max_length=12)
amt = models.IntegerField()
I would like to display a report in my template that lists filtered invoices each with a sub-list of filtered transactions.
In my view I have done the following:
invoice_list = Invoice.objects.filter(customer=customer)
Which I pass into my template. In the template, I do something like the following:
{% for invoice in invoice_list %}
{{ invoice.inv_number }}, {{ invoice.customer}}, {{ invoice.inv_date }}
{% for txn in invoice.txn_set.all %}
{{ txn.transaction_date }}, {{ txn.reference }}, {{ txn.amt }}
{% endfor %}
{% endfor %}
This works great to show the entire transaction list per filtered invoice. The question is, how does one also filter the list of transactions per invoice in the template - what if I wanted just the transactions within a certain date range or that match a specific reference? Is there maybe a way to pass a filter to the txn_set queryset per invoice in the view before putting the main queryset in the context and without converting them to lists?
Thank you for any response!
Suggestion: collect the invoices and transactions in the view, not in the template.
With this view code you can reduce the amount of queries to 1, so it is a lot more optimal than your code (with queries the Txn table for each invoice):
# build basic query
qs = Txn.objects.select_related('invoice')\
.filter(invoice__customer=customer)\
.order_by('transaction_date')
# add filtering; here is an example, but it could be with date filter as well
reference = request.GET.get('reference', '')
if len(reference) > 0:
qs = qs.filter(reference__icontains=reference)
invoice_dict = {}
for txn in qs:
# add the Invoice if it does not exist yet
if txn.invoice_id not in invoice_dict:
invoice_dict[txn.invoice_id] = {
'invoice': txn.invoice,
'txn_list': [],
}
# add the Txn
invoice_dict[txn.invoice_id]['txn_list'].append(txn)
# sort by Invoice date
invoice_data_list = sorted(
invoice_dict.values(),
key=lambda x: x['invoice'].inv_date)
Then in your template:
{% for elem in invoice_data_list %}
{{ elem.invoice.inv_number }}
{{ elem.invoice.customer}}
{{ elem.invoice.inv_date }}
{% for txn in elem.txn_list %}
{{ txn.transaction_date }}
{{ txn.reference }}
{{ txn.amt }}
{% endfor %}
{% endfor %}

How to display multiple ForeignKey filtered items in a single view in django?

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

how to get the file name?

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 %}

Django cannot access all objects in a model

My view looks like this:
class PageView(DetailView):
queryset = Page.objects.all()
template_name = 'page.html'
def get_context_data(self, **kwargs):
context = super(PageView, self).get_context_data(**kwargs)
context['category'] = Category.objects.all()
context['categoryitem'] = CategoryItem.objects.all()
return context
When in template I try to execute the given context variables like so {{ category }} it prints out [<Category: Something not so interesting>] aka model name + it's title, I presume the title gets printed out because I've set __unicode__(self): return self.title in the model.py, but I cannot access any other fields from the given object. category.id is blank as so is everything else. How can I access those?
Your code is:
context['category'] = Category.objects.all()
So it should be:
context['categories'] = Category.objects.all()
And in your template:
{% for category in categories %}
{{ category.name }}
{% endfor %}
The output you got in your test makes sense:
[<Category: Something not so interesting>]
it's an array with only one entry, this entry is an object of the class Category, and the string representation of it is "Something not ..."
You need to iterate over the category, since it's queryset. E.g. in your template, you can do
<ul>
{% for c in category %}
<li> {{ c }} </li>
{% endfor %}
</ul>
category in template is queryset ( list of objects) not one single object. You need to iterate over it as
{%for c in category %}
{{c.id}} : {{ c.other_attribute }}
{%endfor%}

Haystack search on a many to many field is not working

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.

Categories