Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django exact match of ForeignKey Values

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?

like image 548
Andrew Fount Avatar asked Jan 13 '18 10:01

Andrew Fount


Video Answer


2 Answers

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')
like image 127
RajSoundar Avatar answered Oct 14 '22 11:10

RajSoundar


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()
like image 34
Jay Avatar answered Oct 14 '22 11:10

Jay