Django: All objects AND all M2M related fields in one query - django

Let's say I have these:
class Publication(models.Model):
title = models.CharField(max_length=128)
author = models.ManyToManyField(Author, through='Authorship')
class Author(models.Model):
first_name = models.CharField(db_index=True, max_length=64)
last_name = models.CharField(db_index=True, max_length=64)
How can I get ALL the publications AND their author(s) in one query. I want to list each publication and its authors on a page.
But I don't want to hit the authors table for every publication.
The only way I know to do it is with select_related in the view and authorship_set.all() on the template. But that's one query for every publication. I could easily do it with raw sql, but that's yucky.
*BTW, I'm using the through model because I have to keep some extra data in there, like author_display_order.
EDIT:
Turns out authorship_set was doing all the querying.
When I run it this way from console only one query gets fired:
pubs = Publication.objects.all().prefetch_related('author')
for p in pubs:
print p.title
for a in p.author.all():
print a.last_name

Related

How to define models in django that provides custom values based on pre-selection for a field?

Given the following model that stores the user's wish list for reading books:
class ReadingList(models.Model):
user_id = models.ForeignKey(UserInfo, on_delete=models.DO_NOTHING, null=False, blank=False, default=None, db_column='user_id')
book= models.CharField(max_length=255, blank=False)
creation_time = models.DateTimeField(blank=True)
class Meta:
unique_together = (('user_id', book),)
I want to create a model that helps in tracking the time spent in the reading the book on different days which looks something like this:
class ReadingTracker(models.Model):
user_id = models.ForeignKey(ReadingList, on_delete=models.DO_NOTHING, related_name='user', blank=False, db_column='user_id')
book= models.ForeignKey(ReadingList, on_delete=models.DO_NOTHING, related_name='book-to-read', blank=False, db_column='book')
time = models.DateTimeField(blank=True)
time_spent = models.floatfield()
On the client-side (corresponding to ReadingTracker) for both the fields user_id and book
I see that ReadingList object (1), ReadingList object (2), ... are listed. But, this is not working as expected.
What I want to achieve are the following:
For user_id field I want to see the something like dummy_uid1, dummy_uid2, ... to be listed.
Consider dummy_uid1 wants to read book1 and book2 whereas dummy_uid2 wants to read book1 and book3.
When dummy_uid1 is selected as user_id, I want only book1 and book2 to be listed for selection.
How do I define the model in django rest framework to achieve this?
Any suggestions related to the above would be much appreciated and thank you in advance.
There are two parts to this question:
If you want to see a different value than ReadingList object (1) then you need to define the __str__ value of your model, you can do this like so:
class ReadingList(models.Model):
...
def __str__(self):
return f'{self.user_id}' # return whatever string you want to display
If you want to just display the books for a particular user then you can use a filter() (see the Django documentation):
reading_list = ReadingList.objects.get(...)
ReadingTracker.objects.filter(user_id=reading_list)
However, I would add that you have a user_id on your ReadingList object which does seem to connect to a User model, but your user_id on ReadingTracker is a ForeignKey relation to ReadingList, which is confusing. I would suggest renaming the field or actually making it link to the User model (though this is unnecessary as you can still filter by User through the ReadingList model).

How to call a a field of one model A into another model B so that b can work as a view

