Sunday, January 28, 2018

Django exact match of ForeignKey Values

Leave a Comment
class Sentence(Model):     name = CharField()  class Tokens(Model):    token = CharField()    sentence = ForeignKey(Sentence, related_name='tokens') 
  1. I want to implement two cases: Sentence consists exactly of three tokens ['I', 'like', 'apples']. So list of sentence.tokens.all() is exactly ['I', 'like', 'apples'].

  2. Same as above, but contains tokens (part of sentence).

Sentence.objects.annotate(n=Count('tokens',distinct=True)).filter(n=3).filter(tokens__name='I').filter(tokens__name='like').filter(tokens__name='apples') doesn't work, since it matches I I I as well.

Is there any way to filter on exact set of values in ForeignKey?

2 Answers

Answers 1

Ah, I understand the question better now. Just leveraging elements of your and Jay's code, the following might be one approach. May be not very elegant. But seems to work.

def get_sentences(my_tokens):     counts = dict()     for t in my_tokens:         counts[t] = my_tokens.count(t)     results = Sentence.objects     for k, v in counts.iteritems():         results = results.filter(tokens__token=k).annotate(n=Count('tokens',distinct=True)).filter(n__gte=v)     return results  >>> from django.db.models import Count >>> from my.models import Sentence, Tokens  >>> s1 = Sentence.objects.create(name="S1") >>> t10 = Tokens.objects.create(token="I", sentence=s1) >>> t20 = Tokens.objects.create(token="like", sentence=s1) >>> t30 = Tokens.objects.create(token="apples", sentence=s1)  >>> s2 = Sentence.objects.create(name="S2") >>> t11 = Tokens.objects.create(token="I", sentence=s2) >>> t21 = Tokens.objects.create(token="like", sentence=s2) >>> t31 = Tokens.objects.create(token="oranges", sentence=s2)  >>> s3 = Sentence.objects.create(name="S3") >>> t31 = Tokens.objects.create(token="I", sentence=s3) >>> t32 = Tokens.objects.create(token="I", sentence=s3) >>> t33 = Tokens.objects.create(token="I", sentence=s3)  >>> my_toks = ("I", "like", "apples") >>> sentences = get_sentences(my_toks) >>> sentences[0].name u'S1'  >>> my_toks = ("I", "I", "I") >>> sentences = get_sentences(my_toks) >>> sentences[0].name u'S3' 

For exact reference, my models look like this:

class Sentence(Model):     name = models.CharField(max_length=16)  class Tokens(Model):     token = models.CharField(max_length=16)     sentence = models.ForeignKey(Sentence, related_name='tokens') 

Answers 2

Are you trying to get every sentence that contains each of the search tokens?

A flexible (although likely non-optimal) way of doing this might be:

search_tokens = ('I', 'like', 'apples') results = Sentence.objects  for token in search_tokens:     results = results.filter(tokens__name=token)  results.distinct() 

This is equivalent to just chaining the filters together:

results = Sentence.objects.filter(tokens__name='I').filter(tokens__name='like').filter(tokens__name='apples').distinct() 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment