Retrieve items from Many-to-Many relationship in Django query - django

Models.py
class SalesOrderItems(models.Model):
item = models.ForeignKey(MasterItems, on_delete=models.CASCADE)
item_quantity = models.IntegerField(default=0)
class SalesOrder(models.Model):
delivery_method_choice = (('Full-Truck Load', 'Full-Truck Load'), ('Part-Truck Load', 'Part-Truck Load'))
status_choices = (('On Hold', 'On Hold'),('Ready to Dispatch', 'Ready to Dispatch'),('Dispatched', 'Dispatched'))
owner = models.ForeignKey(Teacher, on_delete=models.CASCADE, related_name='so_owner')
client = models.ForeignKey(MasterClient, on_delete=models.CASCADE, related_name='so_client')
reference_no = models.CharField(max_length=500, blank=True, null=True)
date = models.DateField(default=datetime.date.today)
shipment_date = models.DateField(default=datetime.date.today)
delivery_method = models.CharField(max_length=500, default='Full-Truck Load', choices=delivery_method_choice)
items = models.ManyToManyField(SalesOrderItems, related_name='items_so', blank=True, null=True)
status = models.CharField(max_length=500, default='On Hold', choices=status_choices)
origin = models.CharField(max_length=255)
destination = models.CharField(max_length=255)
I want to retrieve the items of a particular sales order by django query
My attempt to get the items:
items = []
for i in sales_orders:
so = SalesOrder.objects.filter(pk=i)[0]
print("items",so.items)
Output:
items classroom.SalesOrderItems.None
How can I get the list of items in a particular SalesOrder ??

sales_orders = SalesOrder.objects.prefetch_related('items').filter(**sales_filter_here)
for sales_order in sales_orders:
for sales_order_item in sales_order.items.all():
print(sales_order_item)

this query will work for you
SalesOrder.objects.filter(id=1).values_list('items__item__item_name')
below is my simple Masteritems model
class MasterItems(models.Model):
item_name = models.CharField(max_length=50)
def __str__(self):
return self.item_name
by this way you will get every item name in queryset. this is my simple output
<QuerySet [('rice',), ('pepsi',), ('Computer',)]>

Related

How can i join two prefetch related in Django query

i have to do a django filter with complex form query and i don't have experience with select_related and prefetch_related methods.
models.py:
class User(models.Model):
class Park(models.Model):
class Plot(models.Model):
owner = models.ForeignKey(get_user_model(), verbose_name=_('Owner'), on_delete=models.SET_NULL, null=True, related_name='plots')
park = models.ForeignKey('Park', verbose_name=_('Park'), on_delete=models.SET_NULL, null=True, related_name='plots')
class Building(models.Model):
plot = models.ForeignKey('Plot', verbose_name=_('Plot'), on_delete=models.SET_NULL, blank=True, null=True, related_name='plot_buildings')
owner = models.ForeignKey(get_user_model(), verbose_name=_('Owner'), on_delete=models.SET_NULL, blank=True, null=True, related_name='owner_buildings')
tenant = models.ForeignKey(get_user_model(), verbose_name=_('Tenant'), on_delete=models.SET_NULL, blank=True, null=True, related_name='tenant_buildings')
I need to get all de user that are owner of a plot or a building or tenant of a building.
i have this for now...
filters.py:
class UserApiFilter(filters.FilterSet):
park_id = filters.CharFilter(method='filter_by_park')
def filter_by_park(self, queryset, name, value):
params = {name: value}
result = queryset.prefetch_related(Prefetch('plots', queryset=app_models.Plot.objects.filter(**params)))
Anybody could help me please?
Thanks in advance.
For now i have this solution:
params = {'park__id': value}
plots = app_models.Plot.objects.filter(**params)
users_plots = list(map(lambda x: x.owner, plots))
params = {'plot__park__id': value}
buildings = app_models.Building.objects.filter(**params)
users_buildings_owners = list(map(lambda x: x.owner, buildings))
users_buildings_tenats = list(map(lambda x: x.tenant, buildings))
result = list(map(lambda x: x.id, set(users_plots + users_buildings_owners + users_buildings_tenats)))
return queryset.filter(id__in=result)

