Django: Referencing a manytomany field that has been defined in another model - python

I'm trying to work out how to calculate the number of books written by an author. The book model sets up the manytomany field with the author model. I have no idea how even google something like this which I thought would be very simple to do but I don't even know how to start.
Here's the stripped down code:
class Author(models.Model):
first_name = models.CharField()
last_name = models.CharField()
def books(self):
"""Return a list of comma separated list of books by the author"""
pass
class Book(models.Model):
title = models.CharField()
authors = models.ManyToManyField(Author)
Maybe I could do a filter query to get books by a particular author but it doesn't seem like the django way to do it? I'm also not sure how to pass the filter function a python object rather than text.
Thanks

when you define a foreignkey or manytomany field, django sets up a reverse relation for you, which in your case is book_set. so something like:
def books(self):
books = self.book_set.all()
return ', '.join([book.title for book in books])
see related objects in the docs

Related

Django many to many relation, include all IDs in queryset in both directions

I have 2 models connected via M2M relation
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(B, related_name='papers')
class Author():
name = models.CharField(max_length=70)
Is there a way to include authors as all related authors' IDs (and maybe name somehow)?
Is there a way to include papers IDs as reverse relation (and maybe title as well)?
Author.objects.all().annotate(related_papers=F('papers'))
this only adds id of one paper, first one it finds I think.
Furthermore, changing related_papers to papers gives an error:
ValueError: The annotation ‘papers’ conflicts with a field on the
model.
From what I understand in your comments, you're using DRF. I will give you 2 answers.
1) If you're talking about model serializer, you can use PrimaryKeyRelatedField :
class AuthorSerializer(serializers.ModelSerializer):
papers=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Author
fields = ['name', 'papers']
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
fields = '__all__'
This will return the IDs for the other side of the relationship whether you're on Paper or Author side. That will return the primary keys, not a representation of the object itself.
2) Now you're also talking about performance (e.g. database hit at each iteration).
Django (not DRF-specific) has a queryset method to handle preloading related objects. It's called prefetch_related.
For example, if you know you're going to need the relation object attributes and want to avoid re-querying the database, do as follow:
Author.objects.all().prefetch_related('papers')
# papers will be already loaded, thus won't need another database hit if you iterate over them.
Actually, it has already been implemented for you. You should include a Many-to-Many relationship to author in your Paper model like this:
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(Author, related_name='papers')
That gives you the opportunity to add Author objects to a related set using
p.authors.add(u), assuming that p is the object of Paper model, and a is an object of Author model.
You can access all related authors of a Paper instance using p.authors.all().
You can access all related papers of an Author instance using u.papers.all().
This will return an instance of QuerySet that you can operate on.
See this documentation page to learn more.

the use of the underscore : q.choice_set.all()

I am working on the Django
Writing your first Django app, part 2
In the section "2.4.4 Playing with the API".
If someone could explain to me how does the underscore works in this code q.choice_set.all()
It's pretty simple. Let's consider these two models:
class Author(models.Model):
name = models.Charfield(max_lenght=100)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
name = models.Charfield(max_lenght=100)
Explain:
We have some Authors that each author has some books.
but what if we want to get all books of a specific author?
let's assume we want all books that written by Sara:
my_author = Author.objects.get(name='Sara')
Here is the magic! Django will automatically generate the backward relation for this object called book_set which contains all books writen by Sara.
The naming rule is straightforward, <related_model_name>_set.
So, you can get those books by writing this line:
sara_books = my_author.book_set.all()
It's nice, isn't it ? wait, it could be nicer!
book_set is not a good name for me, i want a more human readable name!
you can easily change this by adding related_name to the author field of Book model:
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='her_books') # you can name it anything you want
name = models.Charfield(max_lenght=100)
then, you can get same books like this:
sara_books = my_author.her_books.all()
it's now more human readable :) Hope you get it.

Look up objects that aren't the target of any foreign keys using Django ORM

I have two models, Author and Book. I need to get all Authors who have no Books with Django's ORM. How can I do that?
Here are my models:
class Author(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
class Book(models.Model):
name = models.CharField(max_length=500)
author = models.ForeignKey(Author, related_name='books')
I am currently doing it with for loop, but I would prefer to do it with the ORM:
for person in Author.objects.all():
if person.books.count() == 0:
"something code"
But it need to make update for objects.
I tried this but I just get an empty QuerySet:
Author.objects.select_related('books').values('id').annotate(books_count=Count('id')).filter(books_count=0)
.annotate(books_count=Count('id'))
Django will count the Author's ids here. Try
.annotate(books_count=Count('books__id'))
instead. The double underscore indicates a query that spans relationships:
Django offers a powerful and intuitive way to "follow" relationships in lookups, taking care of the SQL JOINs for you automatically, behind the scenes. To span a relationship, just use the field name of related fields across models, separated by double underscores, until you get to the field you want.
This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':
>>> Entry.objects.filter(blog__name='Beatles Blog')

How to combine django "prefetch_related" and "values" methods?

How can prefetch_related and values method be applied in combination?
Previously, I had the following code. Limiting fields in this query is required for performance optimization.
Organizations.objects.values('id','name').order_by('name')
Now, I need to prefetch its association and append it in the serializer using "prefetch_related" method.
Organizations.objects.prefetch_related('locations').order_by('name')
Here, I cannot seem to find a way to limit the fields after using "prefetch_related".
I have tried the following, but on doing so serializer does not see the associated "locations".
Organizations.objects.prefetch_related('locations').values("id", "name").order_by('name')
Model Skeleton:
class Organizations(models.Model):
name = models.CharField(max_length=40)
class Location(models.Model):
name = models.CharField(max_length=50)
organization = models.ForeignKey(Organizations, to_field="name", db_column="organization_name", related_name='locations')
class Meta:
db_table = u'locations'
Use only() to limit number of fields retrieved if you're concerned about your app performances. See reference.
In the example above, this would be:
Organizations.objects.prefetch_related('locations').only('id', 'name').order_by('name')
which would result in two queries:
SELECT id, name FROM organizations;
SELECT * from locations WHERE organization_name = <whatever is seen before>;

How do I make a class a sub-group of another class?

from django.db import models
class products(models.Model): #Table name, has to wrap models.Model to get the functionality of Django.
name = models.CharField(max_length=200, unique=True) #Like a VARCHAR field
description = models.TextField() #Like a TEXT field
price = models.IntegerField()
def __unicode__(self): #Tell it to return as a unicode string (The name of the to-do item) rather than just Object.
return self.name
class categories(models.Model):
I'm a python newbie and I'm trying to create an e-commerce store. As you can see above, I've created the products class, but in the Categories class, I need to include all the products I create come under a certain category. I have no clue how to do this.
Sounds like you just want a ForeignKey from Product to Category.
Take a look at Many to Many
I think a Product can belong to Many categories so it should have a ManyToMany relationship to the Category model.

Categories

Resources