Django: get related set from a related set of a model - python

class Book(models.Model):
# fields
class Chapter(models.Model):
book = models.ForeignKey(Book)
class Page(models.Model):
chapter = models.ForeignKey(Chapter)
I want all the pages of the book A, possibly without cycling every chapter to fetch the pages.
book = Book.objects.get(pk=1)
pages = book.chapter_set.page_set #?!?

You can't do it that way. chapter_set is a query set, it doesn't have an attribute page_set.
Instead, turn it around:
Page.objects.filter(chapter__book=my_book)

When you query cross models, double underscores may help
book = Book.objects.get(pk=1)
pages = Page.objects.filter(chapter__book=book)

Related

Django Models - One to Many or Many o Many

In Django there are no One-to-Many relationships, there are just Many-to-One. In cases where defining Foreign Key on the child table is odd, should we go for Many-to-Many?
For example:
Book has many pages. If we define foreign key on the Page model, then the page has one book which is not an intuitive thing to do (or say).
OPTION 1:
class Book(models.Model):
name = models.CharField(max_length=100)
date_published = models.DateTimeField(default=timezone.now)
class Page(models.Model):
page_number = models.IntegerField()
page_text = RichTextField()
book = models.ForeignKey(Book, on_delete=models.CASCADE)
OPTION 2
class Book(models.Model):
name = models.CharField(max_length=100)
date_published = models.DateTimeField(default=timezone.now)
pages = models.ManytoMany(Page)
class Page(models.Model):
page_number = models.IntegerField()
page_text = RichTextField()
In option2 I can access the pages of the book by book.pages.
In option 1 I don't know how to access the pages, maybe book.pages_set.objects.all() which is not pretty.
I asked a fellow programmer and he said just use Many-to-Many. I understand what each approach means and I also understand the difference between the two as to what's happening in the database. My question is what is a better/standard approach.
I prefare the first option because many books doesn't have common page and its contains.
So, using first option you can access book pages using following queryset
pages = book.page_set.all()
Here, you can also use related_name parameter in foreignkey field as:
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name="pages")
then book pages can be fetched using
pages = book.pages.all()
I would like to use first example. As Books do not have common pages. For connecting Book to Page, you have to use backward relationship. In this relationships, _set method is used. All the model name & filed name must be small letter although they are in capital letter.
m=book.page_set.all()
Now you can use all attribute of Page from Book using m.
For more about reverse or backward relationship visit here
In your view you pass the id of the Book and you use it in your query to filter the pages they are linked to it.
for example:
def show_pages(request, book_id):
pages = Pages.objects.filter(book=book_id)

Django Model for players

