How to count objects using a ManyToManyField relationship in Django? - django

My models.py are like this:
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
tags = models.ManyToManyField(Tag, related_name='article_tags')
Lets say, I have a tag wit the name 'Movie Review', how do I get the number of articles that has a relationship with this tag? As in the count.
Thanks.

There are two ways to get the articles that are using a specific tag (your_tag in the examples below):
Either using a filtering on Article: Article.objects.filter(tags=your_tag)
Or using the related relation: your_tag.article_tags.all()
Once you did that, you can just use .count() to get the number of rows returned.

You can use Django's .count().
tag = Tag.objects.get(name='Movie Review')
article_count = tag.article_tags.all().count()

Use this query..
Article.objects.filter(tags__name='Movie Review')
to get the count
Article.objects.filter(tags__name='Movie Review').count()

Related

How to create a content tagging system for two models in Django?

Edited my question to make it more clear. And I am sorry if what i'm asking is obvious or easy to solve, I am just starting out.
Hello, I am developing a website which has 2 models for the content: Article and Comic. Now I want a system that allows me to tag Articles and Comics with certain keywords. These keywords can then be used to retrieve a list of objects from both the models and display them.
At first, I set up the system as follows:
class Article(models.Model):
.....
class Comic(models.Model):
.....
class Tag(models.Model):
tag = models.CharField(max_length = 25)
article = models.ForeignKey(Article)
comic = models.ForeignKey(Comic)
However, this did not work out.
Then I tried implementing 2 different tag models for the 2 different models: ArticleTag and ComicTag, in the following way:
class Article(models.Model):
.....
class ArticleTag(models.Model):
tag = models.CharField(max_length = 25)
article = models.ForeignKey(Article)
class Comic(models.Model):
.....
class ComicTag(models.Model):
tag = models.CharField(max_length = 25)
comic = models.ForeignKey(Comic)
This also did not work, as I was able to get a list of tags of an article, but I could not get a list of articles that had the same tag. And also it was using 2 different tag models so that was also not preferable.
A user suggested a ManyToManyField based on the previous information I had provided. However, how would this exactly work?
Now, what is the best way to create a content tagging system using Django? It should preferably work with just 3 models: Article, Comic and Tag, not 4 like I tried. In conclusion, I want a system that lets me get a list of objects from 2 models using one single tag. So, I would really appreciate help of any kind as I am an amateur programming (pretty obvious) and I am completely stumped by this problem.
You could use a ManyToManyField.
Basically something like the following. It's more intuitive if you change the name of ArticleTag.tag to ArticleTag.name. I've also added a related_name field to the M2M relationship so you can retrieve an article based on its tags using Article.objects.filter(tags__name="tag_name_here"). In the reverse direction, you could get all the tags for an article using ArticleTag.objects.filter(article__title="article_name_here").
class Article(models.Model):
title = models.CharField(max_length=140)
.....
class ArticleTag(models.Model):
name = models.CharField(max_length = 25)
articles = models.ManyToManyField(Article, related_name="tags")
[EDIT based on your updated question]
Based on your update, you could do this:
class Article(models.Model):
.....
class Comic(models.Model):
.....
class Tag(models.Model):
tag = models.CharField(max_length = 25)
articles = models.ManyToManyField(
Article,
related_name="tags",
blank=True,
null=True
)
comics = models.ManyToManyField(
Comic,
related_name="tags",
blank=True,
null=True
)
This way an Article/Comic can have many different tags, and any one tag can tag many different articles/comics. The blank=True, null=True means that you don't have to have an associated comic/article with a given tag, i.e., one tag could happen tag only comics but no articles.
To get a list of articles with the same tag:
Article.objects.filter(tags__name="your_tag_name")

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.)

Django fetch all relations

I have an app with a similar structure as this snippet
class Blog(models.Model):
name = models.CharField(max_length=25)
class Category(models.Model):
name = models.CharField(max_length=25)
blog = models.ForeignKey(Blog)
class Entry(models.Model):
title = models.CharField(max_length=25)
category = models.ForeignKey(Category)
What is the most generic way, that i will be able to use in other apps to fetch all blogs with their category and entries?
I thought about creating a manager for the Blog model, that can fetch all the Categories for that blog, but it's hardcoding the model names
class BlogManager(models.Manager):
def categories(self):
return Category.objects.filter(blog=self)
Any suggestions?
What you want is a Select Related. It returns a QuerySet that will automatically "follow" foreign-key relationships, selecting that additional related-object data when it executes its query. Your query for Blogs would look something like:
Blog.objects.select_related().filter( something )
or
Blog.objects.select_related().get( something )
Why not use a standard way?
Filter using Blog's PK with FK relation.
Category.objects.filter(blog__pk=self.pk)

How to create a unique_for_field slug in Django?

Django has a unique_for_date property you can set when adding a SlugField to your model. This causes the slug to be unique only for the Date of the field you specify:
class Example(models.Model):
title = models.CharField()
slug = models.SlugField(unique_for_date='publish')
publish = models.DateTimeField()
What would be the best way to achieve the same kind of functionality for a non-DateTime field like a ForeignKey? Ideally, I want to do something like this:
class Example(models.Model):
title = models.CharField()
slug = models.SlugField(unique_for='category')
category = models.ForeignKey(Category)
This way I could create the following urls:
/example/category-one/slug
/example/category-two/slug
/example/category-two/slug <--Rejected as duplicate
My ideas so far:
Add a unique index for the slug and categoryid to the table. This requires code outside of Django. And would the built-in admin handle this correctly when the insert/update fails?
Override the save for the model and add my own validation, throwing an error if a duplicate exists. I know this will work but it doesn't seem very DRY.
Create a new slug field inheriting from the base and add the unique_for functionality there. This seems like the best way but I looked through the core's unique_for_date code and it didn't seem very intuitive to extend it.
Any ideas, suggestions or opinions on the best way to do this?
What about unique_together?
class Example(models.Model):
title = models.CharField()
slug = models.SlugField(db_index=False)
category = models.ForeignKey(Category)
class Meta:
unique_together = (('slug','category'),)
# or also working since Django 1.0:
# unique_together = ('slug','category',)
This creates an index, but it is not outside of Django ;) Or did I miss the point?

Django order by related field

I want to sort a QuerySet of contacts by a related field. But I do not know how.
I tried it like this, but it does not work.
foundContacts.order_by("classification.kam")
Actually in a template I can access the kam value of a contact through contact.classification.kam since it is a OneToOne relationship.
The (simplified) models look like this:
class Classification(models.Model):
kam = models.ForeignKey(User)
contact = models.OneToOneField(Contact)
class Contact(models.Model):
title = models.ForeignKey(Title, blank=True, null=True)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
It should be:
foundContacts.order_by("classification__kam")
Here is a link for the Django docs on making queries that span relationships: https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships
You can also see some examples in the order_by reference:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.order_by
as the documentation indicates, it should be used as in queries about related models
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#order-by
foundContacts.order_by("classification__kam")
I've struggled a lot with this problem, this is how I solved it:
contact ordered by "kam" (Classification) field:
show_all_contact = Contact.objects.all().order_by(title__kam)
classification ordered by Users email (no sense in it, but only show how it works):
show_all_clasification = Classification.objects.all().order_by(kam__email)