Django QuerySet filter on inverse relationship - django

Really struggling with this one and could appreciate some help. I have the following model...
class Booking(models.Model):
property = models.ForeignKey(Property, related_name='booking', on_delete=models.SET_NULL, null=True)
check_in_date = models.DateField()
check_out_date = models.DateField()
def __str__(self):
return f"{self.property}"
class Property(models.Model):
name = models.CharField(max_length=100, blank = False)
def __str__(self):
return f"{self.name}"
But when I run the following (below) to retrieve property bookings with a date equal to 2021-05-14, instead of just the bookings with a check_in_date of 2021-05-14 being returned, all the bookings for each property are returned even if they don't have a check_in_date of 2021-05-14
Property.objects.filter(booking__check_in_date='2021-05-14')
Probably something really simple, but I'm new to Django have been stuck on this all morning and could appreciate some help. Thanks

You actually need something like this:
bookings = Booking.objects.filter(check_in_date='2021-05-14')
properties = [booking.property for booking in bookings]
Because property is available on a Booking object and you're initally filtering bookings.

to filter DateFields in Django you must convert the date string to a datetime object:
from datetime import datetime
check_in_date = datetime.strptime('2021-05-14', '%Y-%m-%d')
properties = Property.objects.filter(booking__check_in_date=check_in_date)

Related

How to add dynamic field to django.core.serializers.serialize

I'm trying to export a queryset into json format. However, my query has a dynamic field (ie not defined in the model), and when I try to add it nothing shows.
My model:
class MyModel(models.Model):
id = models.TextField(primary_key=True, blank=True)
quantity = models.IntegerField(null=True, blank=True)
rate = models.IntegerField(null=True, blank=True)
class Meta:
managed = False
My queryset:
qs = MyModel.objects.filter(id=id).annotate(result=F('rate') * F('quantity'))
My call:
class ClassName:
#classmethod
def build__json(cls, queryset):
geojson_str = serialize('json',
queryset,
fields=('result')
)
my_geojson = json.loads(geojson_str)
return my_geojson
qs_json = ClassName.build_json(qs)
Is there a way to use serialize to do this? Or do I need to write a custom class?
PS: I'm not building a view, just trying to convert a queryset into a json.
Thanks in advance
I think that would solve you're problem
serializers.py
class Serializer(serializers.ModelSerializer):
...
rate_quantity = serializers.CharField(read_only=True, source="rate_quantity_value")
models.py
#property
def rate_quantity_value(self):
return rate * quantity

django orm foreign key latest data using filter query

class Schedule(models.Model):
user = models.ForeignKey(USER, on_delete=models.SET_NULL, null=True)
area = models.ForeignKey(Site, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True, null=True)
updated_at = models.DateTimeField(auto_now=True, null=True)
area = Schedule.objects.values("area").annotate(latest=Max('created_at')).values("area")
latest = Schedule.objects.values("area").annotate(latest=Max('created_at')).values("latest")
Schedule.objects.filter(created_at__in=latest, area__in=area)
I got the value I want.
However, I am uneasy when I filter with ForeignKey and DateTimeField. Can't there be a better way?
Also, can't you make it cleaner other than the code above?
Guess you can create a Manager for your Model and use selected_related feature, it will create quick query for you, and it`s ready "out-of-the-box" Django solution. Plz see Django docs.
Here`s example based on your code
In Model.py something like
class SheduleManager(model.Manager):
def all_prefetched_data(self):
qs = self.get_queryset()
qs = qs.select_related(
'area_set'
)
return qs
in the same Model.py you need to assign your manager in Shedule model
listing = SheduleManager
Now in view you can sort db items by creation date, query with the ForiegnKey fetching through all your db. You also can display result, for example in your_template.html with the dictionary b_list
view.py
def GetList(request):
sh_list = Schedul.listing.all().order_by('-created_at')
return render(request, 'your_template.html', {'b_list': sh_list})

Overwriting Datetime is not working in Postgresql in Django