I'm working on a django website that can allow me to display some information about players and matches for a game.
There will be multiple matches and multiple players. The problem I'm having is that I'm not sure if this is what I should be doing:
class Player(models.Model):
player_id = models.IntegerField(default=0)
matches = models.ManyToMany(Match)
class Matches(models.Model):
match_id = models.IntegerField(default=0)
players = models.ManyToMany(Player)
or I should be doing:
class Player(models.Model):
player_id = models.IntegerField(default=0)
class Matches(models.Model):
match_id = models.IntegerField(default=0)
players = models.ManyToMany(Player)
A player can be in multiple matches, and a match can contain multiple players, which is why I'm a bit confused.
You should define the relation in only one of the models, so the second example is the correct one.
In the first one (you have a typo there that should be Matches by the way) you are creating two separate M2M relations between the two models that you don't want to have in your case.
On an unrelated note, you don't need to to have player_id and match_id columns as Django will automatically create id columns for your models.
The following code will solve your model structure for your project.
class TimeStamped(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Player(TimeStamped):
name = models.CharField(max_length=50)
extra_info = models.TextField()
matches = models.ManyToManyField('Match')
def __unicode__(self):
return self.name
class Match(TimeStamped):
title = models.CharField(max_length=50)
extra_info = models.TextField()
class Meta:
verbose_name_plural = 'Matches'
def __unicode__(self):
return self.title
The TimeStamped model is a good idea for every project you build. If you are using Python 3 then replace __unicode__ with __str__. Don't use plural in model names. Use verbose_name_plural, that is the convention in my opinion. It seems like you might have a problem with query players from a Match instance. Read the documentation for a detailed info on that. If you can't find that post here. Since this post is not about the querysets so I skipped that part.
Absolutely right for this scenario, ManyToMany relation is required to setup in DB.
id is the default field in Django model if you are not providing any other primary key. And ID is incremental. As per my opinion, Following models are enough to satisfy your scenario.
class Player(models.Model):
name = models.CharField(max_length=255)
class Match(modles.Model):
player = models.ManyToMany(Player, related_name="matches")
According to above model sample, you can accessing matches of one player and players who played a single match.
match = Match.objects.get(id=1)
print match.player.all() # It will print all players for match of id 1
player = Player.objects.get(id=1)
print player.matches
You definetelly have to use ManyToManyField, you can add it only to one model:
class Player(models.Model):
player_id = models.IntegerField(default=0)
matches = models.ManyToMany(Match)
class Matches(models.Model):
match_id = models.IntegerField(default=0)
After declaring models, Player objects have access to their related Matches objects in next way:
player.matches.all()
Matches objects have access to their related Player objects in next way:
match.player_set.all()
See article Many-to-many relationships, Django documentation, there are plenty of use full information of how to dial with ManyToMany relations in Django.

Listing foreign keys related to a proxy model in django admin

My models.py looks like this :
class Author(models.Model):
name = models.CharField()
class DraftBooks(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
status_choices = ((1,Draft),(2,Published))
status = models.SmallIntegerField(choices=status_choices)
class PubBooks(DraftBooks):
class meta:
proxy = True
verbose name = 'Published Books'
I am using a proxy model since I want a different change list view for books in draft state and books which have been published.To achieve this,my admin.py looks like this :
class AuthorAdmin(admin.ModelAdmin):
pass
admin.site.register(Author, AuthorAdmin)
class DraftBooksAdmin(admin.ModelAdmin):
list_display = ('title','author','status')
def queryset(self):
return DraftBooks.objects.filter(status='1')
admin.site.register(DraftBooks, DraftBooksAdmin)
class PubBooksAdmin(admin.ModelAdmin):
list_display = ('title','author','status')
def queryset(self):
return PubBooks.objects.filter(status='2')
admin.site.register(PubBooks, PubBooksAdmin)
This setup works perfectly fine.In my admin,now I have 3 change list views,one which shows a list of all authors,one which shows book which are in a draft state and finally one which shows the list of books which are in a published state.
I now need to add a hyperlink to every item (Author) in the authors' list overview that links to a view showing all books of the specific authors.For Example:
J.K. Rowling (books)
J.R.R. Tolkien (books)
where books is a hyperlink to a site showing all books of a particular author.
Now I am completely clueless as to how to do this.Django Xadmin has a plugin which provides just this feature.This Stackoverflow question also provides answer to this problem.But the problem is that they do not work in proxy models with the custom filters that I have.When I try to get the list of books by an author,I get only the books which are in Draft state.I would ideally want all the books,Draft and Published by an author.How do i achieve this ?

joining two separate app models django

okay so I have just a test blog system to practice my django skills. I have 2 apps one called article on called likes here they are:
article models:
from django.db import models
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length = 200)
description = models.TextField()
pub_date = models.DateTimeField('Date Published',auto_now = True)
def __str__(self):
return self.title
and here is the likes models:
from django.db import models
from apps.article.models import Article
# Create your models here.
class Like(models.Model):
article_id = models.ForeignKey(Article)
likes = models.IntegerField(default = 0)
def __str__(self):
return self.Likes
now im rendering out the pages but I want to display how many likes each article has. How can I join both these models. Change the objects.all method to also grab the likes from the Like model
You can use FOO_set, docs about this here, basically you do this to get all likes:
article.likes_set.all()
and you can just use count() to get number
First you might want to rename article_id to article since when you use the attribute, you will actually get the article and not just the id.
In this case you seem to have a many-to-one relationship between Like and Article. That means you need to refer to the likes as 'like_set'. So if you happen to have the object stored in article, you can get all the likes with article.like_set.all() and the count with article.like_set.count().
Reference: https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_one/
If you're interested in fetching this ahead of time you can use prefetch_related to save the additional database calls:
https://docs.djangoproject.com/en/1.4/ref/models/querysets/#prefetch-related
It would like something like this:
articles = Article.objects.all().prefetch_related('like_set')

Django inheritance

Please have a look:
class Categorie(models.Model):
id = models.AutoField('id', primary_key=True)
title = models.CharField('title', max_length=800)
articles = models.ManyToManyField(Article)
class Article(models.Model):
id = models.AutoField('id', primary_key=True)
title = models.CharField('title', max_length=800)
slug = models.SlugField()
indexPosition = models.IntegerField('indexPosition', unique=True)
class CookRecette(Article):
ingredient = models.CharField('ingredient', max_length=100)
class NewsPaper(Article):
txt = models.CharField('ingredient', max_length=100)
So I created "CookRecette" and "NewsPaper" as "Article".
I Also create a "Categorie" class who link to (manyToMany) "Article".
But in the admin interface, I can't link from "Categorie" to an "CookRecette"or "NewsPaper".
Same from the code.
Any help ?
Cheers,
Martin Magakian
PS: I'm so sorry but actually this code is correct! So everything is working fine, I can see my "CookRecette"or "NewsPaper" from "Categorie"
I'll start by saying that you don't need to define the 'id' field, if you don't define it then Django will add it automatically.
Secondly, the CookRecette and NewsPaper objects are not linked to the Categorie object by any means (ForeignKey, OneToOne, OneToMany, ManyToMany) so they wouldn't be able to be accessed that way anyway.
After you have linked the models together in whichever way you wish, you might want to have a look at http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin which will show you how to quickly edit related objects in the Djano admin console.
NewsPaper has part of it as Article object. If you will create new NewsPaper object, you will see a new object in articles. So in admin interface, when managing Categories, you will be able to select any article, and some of them are NewsPaper.
You can add news paper to a category like this:
category = Categorie(title='Abc')
category.save()
news_paper = NewsPaper(slug='Something new', indexPosition=1, txt='...')
news_paper.save()
category.articles.add(news_paper)
You can retrieve news papers from specific category like this:
specific_category = Categorie.objects.get(title='Abc')
NewsPaper.objects.filter(categorie_set=specific_category)

Categories

Resources