I have created a model called Department, Course. Models are as follow
This is the model for departments and course
class Departments(models.Model):
Department_Id = models.IntegerField(primary_key=True)
Department_Name = models.CharField(max_length=200)
Department_Code = models.CharField(max_length=200)
class Course(models.Model):
Course_Id = models.IntegerField(primary_key=True)
Department_Id = models.ForeignKey(Departments, on_delete=models.CASCADE)
Course_Name = models.CharField(max_length=200)
Course_Code = models.CharField(max_length=200)
I want to create a model called view which can be later on called for search. I want a view model in a such a way that it consit of the data in concat form i.e. name= Department_name+ Course_Name
class View (models.model):
view_id= models.IntegerField(primary_key=True)
Name= Department_name(I want this from Departments table)
+ Course_Name(I want this from Course table)
I try using one to one relation . I would really appricate the help
It's not clear why you'd want to do that. It's never a good idea to duplicate data from one model into another one, as it can lead to inconsistencies.
You can add a ForeignKey in View to your Course model and then when you do f"{view.course.name} {view.course.department.name}" you already have your string:
class View(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def name(self):
return f"{self.course.name} {self.course.department.name}"
Notes:
Don't call your foreign key Department_id because it's not referring to the id but to the object itself in the Django ORM: department = models.ForeignKey(Department, on_delete=models.CASCADE). As you can see, this makes reading the code much simpler: self.course.Department_id is a Department object not an integer, so self.course.department makes more sense.
Don't prefix your field names with the class, it just makes the code so much less readable: Do you prefer department.name or department.Department_name?
The View model is still a mystery to me, as you can search without it. You can search for example for courses with a matching department name like this:
Course.objects.filter(department__name__icontains="maths")
which will return all courses with "maths" in their department name.
Remove all the ids from your models, they are created automatically by Django anyway (and called id). Again, department.id is much easier to read than department.Department_id. Also in your code, you have to generate the ids yourself since you don't set them to auto-populate.

How to filter joined models in Django?

I have the following models:
class Street(models.Model):
name = models.CharField(max_length=40)
class House(models.Model):
street = models.ForeignKey(Street, models.PROTECT)
number = models.CharField(max_length=10)
class Meta:
unique_together = ('street', 'number')
class Room(models.Model):
house = models.ForeignKey(House, models.PROTECT)
number = models.CharField(max_length=10)
class Meta:
unique_together = ('house', 'number')
I already know the ID of a Street and would like to get all the rooms of all the houses in that street. In SQL that is easy:
SELECT *
FROM room JOIN house ON house.id = room.house_id
WHERE house.street_id = xyz;
Now how do I do this in Django? I tried
Room.objects.select_related('house').filter(street=xyz)
But I get an exception saying I can't access this field:
django.core.exceptions.FieldError: Cannot resolve keyword 'street' into field. Choices are: house, house_id, id, number
Because of the amounts of data I am facing, I would really like to be able to join and filter using a single query! When giving up one or the other, I would have to resort to either making multiple queries or filtering in Python, both of which are inherently inefficient. An alternative would be raw queries, I guess...
You can get access to related object's field with __ syntax:
Room.objects.select_related('house').filter(house__street=xyz)
This can be done as much time as you need, to select rooms by street name you can do this:
Room.objects.select_related('house').filter(house__street__name=xyz)
Chech details here.

Django-views Custom filter

If having different books stored in django database, each book has a date in which it was added to the database. Is their a way of filtering books written by a certain author that was within a date range only using django views?
Not sure what you mean by only django views, I assume you want to use querysets. Your question is poorly written - read this.
class Book(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField()
author = models.ForeignKey(Author)
class Author(models.Model):
name = models.CharField(max_length=200)
And Queryset would be something like this.
books = Book.objects.filter(author__name=authors_name,
date__range=["2011-01-01", "2011-01-31"])

Selecting objects through multiple ManyToMany relationships

This seems to be simple question but it is never answered anywhere and there is no obvious answer. I have two "cascading" ManyToMany models:
User has ManyToMany to Tag:
class User (models.Model):
...
watches_tags = models.ManyToManyField(Tag, related_name='watched_tag_set', blank=True, null=True)
ignores_tags = models.ManyToManyField(Tag, related_name='ignored_tag_set', blank=True, null=True)
and Tag has ManyToMany to Status:
class Tag (models.Model):
tag = models.TextField ()
status = models.ManyToManyField (Status)
Is there a ORM expression that will give me all the Statuses tagged with tags any given User watches/ignores? I could iterate over Tags in logic and concatenate QuerySets, but I want to use this in much more complicated Q-expression based query, and I want as much work to be pushed to the database engine.
Try this:
watched_statuses = Status.objects.filter(tag__watched_tag_set=user)
ignored_statuses = Status.objects.filter(tag__ignored_tag_set=user)
(Note that your related names on Tag are confusing since watched_tag_set is a set of Users, not Tags.)