I need to perform FTS across multiple different models. I want to get any model type in search result.
I would like to sort results by rank, to get most relevant result. I can run search one by one, but not sure how can I combine results, especially preserving rank relevancy.
Here are models, its an example from Making queries manual page.
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField()
number_of_pingbacks = models.IntegerField()
rating = models.IntegerField()
You can combine your multiple querysets into one with something like this.
from itertools import chain
blogs = Blog.objects.filter(...)
authors = Author.objects.filter(...)
entries = Entry.objects.search(...)
chain = chain(blog_results, lesson_results, profile_results)
qs = sorted(chain, key=lambda instance: instance.pk, reverse=True)
Related
I know how I can count things with annotate (in my view) but I would like to do the same in model (so it would be more reusable).
For example (lets take an example from django documentation) I have this model:
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
class Publisher(models.Model):
name = models.CharField(max_length=300)
class Book(models.Model):
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
rating = models.FloatField()
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
pubdate = models.DateField()
class Store(models.Model):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
and I can use in view this:
from django.db.models import Count
pubs = Publisher.objects.annotate(num_books=Count('book'))
But how I do that in model?
I know this question is pretty basic (probably) but I'm pretty much beginner in django.
Thanks for answers!
You can use custom managers:
Django docs: Managers
class BookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(num_books=Count('book'))
class Publisher(models.Model):
name = models.CharField(max_length=300)
books = BookManager()
Now you can call it like this:
pubs = Publisher.books.all()
And you will have num_books with your objects.
You can use classmethod for this.
class Publisher(models.Model):
...
#classmethod
def get_book_count(cls):
return cls.objects.annotate(num_books=Count('book'))
You can call this method as
pubs = Publisher.get_book_count()
Edit - Also check out the answer by #Navid2zp which might be a better solution for you.
I want to ask the user how many questions they want to ask; based on their response, I want to populate a model with those many fields. The way I am currently thinking about doing that is as follows:
from __future__ import unicode_literals
from django.db import models
class Interview(models.Model):
title = models.TextField()
description = models.TextField()
number_questions = models.IntegerField()
question_one = models.ForeignKey('Question', related_name='question_one')
question_two = models.ForeignKey('Question', related_name='question_two')
question_three = models.ForeignKey('Question', related_name='question_three')
question_four = models.ForeignKey('Question', related_name='question_four')
question_five = models.ForeignKey('Question', related_name='question_five')
class Question(models.Model):
question_description = models.TextField()
prep_time = models.IntegerField()
response_time = models.IntegerField()
I realize that this solution is inefficient because a) the user is limited to a preset number of questions and b) if the user specifies less than 5 questions, there are unnecessary question entries created. What is a better way to go about storing multiple questions?
Do the foreign key relation the other way round. That's how you model a many-to-one relation:
class Interview(models.Model):
title = models.TextField()
description = models.TextField()
#property
def number_questions(self):
return self.questions.count()
class Question(models.Model):
interview = models.ForeignKey(Interview, related_name='questions')
question_description = models.TextField()
prep_time = models.IntegerField()
response_time = models.IntegerField()
Now you can access an interview's question via:
interview.questions.all()
An Interview can now have any number of Questions.
Btw, the related_name of all the ForeignKeys in your original Interview model should have been 'interview' to make any semantic sense.
I have a file, user and share models like this:
class File(models.Model):
users = models.ForeignKey(User)
file_name = models.CharField(max_length=100)
type = models.CharField(max_length=10)
source = models.CharField(max_length=100)
start_date = models.TextField()
time_overview = models.CharField(max_length=55)
end_date = models.TextField()
duration = models.TextField()
size_overview = models.IntegerField()
size = models.TextField()
flag = models.TextField()
flag_r = models.TextField()
class Share(models.Model):
users = models.ForeignKey(User)
files = models.ForeignKey(File)
shared_user_id = models.IntegerField()
shared_date = models.TextField()
I found out the number of users with whom the particular file is shared using this query:
shared_file = File.objects.filter(id__in= Share.objects.filter(users_id = log_id).values_list('files', flat=True)).annotate(count=Count('share__shared_user_id'))
Now, I want to find out the users name with whom the particular file is shared.
Tried using this:
shared_username = User.objects.filter(id__in= Share.objects.filter(users_id = log_id).values_list('shared_user_id', flat=True))
Didn't get it right. I want to get the username with whole the particular files are shared. How can I do this?
Add the condition of a particular file name in the second queries:
shared_username = User.objects.filter(id__in = Share.objects.filter(users_id = log_id, files__file_name='file1').values_list('shared_user_id', flat=True))
However your model has some problems worth noting:
1- If you replace Share.shared_user_id with a OnetoMany field your model would be more correct and your queries a lot easier.
2- If you use related_name on your foreign keys you can access them from the opposite model and your queries would be much easier.
3- You should not use plural var names for foreign keys as they represent one object. File.users should be File.user and the same for Share.files and Share.users
4- Your model could be a lot more readable if you name your vars properly. For example instead of File.users you could use File.owner or File.owning_user. Same with Share.users: Share.sharing_user.
I have 2 models named 'Author' and 'Entry' as defined below.
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
msgtoauthor = models.TextField()
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
I am trying to access 'Author.msgtoauthor' from 'Entry'.
I know, I can retrieve the relationship between Entry and Author by
e = Entry.objects.get(authors)
Is it possible to extract the author id?
I know in the backend, Django creates a table for Authors and Entries but I want to update 'msgtoauthors' from a method in 'Entry'.
Thanks In Advance.
Did you mean
for author in my_entry.authors.all():
author.msgtoauth = 'Here is new content'
author.save()
?
Entry.authors returns a RelatedManager and my_entry.authors.all() is a QuerySet, that returns the Author objects. See https://docs.djangoproject.com/en/1.3/ref/models/relations/ and https://docs.djangoproject.com/en/1.3/topics/db/models/#many-to-many-relationships.
(Updated.)
Try this:
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
def msgtoat(self, message):
self.authors.update(msgtoauthor=message)
For example, to update an entry:
entry.msgtoat('Hello')
If all the authors get the same value you can do:
entry.authors.update(msgtoauthor='Hey there!')
https://github.com/Ry10p/django-Plugis/blob/master/courses/models.py
line 52
class name():
the_thing1 = models.CharField()
another = models.TextField()
class name2():
the_thing2 = models.ForignKey(the_thing1)
another2 = models.ForignKey(another)
-Cheers
For this example:
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, related_name='entries')
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateTimeField()
mod_date = models.DateTimeField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __unicode__(self):
return self.headline
So, how to find all Blogs with its Entries?
I want to do something like this
q = Blog.objects.all().entries.filter(...)
But it gave me an error. So does Django only supports to use the backward navigation properties for only one object rather than a set of objects?
Let's say you want to filter on the entries of all blogs, and get the blogs that have entries you require:
Blog.objects.filter(entry__headline__icontains="cats").distinct()
There is a one blog to many entries relationship here, but this query works and gives you the blogs that have entries with cats in headlines.
If you want to get all entries associated with each Blog, you can do something like:
blogs = Blog.objects.all()
for blog in blogs:
blog.cached_entries = blog.entry_set.all()
Although this looks neat, n + 1 queries will be made -- 1 query for getting all blogs plus n queries for getting each entry associated with the blog.
It is possible to do this in just one query, but you'll have to do more work if you want to segregate entries by blog.
blog_with_entries = []
entries = Entry.objects.order_by('blog')
previous_blog = None
acc_entries = [] # accumulate entries of one blog
for entry in entries:
if previous_blog != entry.blog:
if previous_blog is not None:
blog_with_entries.append((previous_blog, acc_entries))
acc_entries = []
previous_blog = entry.blog
acc_entries.append(entry)
blog_with_entries.append((previous_blog, acc_entries))
Yes.
"Lookups that span relationships"