What is the best way of getting the max datetime of related objects of related objects of a Django instance? I need this value as an instance property.
For example, with these models,
class Show(Model):
is_live = BooleanField()
...
class Season(Model):
show = ForeignKeyField(Show, on_delete=CASCADE, related_name='seasons')
is_live = BooleanField()
...
class Episode(Model):
season = ForeignKeyField(Season, on_delete=CASCADE, related_name='episodes')
is_live = BooleanField()
live_date = DateTimeField(blank=True, null=True)
...
How could I get the most recent episode live_date of a show instance, including only episodes where the season is_live == True and the episode is_live == True?
I have tried this in the Show model:
#property
def max_episode_live_date(self)
return self.seasons.filter(
is_live=True
).aggregate(
max_=Max(
'episodes__live_date',
filter=Q(episodes__is_live=True)
)
).get('max_')
but I get this error
AttributeError: 'str' object has no attribute 'tzinfo'
I've tried using Django SubQuery expressions,
#property
def max_episode_live_date(self)
episodes = Episode.objects.filter(
is_live=True,
season=OuterRef('pk')
).values('live_date')
return self.seasons.filter(
is_live=True
).aggregate(
max_=Max(Subquery(episodes))
).get('max_')
but then I get django.db.utils.OperationalError: (1242, 'Subquery returns more than 1 row')
I believe this is due to some episodes containing null live_date, but I'm not sure how to work around. Any insight would be appreciated.
#property
def max_episode_live_date(self)
return self.seasons.filter(
is_live=True
).aggregate(
max_=Max(
'episodes__live_date',
filter=Q(episodes__is_live=True)
)
).get('max_')
The SQL
"SELECT MAX(CASE WHEN `episode`.`is_live` is true THEN `episode`.`live_date` ELSE NULL END) AS `max_`"
will be executed. The result will be a string or None. When a string is converted to DateTime with timezone, the error happens.
I think it may be a bug of Django.
Related
I am using flask-sqlalchemy and have a model representing a fish, which has a birthday as a Date object. Associated with the Fish class is a method which returns the number of months the fish has been alive, is there a way to filter based on what is returned from that custom method?
Here is my code
#the model
class Fish(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
birthday = db.Column(db.Date)
#the method for returning number of months
def getMonths(self):
if self.status == "Dead":
return
if self.birthday == None:
return
today = datetime.today().date()
birthday = self.birthday
age_difference = relativedelta.relativedelta(today,birthday)
return age_difference.months
Here is what I have tried so far
#number or months to filter by
cutoff = 6
#the query
result = Fish.query.filter(Fish.getMonths() >= cutoff).all()
but this is returning a type error, because there is no self passed to the method
TypeError: getMonths() missing 1 required positional argument: 'self'
Any help on how to do this would be greatly appreciated and let me know if you need any more information!
models.py
class club(models.Model):
name = models.CharField(max_length=30)
city = models.CharField(max_length=30)
premiere_leauge = models.BooleanField(default=False)
def __str__(self):
return self.name
Views.py
...
a = request.POST['club']
b = request.POST['city']
result = club.objects.all.get(name__exact=a, city__exact=b)
....
All is fine, however I believe result returns me:
def __str__(self):
return self.name
Whatsover, I would like an equivalent of 'SELECT * FROM APP_CLUB where name='something and city='something'; so i would be able to do further manipulation in django like:
if result[3] is True:
do_something()
As suggested in the offical documentation:
club.objects.filter(name='something')
It will return exactly the same as:
'SELECT * FROM APP_CLUB where name='something';
Example:
clubs = club.objects.filter(name__exact='something')
for club in clubs:
if club.premier_league:
do_something()
If you want to get only one club, then do it like this:
club = club.objects.get(name='something')
premier_league_value_of_club = club.premier_league
Use filter instead of get.
results = club.objects.filter(name__exact=a, city__exact=b)
You can then iterate over it to access all the model attributes like below
for result in results:
print result.name, result.city, result.premier_league
Also, according to PEP-8, you should name your class name should ideally be titlecase Club instead of lowercase club.
You're nearly there, I think you're missing the filter function here. You can use it like this :
a = request.POST['club']
b = request.POST['city']
result = club.objects.filter(name__exact=a, city__exact=b)
It will return you a query set with the actual database entries.
The __str__(self) function is used in transforming your query set entry into a string, whether you string-cast it or print it.
Then about this :
if result[3] is True:
do_something()
I don't get well what you mean about this, but if 3 is the id of the entry in the database, you then can do this :
if result.get(id=3).premiere_leauge:
do_something()
But you might want to check if the entry with the id exists first to avoid errors :
if result.filter(id=3).exists() and result.get(id=3).premiere_leauge:
do_something()
You should modify you query as shown below
result = club.objects.filter(name__exact=a, city__exact=b)
Good Luck !!
class Control(models.Model):
period = models.DurationField()
active = models.BooleanField()
device_collection = models.ForeignKey(DeviceSet)
class DeviceSet(models.Model):
name = models.CharField()
date_last_control = models.DateField()
def get_next_control(self):
return self.date_last_control + self.control_actif.period
#property
def control_actif(self):
if not hasattr(self, "_control"):
setattr(self, "_control", self.control_set.get(active=True))
return self._control
There are several Control associated with DeviceSet but only one Control which is active by DeviceSet.
I'd like to get the active Control of the DeviceSet when I get the queryset in a column _control.
I already try :
DeviceSet.objects.annotate(_control = Q(control__active=True))
That don't work
'WhereNode' object has no attribute 'output_field'
And after set output_field=Control I have the following exception:
type object 'Control' has no attribute 'resolve_expression'
I just want to have like a prefetch_related with filter but in a new column to use the _control attribute in model's method.
You are getting errors from what you've attempted because annotate method needs an aggregate function (eg Sum, Count etc) rather than a Q object.
Since Django 1.7 it's possible to do what you want using prefetch_related, see docs here:
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.Prefetch
DeviceSet.objects.prefetch_related(
Prefetch('control_set',
queryset=Control.objects.filter(active=True),
to_attr='_control')
)
Based on this question i wrote this code:
class CourseManager(models.Manager):
def current(self):
return self.filter(date_start < datetime.datetime.now()).filter(date_end > datetime.datetime.now())
class PlannedCourse(models.Model):
course = models.ForeignKey(ECourse)
date_start = models.DateTimeField()
date_end = models.DateTimeField(blank=True)
...
objects = CourseManager()
def __str__(self):
return self.course.name + ' ' + self.date_start.strftime("%d-%m-%Y %H:%M") + ' - ' + self.date_end.strftime("%d-%m-%Y %H:%M")
but when I try to run PlannedCourse.objects.current() I get the error:
NameError: name 'date_start' is not defined.
I'm a newbie and don't understand why this happens :(
You should use fields lookups: __gt instead of > and __lt instead of <:
class CourseManager(models.Manager):
def current(self):
return self.filter(date_start__lt=datetime.datetime.now(),
date_end__gt=datetime.datetime.now())
You're comparing an undefined python object (date_start) with a date in python. However, the goal of a queryset is to compare objects in the database. Even if date_start was defined, this would be the equivalent of calling .filter(True) or .filter(False).
To tell Django to check in the database, you have to pass keyword arguments to the filter that are converted to a query by the ORM:
return self.filter(date_start__lt=datetime.datetime.now(),
date_end__gt=datetime.datetime.now())
Note that you're not actually comparing date_start and datetime.datetime.now(), the .filter function uses **kwargs, and gets the following arguments as a dictionary:
{
'date_start__lt': datetime.datetime.now(),
'date_end__gt': datetime.datetime.now(),
}
The __lt and __gt are field lookups that tell the queryset how to perform the comparison in the database.
I am trying to use this way,
models.py
class Father(models.Model):
name = models.CharField(...)
def last_child_age(self):
children = self.child.order_by('-pk')
if len(children) > 0:
return find_Year(datetime.datetime.now()-children[0].birth_day)
return -1
class Child(models.model):
father = models.ForeignKey(Father, related_name='child')
birth_day = models.DateTimeField(auto_now_add=True)
views.py
def get_old_fathers(request):
father_list = Father.objects.filter(last_child_age__gte=15)
Returns:
Cannot resolve keyword
error.
What is the correct way of making this query other than iterating one by one.
You cant make queries using model class method (passing function to filter) in Django
If you need list of Father objects which have childs with age > 15 years:
d = datetime.date.today()
father_list = Father.objects.filter(
child__birth_day__lte=datetime.date(d.year-15, d.month, d.day))
Without adding flags to Child you can decide which is last (youngest) by making subqueries to child table. Annotation with max birthday (last son -> bigger birth_day date) can help. Try this query (im not test it):
from django.db.models import Max
father_list = Father.objects.annotate(max_birthday=Max('child__birth_day')
).filter(max_birthday__lte=datetime.date(d.year-15, d.month, d.day))
You have not posted your Child model and last_child_age code, but something like this should work:
Child.objects.filter(age__gte=15).prefetch_related('father')
last_child_age is a method, not a column. ORM queries generate SQL, and SQL don't know zilch about your models methods - how would the SQL database call back on your Python code ?