Django Querying the Database - python

Here's my Answer Model,
class Answer(models.Model):
likes = models.ManyToManyField(User, related_name='answer_likes')
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
I wants to filter out the Answers which received Maximum likes in last 24 Hours. How can I do that in view?
Thank You :)

You need django aggregation api. Try:
from datetime import *
from django.db.models import Count
last_24 = datetime.now() - timedelta(hours = 24)
ans = Answer.objects.filter(timestamp__gte = last_24).annotate(counted_likes = Count('likes')).order_by('-counted_likes')
Now you can ans[0].counted_likesto find out how many likes answer ans[0] have, and order_by term up there assures to you that this first element has the largest number of likes.
See aggregation in django docs for further explanations.

Related

Django ORM Get users who have NOT updated their salary information in the last 1 year (Simlpe History )

I want to bring users who have not updated their salary information in the last 1 year. BUT WITH ORM not For Loop.
from simple_history.models import HistoricalRecords
class User(AbstractUser):
...
salary_expectation = models.IntegerField()
history = HistoricalRecords(cascade_delete_history=True)
################################################################
User.objects.filter(# MAGIC ) # Get users who have NOT updated their salary information in the last year
I can see that this is a package which has its documentation in querying its entries, see below:
https://django-simple-history.readthedocs.io/en/latest/querying_history.html
nevertheless you can do that intuitively following Django's normal behavior and a couple of SQL knowledge, I'd expect that history field's table most likely has a one-to-many relationship with the users table, so what I'd do is first open the database, find the column that shows the date of change, write down its name and then write this ORM query below
sub_query = ~Q(history__history_date__lte= "Replace with end of date", history__history_date__gte= "Replace with beginning of date", salary_expectation__isnull=False)
users = User.objects.filter(sub_query)
dont forget to import Q
from django.db.models import Q
You do not need to check HistoricalRecords class for this information.
Add created_at and updated_at (date_time_fields) fields to your User model
class User(...):
...
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Queryset Code
from django.db.models.functions import Now, ExtractDay
from django.contrib.auth import get_user_model
User = get_user_model()
users = User.objects.annotate(
# Calculate duration between now and last update date saved
duration=models.ExpressionWrapper(
Now() - models.F("updated_at"),
output_field=models.DurationField()
),
# Extract the amount of days in the duration
days=ExtractDay('duration'),
# Check if the number of days between the 2 fields exceeds 1 year (365.25 Days)
last_update_beyond_a_year=models.Case(
models.When(
models.Q(days__gte=365.25),
then=True
),
default=False,
output_field=models.BooleanField()
)
# Then filter
).filter(last_update_beyond_a_year=True)
and Voila !

Is there a way to merge 2 querysets in Django and order them by a their repecting field?

I'm trying to create a twitter clone and this is my user and tweet Model(some irrelevant fields have been removed).
class TwitterUser(models.Model):
user = models.OneToOneField(to=User, on_delete=models.CASCADE,primary_key=True)
Bio = models.CharField(max_length=200, blank=True)
Location = models.CharField(max_length=200, blank=True)
Website = models.URLField(blank=True)
ProfilePicture = models.ImageField(upload_to="Twitter", default="../static/twitter/images/default_profile.png")
CreateDate = models.DateField(default=timezone.now)
class Tweet(models.Model):
TweetBody = models.CharField(max_length=140, blank=False)
TweetDate = models.DateTimeField(default=timezone.now)
Owner= models.ForeignKey(to=TwitterUser,on_delete=models.CASCADE,related_name="Owner")
RetweetedBy= models.ManyToManyField(to=TwitterUser,related_name="Retweeted",blank=True,through="RetweetIntermediate")
and this the table that my many to many relationship for retweet is using.
class RetweetIntermediate(models.Model):
twitteruser=models.ForeignKey(TwitterUser,on_delete=models.CASCADE)
tweet=models.ForeignKey(Tweet,on_delete=models.CASCADE)
retweetDate=models.DateTimeField(default=timezone.now)
In profile view all the tweets and retweets should be shown ordered by date
what I'm doing right now (and it is working fine) is this:
def keymaker(a):
return a.TweetDate
def ProfileView(request):
tweets= list(Tweet.objects.filter(Owner=user.user_id,IsReplyToTweet__isnull=True).order_by("-TweetDate"))
retweets = list(user.Retweeted.all().order_by("-id"))
retweetInter=RetweetIntermediate.objects.all().order_by("-tweet_id")
for i , j in zip(retweets,retweetInter):
i.TweetDate=j.retweetDate
tweets=(tweets+retweets)
tweets.sort(key=keymaker,reverse=True)
I retrieve all the tweets ordered by date. then I retrieve all of retweets and make a list out of them and change the data of tweet to the date saved in intermediate table
and merge both lists and sort them by date.
I want to know is there a better way or more standard way to do this?
Thanks in advance.
You can do it using union together with annotate.
from django.db.models import F
tweets_qs = Tweet.objects\
.filter(Owner=user, IsReplyToTweet__isnull=True)\
.annotate(date=F('TweetDate'))
retweets_qs = Tweet.objects\
.filter(retweetintermediate__twitteruser=user)\
.annotate(date=F('retweetintermediate__retweetDate'))
timeline_qs = tweets_qs.union(retweets_qs).order_by('-date')
Notice that both querysets have Tweet objects.
Edit: Sorry for not understanding the question correctly the first time.

Order DateTimeField by rounded time using only the ORM?

I came up with a solution but it's using a model method ( as far as i understand it cannot be used with X.objects.filter() ) and then use the python method sorted. I've read that it's way faster to use django ORM than direct python so I'm searching for a solution. I precise that adding fields to my model is not possible as the database is already well populated.
Basically I've an Articles model :
class Articles(models.Model):
title = models.CharField(max_length=200, null=False, blank=False)
image = models.URLField(null=False, blank=False)
summary = models.TextField()
link = models.URLField(null=False, blank=False)
pub_date = models.DateTimeField(default=timezone.now)
source = models.ForeignKey(
Sources, on_delete=models.CASCADE, related_name="souurce")
category = models.ManyToManyField(Categories)
and what I want to do is ordering result by approximate publication date ( for example an article published at 5:34 and an another one published a 5:31 are both considered published at the same time ), then I can perform other orderings like per category, source or even by title.
Here is my class method to do that (by closest 10 minutes ):
def get_approximate_date(self):
pub_date = self.pub_date
def timeround10(dt):
"""timertound closets 10 minutes"""
a, b = divmod(round(dt.minute, -1), 60)
h = (dt.hour + a) % 24
m = b
new = dt.replace(hour=h, minute=m, second=0, microsecond=0)
return new
return timeround10(pub_date)
Then in my view I can do the following ( I chose to order by approximate date then by reverse alphabetical order ) :
articles_ = Articles.objects.all()
articles_list = sorted(articles_, key=lambda i: (i.get_approximate_date(), i.summary), reverse=True)
The closest thing I came up with using only django ORM is :
Articles.objects.order_by("-pub_date__year","-pub_date__month","-pub_date__day","-summary")
Apart from being ugly it only round the pub date by hour, so 1:59 PM = 1:01PM.
I'm aware of Trunc https://docs.djangoproject.com/en/3.1/ref/models/database-functions/#trunc but it doesn't only implement a way to order by hour minutes etc, maybe i should expand it if it's the only option.
Thanks in advance !

Django exclude on multiple fields in subquery

Given some code like this:
# coding: utf-8
import datetime
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
class Premium(models.Model):
"""Access to Premium Features™®."""
end = models.DateField()
user = models.ForeignKey(User)
site = models.ForeignKey(Site)
def get_ending_premiums():
"""Get a queryset of all Premiums for which a user has none following."""
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
future_premiums = Premium.objects.filter(end__gt=tomorrow).values('user', 'site')
return Premium.objects.filter(end=tomorrow).exclude(
# Would love if something like this actually worked...
user_and_site__in=future_premiums,
)
How can I complete get_ending_premiums()? One of the key things is I want Premiums only when there isn't another one that ends later, but on a per-site basis. So if a user has another Premium on groceries.com, the one about to end tomorrow doesn't get returned, but if they don't have another Premium on officesupplies.com, that one does get returned.
(Note the line with with the comments before it doesn’t actually work... that’s the part I need to complete.)
I can work out how to do this outside the ORM but I’d really prefer an ORM solution, as we’re planning on switching database vendors in a few months, so I’m trying to avoid raw SQL as much as possible.
Here’s a test for the behavior I’d like to get:
class PremiumTest(TestCase):
def test_gets_ending_premiums(self):
today = date(2020, 6, 5)
tomorrow = today + timedelta(days=1)
next_year = today + timedelta(days=366)
groceries = Site.objects.create(domain='groceries.com')
catvids = Site.objects.create(domain='catvids.com')
dave = User.objects.create_user('dave')
sally = User.objects.create_user('sally')
Premium.objects.create(user=dave, site=groceries, end=tomorrow)
Premium.objects.create(user=dave, site=groceries, end=next_year)
Premium.objects.create(user=dave, site=catvids, end=tomorrow)
Premium.objects.create(user=sally, site=groceries, end=tomorrow)
Premium.objects.create(user=sally, site=catvids, end=tomorrow)
Premium.objects.create(user=sally, site=catvids, end=next_year)
ending_premiums = get_ending_premiums(today)
ending = set((p.user, p.site) for p in ending_premiums)
self.assertNotIn((dave, groceries), ending)
self.assertIn((dave, catvids), ending)
self.assertIn((sally, groceries), ending)
self.assertNotIn((sally, catvids), ending)
self.assertEqual(2, len(ending_premiums))
I've come up with this... It's got some raw SQL but it still returns a QuerySet with normal QuerySet methods (although it uses the apparently deprecated QuerySet.extra() method)
def get_ending_premiums(day=None):
"""Get a queryset of Premiums for which a user has none following."""
if day is None:
day = date.today()
tomorrow = day + timedelta(days=1)
ending_premiums = Premium.objects.filter(
end=tomorrow,
).extra(
where=['NOT EXISTS (SELECT NULL FROM premium_premium child where premium_premium.site_id = site_id AND premium_premium.user_id = user_id AND end > %s )'],
params=[tomorrow],
)
return ending_premiums
Still wondering if there isn’t a better way...

Django-date incrementation in a list with ManyToManyField

New to django/programming, any help is greatly appreciated. I need help moving through a history of doctor appointments and selecting what immunizations were performed at each appointment, then creating a date when the immunization is due in the future (based on an immunization information table, which has the proper interval of immunizations and will increment from the visit date)
models.py
class Immunizations(models.Model):
immunization = models.CharField(max_length=100, null=True)
interval = models.CharField(max_length=5, null=True)**This should probably be an integer field, will change later
class Visit(models.Model):
patient = models.ForeignKey(Patients)
date_of_visit = models.DateField(null=True)
weight = models.CharField(max_length=5, null=True)
immunization = models.ManyToManyField(Immunizations)
timestamp = models.DateTimeField(auto_now_add=True, default=datetime.datetime.now())
active = models.BooleanField(default=True)
I have been reading the documentation and questions on SO all weekend, but am still very conflicted about what way to go through this.
What I want is:
Visit.date_of_visit1
Visit.immunization1, Visit.date_of_visit1 + Immunization.interval1
Visit.immunization2, Visit.date_of_visit1 + Immunization.interval2
Visit.date_of_visit2
Visit.immunization1, Visit.date_of_visit2 + Immunization.interval1
ETC
This could go on for years with each visit having different immunizations performed. I want to maintain a record of which immunization was performed and record the due date, even if that due date has passed.
views.py
def visit_profile(request, slug):
patient = Patients.objects.get(slug=slug)
try:
visit = Visit.objects.filter(patient_id=patient.id)
except:
return HttpResponseRedirect('/')
#Immunization Due Dates
visitdate = Visit.objects.get(patient_id=patient.id, active=1).date_of_visit
imm = Immunizations.objects.all()
visitimm = []
for immunization in imm:
due = Immunizations.objects.get(id= immunization.pk)
duedate = visitdate + timedelta(days=int(due.interval))
visitimm.append((due, duedate))
return render_to_response('patient.html',locals(), context_instance=RequestContext(request))
Need help with my views.py. The above works, but only at showing the active=1 visit information. I can't figure out how to modify/re-do to achieve what I want and be able to access the data in my template file. I've experimented with __in method, itertools, looping, etc. Can anyone provide the proper method/direction for doing this? I will go back and properly setup error catching once I can get the code to work. Thanks!
Yep, make interval an IntegerField or maybe rather a PositiveSmallIntegerField since it will never get a negative value nor a very huge number.
Careful, better don't mix plural and singular in model names, they affect the related names when you traverse your foreign keys which makes it a pain to debug, see here. I prefer to use only singulars.
Instead of:
visit = Visit.objects.filter(patient_id=patient.id)
You can simply type:
visit = Visit.objects.filter(patient=patient)
Try something like this
def visit_profile(request, slug):
patient = Patients.objects.get(slug=slug)
visitimm = []
# Looping over all active visit records of the patient in date order
for v in patient.visit_set
.filter(active=True).order_by('date_of_visit'):
# Looping over each visit's immunizations
for i in v.immunizations_set.all():
duedate = v.date_of_visit + timedelta(days=int(i.interval))
visitimm.append((i, duedate))
...

Categories

Resources