Django ORM: Filter on timedelta - python

I've got a field 'frequency' and i'd like use it in my filter.
My model:
class Foo(models.Model):
name = models.CharField(max_length=50)
frequency = models.IntegerField(default='60')
last_checked = models.DateTimeField()
I'd like get my data some like it, but instead of '10' minutes I can put there 'frequency' field.
Foo.objects.filter(last_checked__lt=timezone.now()-timedelta(minutes=10))
How can i do this? Or maybe I shoud go other way? Thanks for helpful tips.

Related

Django: add new column by function ( datetimefield to datefield )

Change datetimefield to datefield and put in in new column
python ways are useless because i want to be able to use order_by and distinct
i don't want to use sorted() and etc
my django model is something like this
class Product(models.Model):
price = models.PositiveIntegerField(null=False, default=0)
discounted_price = models.PositiveIntegerField(null=False, default=0)
date = models.DateTimeField(auto_now_add=True)
And i want to be able to call date() function
and put it in another column and order_by it
Product.objects.annotate(day=date.date()).order_by('-day').distinct('day')
You can do somthing like this
Product.objects.annotate(date__date=date.date()).order_by('-date').distinct('date')
check doc. for more information
After lots of search i found my answer
from django.db.models.functions import TruncDate
Product.objects.annotate(day=TruncDate('date')).order_by('-day').distinct('day')

Django Aggregation: Aggregate on relational queryset

Suppose I have following models, which store questions and options for those questions.
P.S: I am just writing the basic code just to give you idea.
class Question(models.Model):
text = models.CharField()
class Option(models.Model):
text = models.CharField()
class QuestionOption(models.Model):
question = models.ForeignKey(Question)
option = models.ForeignKey(Option)
And then I have models which store user Feedback and Options selected for each Question in the Feedback survey.
class Feedback(models.Model):
full_name = models.CharField()
cell_phone = models.CharField()
created_at = models.DateTime()
class FeedbackOption(models.Model):
feedback = models.ForeignKey(Feedback, related_name='feedback_options')
option = models.ForeignKey(QuestionOption)
Every feedback will have lots of feedback option objects. Now I want to filter all the feedback whose feedback options contain specific QuestionOption object and then perform aggregate query where I am checking if that feedback's FeedbackOptions option text is 'boo' add count. Can i do this in one step, Something like this
# lets say i want to filter all feedback with QuestionOption id 1
stats = Feedback.objects.filter(feedback_options__option=1).aggregate(
boo=Count(Case(When(feedback_options__option__option__text='boo', then=1))),
hoo=Count(Case(When(feedback_options__option__option__text='hoo', then=1))))
It looks like it's applying aggregate on the only feedback option where option id is 1 not the rest of the feedback options for each feedback.
.filter() works as a where clause. When you add another method like .aggregate()to the resulting queryset, it is bounded by the same where clause.
In Django 1.11 you can use Subquery
from django.db.models import OuterRef, Subquery, Count
Feedback.objects.filter(feedback_options__option=o1).annotate(
boo=Subquery(
FeedbackOption.objects.filter(option__option__text="boo", feedback=OuterRef('pk')).values('feedback').annotate(count=Count('pk')).values('count'),
output_field=models.IntegerField())
)
Similarly you can add another annotation for hoo.
Doc reference

Django annotate on several fields related to same model

