class Author(models.Model):
name = models.CharField()
class Article(models.Model):
author = models.ForeignKey(Author)
created_at = models.DateTimeField(auto_now_add=True)
In a custom model manager for Author, how could one proceed to sort all the authors by the creation date of their most recent Article? Is this possible without using raw sql or for loops?
To show research effort and clarify the question, here's the (pseudo)code of how I'd do that in raw Python. It is untested and would probably not work because of the if not article.author in sorted_authors condition.
from django.db import models
from .models import Article
class AuthorsManager(models.Manager):
def get_sorted_authors(self):
articles = Article.objects.all().order_by('creation_date')
sorted_authors = []
for article in articles:
# not sure if this if condition would work
if not article.author in sorted_authors:
sorted_authors.append(article.author)
return sorted_authors
Another possible solution: add a last_article_datetime field to Author and sort by that. Widely more efficient and concise. Question still standing for any other use-case though.
Maybe like this:
Author.objects.annotate(lc=Max('article__created_at')).order_by('lc')
Related
I currently am making a simple newspaper Django application, and am working on the Articles model which looks as follows:
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
#date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
)
topic = models.CharField(max_length=255)
score_choices = [(-5,'-5'), (-4, '-4'), (-3,'-3'), (-2, '-2'), (-1,'-1'),
(0,'0'),(1,'1'), (2,'2'), (3,'3'), (4,'4'), (5,'5')]
score = models.IntegerField(choices=score_choices, default=0)
I am attempting to create another model that looks something like this:
class Topic(models.Model):
topic_name = models.CharField(max_length=255)
average_score =
Within the topic model, what I would like to do is somehow query all the Articles that have topic_name as their topic, and then return a list of scores that have been entered for Articles with that topic.
I'm currently pretty lost on this issue and I'm not even sure anymore if using the Django models is the best route. I've been reading through the Django Documentation as well as Third-Party books for a while but I can't find any reference here.
To summarize, I have two models: Article and Topic. Article has a field called 'topic' as well and I would like to create a field for my Topic class that is a function of the score field for all Article objects whose 'topic' field agrees with that of my separate Topic class. I apologize if this is confusing and I don't know all the terminology as I am trying to teach myself.
I have read through Django Documentation's pages on Models, Queries, Many-to-Many Relationships and various other properties. I still am unsure as to the solution.
Something like the following would work, using aggregation:
from django.db.models.aggregates import Avg
class Topic(models.Model):
topic_name = models.CharField(max_length=255)
def average_score(self):
return Article.objects.filter(topic=self.topic_name).aggregate(avg=Avg('score')).get('avg')
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')
Users can upload three different types of content onto our site: image, video, audio. Here are the models for each type:
class ImageItem(models.Model):
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to=img_get_file_path)
title = models.CharFiled(max_length=1000,
blank=True)
class VideoItem(models.Model):
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add=True)
video = models.FileField(upload_to=vid_get_file_path)
title = models.CharFiled(max_length=1000,
blank=True)
class AudioItem(models.Model):
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add=True)
audio = models.FileField(upload_to=aud_get_file_path)
title = models.CharFiled(max_length=1000,
blank=True)
I have a page called library.html, which renders all the items that a user has uploaded, in order from most recently uploaded to oldest uploads (it displays the title and upload_date of each instance, and puts a little icon on the left symbolizing what kind of item it is).
Assuming it requires three separate queries, how can I merge the three querysets? How can I make sure they are in order from most recently uploaded?
As an alternative, you can use multi-table inheritance and factor common attributes into a superclass model. Then you just order_by upload date on the superclass model. The third-party app django-model-utils provides a custom manager called Inheritance manager that lets you automatically downcast to the subclass models in your query.
from model_utils.managers import InheritanceManager
class MediaItem(models.Model):
objects = InheritanceManager()
user = models.ForeignKey(User)
upload_date = models.DateTimeField(auto_now_add=True)
title = models.CharFiled(max_length=1000,
blank=True)
class ImageItem(MediaItem):
image = models.ImageField(upload_to=img_get_file_path)
class VideoItem(MediaItem):
video = models.FileField(upload_to=vid_get_file_path)
class AudioItem(MediaItem):
audio = models.FileField(upload_to=aud_get_file_path)
Then, your query is just:
MediaItem.objects.all().order_by('upload_date').select_subclasses()
This way, you get what you want with one just query (with 3 joins). Plus, your data is better normalized, and it's fairly simple to support all sorts more complicated queries, as well as pagination.
Even if you don't go for that, I'd still use abstract base class inheritance to conceptually normalize your data model, even though you don't get the database-side and ORM benefits.
attrgetter can be used to pull out attributes of objects that you may want to key a sort by.
from operator import attrgetter
results = []
results.extend(list(AudioItem.objects.filter(...)))
results.extend(list(VideoItem.objects.filter(...)))
results.extend(list(ImageItem.objects.filter(...))
results.sort(key=attrgetter("upload_date")
Referencing the question, this is the exact solution I used. Influenced by monkut's answer and the top answer from Frantzdy's link in his comment (thanks guys).
from itertools import chain
from operator import attrgetter
images = ImageItem.objects.filter(user=user)
video = VideoItem.objects.filter(user=user)
audio = AudioItem.objects.filter(user=user)
result_list = sorted(chain(images, video, audio),
key=attrgetter('upload_date'),
reverse=True)
I'm new to Python and Django, so please be patient with me.
I have the following models:
class User(models.Model):
name = models.CharField(max_length = 50)
...
class Post(models.Model):
userBy = models.ForeignKey(User, related_name='post_user')
userWall = models.ForeignKey(User, related_name='receive_user')
timestamp = models.DateTimeField()
post = models.TextField()
class Friend(models.Model):
user1 = models.ForeignKey(User, related_name='request_user')
user2 = models.ForeignKey(User, related_name='accept_user')
isApproved = models.BooleanField()
class Meta:
unique_together = (('user1', 'user2'), )
I know that this may not be the best/easiest way to handle it with Django, but I learned it this way and I want to keep it like this.
Now, all I want to do is get all the post from one person and it's friends. The question now is how to do it with the Django filters?
I think in SQL it would look something like this:
SELECT p.* FORM Post p, Friend f
WHERE p.userBy=THEUSER OR (
(f.user1=THEUSER AND f.user2=p.userBy) OR
(f.user2=THEUSER AND f.user1=p.userBy)
)
With no guarantee of correctness, just to give an idea of the result I'm looking for.
from django.db.models import Q
Post.objects.filter( \
Q(userBy=some_user) | \
Q(userBy__accept_user__user1=some_user) | \
Q(userBy__request_user__user2=some_user)).distinct()
UPDATE
Sorry, that was my fault. I didn't pay attention to your related_name values. See updated code above. Using userBy__accept_user or userBy__request_user alone won't work because that'll be a reference to Friend which you can't compare to to User. What we're doing here is following the reverse relationship to Friend and then once we're there, seeing if the other user on the friend request is the user in question.
This also illustrates the importance of describing reverse relationships appropriately. Many people make the same mistake you've made here and name the related_name after the model they're creating the FK to (User), when actually, when we're talking about reversing the FK, we're now talking about Friend. Simply, your related names would make more sense as something like: friend_requests and accepted_friends.
(with User u)
friends_u1 = Friend.objects.filter(user1 = u).getlist('user2_id', flat=True)
friends_u2 = Friend.objects.filter(user2 = u).getlist('user1_id', flat=True)
friends_and_user = friends_u1+friends_u2+u.id
Post.objects.filter(userBy__id__in = friends_and_user)
I am using django-model-utils for inheritance Managers. I want to get results of only one subclass at a time.
managers.py
from model_utils.managers import InheritanceManager
class PostManager(InheritanceManager):
pass
models.py
from .managers import PostManager
class Post(models.Model):
title = models.CharField(max_length=20)
text = models.TextField()
objects = PostManager()
class ImagePost(Post, models.Model):
source = models.URLField()
image = models.ImageField(upload_to="images/%Y/%m/%d")
class VideoPost(Post, models.Model):
source = models.URLField()
I want to return results of only image type. by writing a simpler query like this.
Post.objects.filter(type='image').select_subclasses()
What i have tried:
if type == 'image':
Post.objects.filter(imagepost__isnull=False).select_subclasses()
This works but is kind of anti-pattern, i don't want to write conditions in views for every content type.
Is there better way like defining a property in models or converting it into a manager method? or am i missing something?
Have you tried to pass the class to select_subclasses method?
Post.objects.select_subclasses(ImagePost)
Check their doc about this feature.
Edit:
I misunderstood the question, but sounds like OP wants only the Post with type ImagePost. Doing select_subclasses(ImagePost) would fetch everything and convert the objects with type ImagePost to ImagePost instances. The solution should be as simple as :
image_posts = ImagePost.objects.all()