Group By Django queryset by a foreignkey related field

I have a model Allotment
class Kit(models.Model):
kit_types = (('FLC', 'FLC'), ('FSC', 'FSC'), ('Crate', 'Crate'), ('PP Box', 'PP Box'))
kit_name = models.CharField(max_length=500, default=0)
kit_type = models.CharField(max_length=50, default=0, choices=kit_types, blank=True, null=True)
class AllotmentFlow(models.Model):
flow = models.ForeignKey(Flow, on_delete=models.CASCADE)
kit = models.ForeignKey(Kit, on_delete=models.CASCADE)
asked_quantity = models.IntegerField(default=0)
alloted_quantity = models.IntegerField(default=0)
class Allotment(models.Model):
transaction_no = models.IntegerField(default=0)
dispatch_date = models.DateTimeField(default=datetime.now)
send_from_warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
flows = models.ManyToManyField(AllotmentFlow)
For a stacked graph I am trying to get the data of different kit_type alloted in different months.
For that I have tried annotate but it isn't getting the desired results
dataset = Allotment.objects.all().annotate(
month=TruncMonth('dispatch_date')).values(
'month').annotate(dcount=Count('flows__kit__kit_type')).values('month', 'dcount')
Expected Output:
[{'month':xyz, 'kit_type':foo, count:123},...]
I am getting the month and count of kit type from above but how do I segregate it by kit_type?
having a field that represents your choice field names in this query is difficult
instead how about use the Count filter argument and annotate to get what you want
dataset = Allotment.objects.all().annotate(month=TruncMonth('dispatch_date')).values('month').annotate(
FLC_count=Count('flows__kit__kit_type', filter=Q(flows__kit__kit_type="FLC")),
FSC_count=Count('flows__kit__kit_type', filter=Q(flows__kit__kit_type="FSC")),
Crate_count=Count('flows__kit__kit_type', filter=Q(flows__kit__kit_type="Crate")),
PP_Box_count=Count('flows__kit__kit_type', filter=Q(flows__kit__kit_type="PP_Box")),
).values('month', 'FLC_count', 'FSC_count', 'Crate_count', 'PP_Box_count')

Django Joining Tables

I am trying to get the information from one table filtered by information from another table (I believe this is called joining tables).
I have these two models:
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.CharField(max_length=500)
price = models.DecimalField(max_digits=11, decimal_places=2, validators=[MinValueValidator(Decimal('0.01'))])
category = models.ForeignKey(Category, on_delete=models.CASCADE, default="")
imageURL = models.URLField(blank=True, max_length=500)
creator = models.ForeignKey(User, on_delete=models.CASCADE, related_name="creator", default="")
isOpen = models.BooleanField(default=True)
def __str__(self):
return f"{self.id} | {self.creator} | {self.title} | {self.price}"
class Watchlist(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="listingWatched", default="")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="userWatching", default="")
What I need to do is to get all the listings from a specific user Watchlist, the idea is to generate a page with all of the information of each of the listings that are in the user's watchlist. What should I do?
Thanks in advance!
Since is a foreign key, in Django you can access the information by calling the attribute
For example:
my_user = Watchlist.objects.get(pk=1)
print(my_user.listing.title)
You can also access to that attrbute in a query in case you need to filter upwards some value
values = Watchlist.objects.all().filter(listing__title='MyTitle')
my_titles = [x.title for x in values]
print(my_titles)
Or in your case, if you want to list all the title for a specific user
values = Watchlist.objects.all().filter(user='foo_user')
my_titles = [x.listing.title for x in values]
print(my_titles)
More documentation here

Django, detect if object.filter returns 0 objects

I got such table structure
class Item(models.Model):
id = models.AutoField(primary_key=True)
class Car(models.Model):
vin_number = models.CharField(max_length=250, null=True, blank=True)
item = models.OneToOneField(Item, on_delete=models.CASCADE)
name = models.CharField(max_length=1000, null=True)
year = models.IntegerField(null=True)
class Yacht(models.Model):
name = models.CharField(max_length=1000, default='')
boat_type = models.CharField(max_length=1000, default='', null=True)
item = models.OneToOneField(Item, on_delete=models.CASCADE)
description = models.TextField(default='')
year = models.IntegerField(null=False, default=0)
So, both Car and Yacht has relation with Item table
If I have only item id in request, what is the right way to write such query
data = request.POST
item = Car.objects.filter(item_id=data['item_id']).first()
if not item:
item = Yacht.objects.filter(item_id=data['item_id']).first()
Is there any way not to use if/else statement?
You don't need to look into the Car and Yacht model. Directly use the Item model's OneToOne relationship
item = Item.objects.filter(id = data['id']).first
This item has a specific id that relates to one of the other model. You can access them using
if item.car:
car = item.car
else:
yacht = item.yacht
But I guess you also need to add {{ related_name='tags', related_query_name='tag' }} to your OneToOne field for both car and yacht.
I would recommend that you check this out https://kite.com/python/docs/django.db.models.ForeignKey.
For more detail go to https://docs.djangoproject.com/en/3.0/topics/db/examples/one_to_one/
You need to use exists().
Car.objects.filter(item_id=data['item_id']).exists()
Yacht.objects.filter(item_id=data['item_id']).exists()
It returns you True or False.
Links to official docs.

Django ORM with Key/Value Table structure

I'm trying to get the django orm to replicate a call on a database for my current table structure:
Tables:
ServiceItems {Id, name, user, date_created}
ServiceItemsProps {fk_to_ServiceItems Item_id, Id, key, value}
I'm trying to select items from the ServiceItem table with multiple keys from the ServiceItemsProps table as columns.
I can accomplish this with a query like the following:
> select tbl1.value as bouncebacks, tbl2.value as assignees from
> service_items join service_item_props as tbl1 on tbl1.item_id =
> service_items.id join service_item_props as tbl2 on tbl2.item_id =
> service_items.id where service_items.item_type='CARD' and
> tbl1.key='bouncebacks' and tbl2.key='assignees'
But I'm not able to figure out how to reproduce this in Django's ORM. I would like to not inject raw SQL into the statements here, because codebase portability is important.
Section of models.py
class ServiceItems(models.Model):
class Meta:
db_table = 'service_items'
unique_together = ('service', 'item_type', 'item_id')
service = models.ForeignKey(Service, blank=False, db_column='service_id', on_delete=models.CASCADE)
item_type = models.CharField(max_length=255, blank=False)
url = models.TextField(blank=True, null=True)
item_id = models.TextField(blank=True, null=True)
item_creation_user = models.TextField(blank=True, null=True)
item_creation_date = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class ServiceItemProps(models.Model):
class Meta:
db_table = 'service_item_props'
item = models.ForeignKey(ServiceItems, blank=False, db_column='item_id', on_delete=models.CASCADE)
prop_id = models.TextField(blank=True, null=True)
key = models.CharField(max_length=255, blank=False)
value = models.TextField(blank=True, null=True)
# change one line to make it easier to query
item = models.ForeignKey(ServiceItems, blank=False, db_column='item_id', on_delete=models.CASCADE, related_name='item_props')
Query should become:
ServiceItem.objects.filter(Q(item_type='CARD') & (Q(item_props__key='bouncebacks') | Q(item_props__key='assignees'))
==============================================================
I think I misunderstood your query.
I believe this is a good case to use .raw() .
Try this one instead:
qs = ServiceItemProps.objects.raw('''
SELECT sip1.*, sip2.value as other_value
FROM {item_table} as service_items
INNER JOIN {props_table} as sip1 on sip1.item_id = service_items.id
INNER JOIN {props_table} as sip2 on sip2.item_id = service_items.id
WHERE service_items.item_type='CARD' and sip1.key='bouncebacks' and sip2.key='assignees'
'''.format(item_table=ServiceItems._meta.db_table, props_table=ServiceItemProps._meta.db_table)
for itemprop in qs:
print(qs.value, qs.other_value)