I have two models:
class Account(models.Model):
...
class Transaction(models.Model):
....
account = models.ForeignKey(Account)
source_account = models.ForeignKey(Account, null=True)
I need to display the number of transactions for each of a user's accounts. Django's annotate seemed like the proper tool for this task. I did:
queryset = models.Account.objects.filter(user=self.request.user)
queryset.annotate(transactions_count=Count('transaction'))
This gives the correct number for transactions with account field set to the predicate account but leaves out transactions where source_account is set to the predicate account.
Using the Django shell I am able to do something like:
accounts_count = user_transactions.filter(Q(account=account)|Q(source_account=account)).count()
This gives the correct answer. Is there something I am doing wrong? Can someone point me in the correct direction. Any assistance is highly appreciated.
I would set related_name to your ForeignKey fields. Then it's a bit easier to work with them. So for example in your models let's set:
class Transaction(models.Model):
...
account = models.ForeignKey(Account, related_name='transactions')
source_account = models.ForeignKey(Account, null=True, related_name='source_transactions')
then can do something like:
queryset = models.Account.objects.filter(user=self.request.user).annotate(transactions_count=(Count('transactions')+Count('source_transactions'))
it would work without the naming too, it's just more readable and easier. The main point is adding the two Count as one field in annotate.
The best approach for these types of problems is to imagine them in raw SQL and then try to mimic it in Django ORM.
(in raw sql you would also simply just add two columns like SELECT (a.col + a.col2) AS count
The problem is that your transaction has to ForgeinKeys to Account. I would suggest trying something like this
class Transaction(models.Model):
....
account = models.ForeignKey(Account, related_name="transaction_account")
source_account = models.ForeignKey(Account, null=True, related_name="transaction_source_account")
Then in your query:
queryset.annotate(transactions_count=((Count('transaction_account') + Count('transaction_source_account'))

Large ManyToMany relations on django admin form

So, I have the following models:
class Band(models.Model):
name = models.CharField(max_length=50)
class Contract(models.Model):
band = models.ForeignKey(Band)
when = models.DateTimeField(auto_now_add=True)
salary = models.IntegerField()
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
bands = models.ManyToManyField(Band, through=Contract)
class Album(models.Model):
artist = models.ForeignKey(Musician)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
So, I wanted to expose that on the admin page. So far, so good.
Note that our musicians here keep jumping in and out from bands. Some say one of them even had been on over 2 millions bands in his life-time. I don't know, maybe the bands are Whitesnake, Metallica or something.
How should we do that on the Django Admin Page?
I tried using raw_id_fields and apart the fact I didn't like the effect, it didn't work so well. It took a lot of time to load and it didn't let me add more ids. Weird.
I've used admin.StackedInline with no luck cause it will try to load every contract in a which, well, it's gonna take only 2 thousand years.
When Musician had a direct relation to Band it worked just fine with this library. But now that the relation isn't an straight one. Looks like autocomplete doesn't support it(it was getting slow anyway).
So, with all of this, I ask you lord SO members. What's the best way to do this? Is it autocomplete? Someone must have had to come across this issue!
Thanks in advance.
To avoid loading every bands in your admin page use autocomplete_fields Django doc.
Just use it like that in your admin.py.
autocomplete_fields = ('bands',)
Then no bands will be pulled from DB to front, but you will be able to select it through a Select2 search field and it will be printed as "tags".
I found this solution and hope it will help somebody in the same situation:
I have many to many relations between the Product and Characteristic model.
So, in the admin.py I am setting a form for a Product like the following where catch/get all the Characteristics and make the "prefecth_related" for Characteristic, as well the "select_related" could be done there:
class ProductAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['characteristics'].queryset = Characteristic.objects.prefetch_related('category').all()

Database query across django ManyToManyField

I'd like to find how to select all objects whose ManyToMany field contains another object. I have the following models (stripped down)
class Category(models.Model):
pass
class Picture(models.Model):
categories = models.ManyToManyField(Category)
visible = models.BooleanField()
I need a function to select all the Pictures in one or more Categories:
def pics_in_cats(cat_ids=()):
pass
BUT it needs to return a QuerySet if possible so that I can do something like:
pics_in_cats((1,2,3)).filter(visible=True)
It could be done by loading all the relevant Category objects and merging their picture_set attributes, but that seems inefficient. I'd also like to avoid falling back to raw SQL if possible.
Thanks in advance
Why write a custom function and not use something like this? (untested)
pics = Picture.objects.filter(categories__in = [1,2,3]).filter(visible=True)

Categories