So what I need to do is to change few rows in a model DateTime field to 40 days in the past, with Django using a PostgreSQL database. Where I choose all products with an even ID and change the date_uploaded value.
This is what I am currently doing...
from django.core.management.base import BaseCommand
from store.models import Product
import datetime
class Command(BaseCommand):
def handle(self, *args, **options):
all_products = Product.objects.all()
for product in all_products:
if product.pk % 2 == 0:
product.date_uploaded = product.date_uploaded - datetime.timedelta(40,2,0,0)
product.save()
print(product.date_uploaded)
And for some reason when I try to save the product it works with no errors but the DateTime value is not changed. Is there anything wrong with what I am doing?
this is my models.py
class Product(models.Model):
image1 = models.ImageField(upload_to="product_images", default="https://eblossomsl.s3-us-west-2.amazonaws.com/logo.png")
image2 = models.ImageField(upload_to="product_images", blank=True)
image3 = models.ImageField(upload_to="product_images", blank=True)
image4 = models.ImageField(upload_to="product_images", blank=True)
name = models.CharField(max_length=100, unique=True)
category = models.CharField(choices=CATEGORY, max_length=20, default="HRT", db_index=True)
price = models.PositiveIntegerField()
search_query = models.TextField(blank=True, null=True)
date_uploaded = models.DateTimeField(auto_now=timezone.now())
quantity_bought = models.PositiveIntegerField(default=0)
Any help would be greatly appreciated since I am not sure what I am doing wrong.
The problem is in auto_now argument passed into DateTimeField, this argument is responsible for changing the value of the field to the current datetime each time .save() is called on the object, i.e. every time you are running your script it sets it to current datetime and your changes are ignored. What you really need is auto_now_add which sets the value only once at the object creation.
date_uploaded = models.DateTimeField(auto_now_add=timezone.now())
Run makemigrations, apply them and run your script again.

how can i add an expiration date function on a model?

i am trying to make a key verification app that when accepted it would create the same app on the user's profile, so i have done everything but i have struggled making the expiration date part, i want the expired boolean become true when the date is expired, but i have no idea on how to implement it
#models.py
class ProductKey(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE,
unique=False)
key = models.CharField(max_length=14)
valid_from = models.DateTimeField(default=timezone.now)
valid_to = models.DateTimeField()
expired = models.BooleanField(default=False)
Please do not add a database field for this. You will introduce data duplication: if you later set the valid_to, you will have to update expred as well, so the logic would introduce extra challenges.
You can annotate your ProductKey model, such that objects that arise from this have an attribute expired:
from django.db.models import BooleanField, ExpressionWrapper, Q
from django.db.models.functions import Now
ProductKey.objects.annotate(
expired=ExpressionWrapper(Q(valid_to__lt=Now()), output_field=BooleanField())
)
You can then filter on that property. For example you can retrieve the ProductKeys that are expired with:
ProductKey.objects.annotate(
expired=ExpressionWrapper(Q(valid_to__lt=Now()), output_field=BooleanField())
).filter(expired=True)
If you need this often, you can annotate this in the manager, like:
class ExpiredManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
expired=ExpressionWrapper(Q(valid_to__lt=Now()), output_field=BooleanField())
)
class ProductKey(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE,
unique=False)
key = models.CharField(max_length=14)
valid_from = models.DateTimeField(default=timezone.now)
valid_to = models.DateTimeField()
objects = ExpiredManager()
You could use a property for this use case and calculate that on the fly.
#property
def expired(self):
# calculate here if its still valid

Django how to order queryset based on filtered many to many field?

Hi all i'm trying to figure out how to order a queryset based on a manyToMany field but I need to filter that field as well. Here is the exact problem.
qs = Drink.objects.all()
I want to order this queryset by a related field ingredientsuserneeds
Models:
class IngredientsUserNeeds(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
drinks = models.ManyToManyField(Drink)
count_need = models.IntegerField(default=0)
class Drink(models.Model):
name = models.CharField(max_length = 1000, null=True, blank=True)
user = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True)
In a DRF list view, I want to order the queryset by the drink's ingredientuserneeds_set but filtered by the user which will return a queryset that is of length one, finally ordering by that single object's count_need in the queryset. It would be sorta like
qs = qs.order_by('ingredientsuserneeds_set__filter(user=user)__first()__count_need')
But clearly, you cant do what i typed. Any ideas?
This should do the trick:
from django.db import models
from django.db.models import Case, When, F
Drink.objects.all().annotate(
count_need=Case(When(
ingredients__user__email='wanaryytel#outlook.com', then=F('ingredients__count_need')),
output_field=models.IntegerField()
)
).first().count_need