I have Tags in my application and it's possible to Tag different things like News, Events...
News and Events have a ManyToMany relation to Tags. Is it possible to get every object where the Tag is used?
My Models (shortened) look like this:
Tag Model
class Tag(models.Model):
title = models.CharField(max_length=35)
News Model
class News(models.Model):
title = models.CharField(max_length=75)
tag = models.ManyToManyField(Tag, related_name="news")
Event Model
class Event(models.Model):
title = models.CharField(max_length=75)
tag = models.ManyToManyField(Tag, related_name="event")
I know that I can get all News that have the Tags assigned by
tag = self.get_object()
tag.news.all()
But is it possible to get all News, Events... without 10 requests? I'm looking for something like tag.all.all()
try this
tag.news.all() | tag.event.all()
"news" and "event" are accessible due to reverse relationship here which are defined in their respective model fields with the keyword "related_name".
note that this may give duplicate tags as it is a union between the two.
for distinct tags,
(tag.news.all() | tag.event.all()).distinct()
Related
I am having some problems with iterating over a ManyToMany field.
I want to have a Post and Tag model, and have the Post model extend the Tag model in the form of a ManyToMany relation.
Below are both my Tag and Post models.
class Tag(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return f"{self.name}"
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
content = RichTextField()
timestamp = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True,default="",max_length=1000)
tag = models.ManyToManyField(Tag,related_name='tags',blank=True)
# .... other non related functions
However, when I am in the Django shell, I can't seem to loop over these tags, despite the object having tags associated to it.
For example, I would do post1 = Post.objects.all()[0], and then post1.tag.name.all(), however it would give me an error saying "AttributeError: 'NoneType' object has no attribute 'all'
Everything else I have tried failed. What can I fix to solve the issue?
Thank you in advance
You are suppose to iterate through tags object like this
for tag in post1.tag.all():
print(tag.name)
It is very simple to iterate over ManyToManyField
for tag in post1.tag.all():
print(tag.name)
Other than iteration, I have some other suggestions. Like
tag will have many tags so naming it tags is more suitable. Secondly related_name is The name to use for the relation from the related object back to this one So in your case it should be posts instead of tags
So it will look like
tags = models.ManyToManyField(Tag, related_name='posts', blank=True)
Now you can access posts from any specific tag using this related_name. For example, If you have tag
tag1 = Tag.objects.all()[0]
Now you can get all posts with this tag using following line
tag1_posts = tag1.posts.all()
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)
I have the following models:
#models.py
class Section(models.Model):
name = models.CharField(max_length=20)
class Tags(models.Model):
parent = models.ForeignKey(Section)
name = models.CharField(max_length=255, blank=True)
class Article(TimeStampedMode):
...
tag = models.ForeignKey(Tags)
In Django admin, tag shows up as a HTML <select multiple>.
What I'm trying to do is:
A Section could have many Tags, and from Article I could pick a Tags from Section.
Also, it needs to be able to get a Article's Section(via tags.parent?).
Currently this works. But, instead of <select multiple>, Tags shows up as a <input> instead of a <select multiple>.
What I want is for both Tags and Section appear as <select multiple>.
edit:
What I want is:
By using a foreign key to define the relationship, you're limiting the number of Tags an Article may have to 1. For an Article to have more than one Tag, you'll want to use a ManyToMany relationship.
Read more on many-to-many relationships with Django here: https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
The Django admin site will automatically use a select if you're using a foreign key relationship, or a multi-select if you're using a many-to-many relationship.
Here's what a many-to-many will look like from Article to Tags:
class Article(TimeStampedMode):
...
tag = models.ManyToManyField(Tags)
I don't understand your need correctly. But according to your image, you need Section and Tags are set One-To-Many field in Article.
#models.py
class Section(models.Model):
name = models.CharField(max_length=20)
class TagName(models.Model):
tag_name = models.CharField(max_length=255, blank=True)
class Tags(models.Model):
parent = models.ForeignKey(Section)
name = models.ForeignKey(TagName)
class Article(TimeStampedMode):
...
tag = models.ForeignKey(Tags)
I think this method is more useful and matching with your need.
screenshot of the given model code:
this is Article page
go into tag and you can see select multiple for both fields
Thank you
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 ?
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)