Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find related objects and display relation

I am using django-follow to allow users to "follow" objects - in this example, Actors in films.

I am pulling back a list of film actors using

 actors_user_is_following = Follow.objects.get_follows(Actor).filter(user=request.user.id)

But what I also want to do is suggest films to the user based on the actors they are following. This does not need to be a complex algorithm of what they already like and suggesting relative films, just a simple "because you follow this actor and this actor is in this film, suggest it to the user"

I have this rather clunky way of doing this right now...

    context['follows'] = {
        'actors': Follow.objects.get_follows(Actor).filter(user=request.user.id),
        'genres': Follow.objects.get_follows(Genre).filter(user=request.user.id),
    }

    actor_ids = []
    for actor in context['follows']['actors']:
        actor_ids.append(actor.target_artist_id)

    genre_ids = []
    for artist in context['follows']['genres']:
        genre_ids.append(artist.genre_ids)

    context['suggested'] = {
        'films': Listing.objects.filter(Q(actors__in=actor_ids) | Q(genres__in=genre_ids))
    }

Which works, but I'm sure there is a better way of doing it?

Most importantly I also want to show the user why that film as been recommended by displaying the actors or genres it features that the user is following, so the end result might be something like...

film = {
 title: 'Dodgeball'
 image: '/images/films/dodgeball.jpg'
 followed_actors: ['Ben Stiller', 'Vince Vaughn'] #could be multiple
 followed_genres: ['Comedy'] #could be multiple
}

Note I would want to return multiple films.

Here's how my models are coded up:

Film Model defined like so:

from django.db import models
from app.actors.models import Actor
from app.genres.models import Genre


class Film(models.Model):
    title = models.CharField(max_length=255)
    strapline = models.CharField(max_length=255)
    slug = models.SlugField(max_length=100)
    image_url = models.CharField(max_length=255)
    pub_date = models.DateTimeField('date published')
    actors = models.ManyToManyField(Actor)
    genres = models.ManyToManyField(Genre)

    def __unicode__(self):
        return self.title

And Actor Model:

from django.db import models

from follow import utils


class Actor(models.Model):
    title = models.CharField(max_length=255)
    strapline = models.CharField(max_length=255)
    image = models.CharField(max_length=255)
    image_hero = models.CharField(max_length=255)
    bio = models.TextField()

    def __unicode__(self):
        return self.title


#followable
utils.register(Actor)
like image 411
kieran Avatar asked May 22 '13 14:05

kieran


People also ask

How do you find related objects in Salesforce?

An object relationship in Salesforce is a two-way association between two objects. Relationships are created by creating custom relationship fields on an object. This is done so that when users view records, they can also see and access related data.

What are the three types of object Relations in Salesforce?

There are three main relationship types in Salesforce… A lookup relationship can be used to link two objects together. It is the most basic type of relationship that creates a child-parent relationship between two objects. A master-detail relationship can also be used to link two objects together.

What is a related object?

Related objects are items associated with a case or alert. For example, a related object can be a physical device, a transaction, or an event.


1 Answers

Behind the scenes, Follow objects are essentially a many-to-many relationship with fields added each time you register a model.

Your question just talks about actors, but your code also includes genres. It's not especially hard to cover both, I'm just not sure which way is the way you want it.

I think you can get your film objects in one queryset:

films = Film.objects.filter(Q(actors__in=Actor.objects.filter(follow_set__user=request.user)) |
                Q(genres__in=Genre.objects.filter(follow_set__user=request.user))).distinct()

As noted in the docs for __in lookups, some database back ends will give you better performance if you evaluate the subqueries before using them:

actor_ids = list(Actor.objects.filter(follow_set__user=request.user).values_list('id', flat=True))
genre_ids = list(Genre.objects.filter(follow_set__user=request.user).values_list('id', flat=True))
films = Film.objects.filter(Q(actors__in=actor_ids) | Q(genres__in=genre_ids)).distinct()

If you just want to return the matching films, I think those are the most concise way to express it.

For the part where you're adding the reasons to the films - I don't see a more elegant way to handle that than to iterate through the films queryset and add the information by hand. I would definitely define the querysets for actor_ids and genre_ids before doing so, although whether or not I evaluated them early would still depend on the db back end.

annotated_films = []
for film in films:
    film.followed_actors = film.actors.filter(id__in=actor_ids)
    film.followed_genres = film.genres.filter(id__in=genre_ids)
    annotated_films.append(film)
like image 61
Peter DeGlopper Avatar answered Nov 15 '22 08:11

Peter DeGlopper