Skip to content

Allow alternatives to pk for object lookup #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ enriched_activities = enricher.enrich_activities(activities)

#### Change how models are retrieved

The enrich class that comes with the packages tries to minimise the amount of database queries. The models are grouped by their class and then retrieved with a pk__in query. You can implement a different approach to retrieve the instances of a model subclassing the ```stream_django.enrich.Enrich``` class.
The enrich class that comes with the packages tries to minimise the amount of database queries. The models are grouped by their class and then retrieved with a bulk query. You can implement a different approach to retrieve the instances of a model subclassing the ```stream_django.enrich.Enrich``` class.

To change the retrieval for every model you should override the ```fetch_model_instances``` method; in alternative you can change how certain models' are retrieved by implementing the hook function ```fetch_<model_name>_instances```

Expand All @@ -326,10 +326,10 @@ class MyEnrich(Enrich):
Overwrites how model instances are fetched from the database
'''

def fetch_model_instances(self, modelClass, pks):
def fetch_model_instances(self, modelClass, lookup_values):
'''
returns a dict {id:modelInstance} with instances of model modelClass
and pk in pks
returns a dict {lookup_value:modelInstance} with instances of model modelClass
and lookup_value in lookup_values
'''
...

Expand All @@ -338,7 +338,7 @@ class AnotherEnrich(Enrich):
Overwrites how Likes instances are fetched from the database
'''

def fetch_like_instances(self, pks):
def fetch_like_instances(self, lookup_values):
return {l.id: l for l in Like.objects.cached_likes(ids)}

```
Expand Down
24 changes: 21 additions & 3 deletions stream_django/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,30 @@ def create_model_reference(model_instance):
creates a reference to a model instance that can be stored in activities

>>> from core.models import Like
>>> like = Like.object.get(id=1)
>>> like = Like.object.get(<lookup_field>=1) where lookup_field defaults to 'pk'
>>> create_reference(like)
core.Like:1

'''
try:
field_name = model_instance.activity_lookup_field()
content_id = getattr(model_instance, field_name)
except AttributeError:
content_id = model_instance.pk

content_type = model_content_type(model_instance.__class__)
content_id = model_instance.pk
return '%s:%s' % (content_type, content_id)


class Activity(object):

@classmethod
def activity_lookup_field(cls):

'''
The name of the field to use for model lookups
'''
return 'pk'

@property
def activity_author_feed(self):
Expand Down Expand Up @@ -78,7 +91,12 @@ def activity_object_attr(self):

@property
def activity_actor_id(self):
return self.activity_actor_attr.pk
try:
field_name = self.activity_actor_attr.activity_lookup_field()
actor_id = getattr(self.activity_actor_attr, field_name)
except AttributeError:
actor_id = self.activity_actor_attr.pk
return actor_id

@property
def activity_actor(self):
Expand Down
30 changes: 23 additions & 7 deletions stream_django/enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import operator
import itertools


try:
from django.apps import apps
from django.core.exceptions import FieldDoesNotExist
get_model = apps.get_model
except ImportError:
from django.db.models.loading import get_model
Expand Down Expand Up @@ -91,18 +93,23 @@ def _collect_references(self, activities, fields):
model_references[f_ct].append(f_id)
return model_references

def fetch_model_instances(self, modelClass, pks):
def fetch_model_instances(self, modelClass, lookup_values):
'''
returns a dict {id:modelInstance} with instances of model modelClass
and pk in pks
returns a dict {<lookup_value>:modelInstance} with instances of model modelClass
and lookup_value in lookup_values (i.e., {1:<obj_1>} or {<uuid>:<obj_2>})
'''
try:
lookup_field_name = modelClass.activity_lookup_field()
except AttributeError:
lookup_field_name = 'pk'

hook_function_name = 'fetch_%s_instances' % (modelClass._meta.object_name.lower(), )
if hasattr(self, hook_function_name):
return getattr(self, hook_function_name)(pks)
return getattr(self, hook_function_name)(lookup_values)
qs = modelClass.objects
if hasattr(modelClass, 'activity_related_models') and modelClass.activity_related_models() is not None:
qs = qs.select_related(*modelClass.activity_related_models())
return qs.in_bulk(pks)
return qs.in_bulk(lookup_values, field_name=lookup_field_name)

def _fetch_objects(self, references):
objects = defaultdict(list)
Expand All @@ -119,8 +126,17 @@ def _inject_objects(self, activities, objects, fields):
continue
f_ct, f_id = activity[field].split(':')
model = get_model(*f_ct.split('.'))
f_id = model._meta.pk.to_python(f_id)


try:
lookup_field_name = model.activity_lookup_field()
if lookup_field_name == 'pk':
f_id = model._meta.pk.to_python(f_id)
else:
lookup_field = model._meta.get_field(lookup_field_name)
f_id = lookup_field.to_python(f_id)
except (AttributeError, FieldDoesNotExist):
f_id = model._meta.pk.to_python(f_id)

instance = objects[f_ct].get(f_id)
if instance is None:
activity.track_not_enriched_field(field, activity[field])
Expand Down