inside my app I have multiple models, like:
models.py:
class Company(models.Model):
name = models.CharField(max_length=100)
class Coworker(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
company = models.ForeignKey(Company, null=False, blank=False, on_delete=models.CASCADE)
As you can see, a Company can contain one, multiple or no Coworker! Is it possible to query Company but also receive the data from connected Coworker? For example something like this:
id | Company | Coworker
1 | Company_A | Coworker_A
2 | Company_B |
3 | Company_C | Coworker_B
4 | Company_C | Coworker_C
5 | Company_C | Coworker_D
6 | Company_D | Coworker_E
7 | Company_D | Coworker_F
8 | Company_E |
9 | Company_F | Coworker_G
10 | Company_F | Coworker_H
...
My problem is, that I can't query on the Coworker, because I don't want to miss those Companies that have no related data!
Thanks for your help and have a great day!
Quite simply, query by company and prefetch results for workers :
Company.objects.prefetch_related("coworker_set").all()
What this will do is give you a list of companies containing their list of workers in the attribute coworker_set. It will also populate those attributes in a single query (that's the whole point of prefetch_related).
More specifically, here is how you could use such a query :
for company in Company.objects.prefetch_related("coworker_set").all():
print(company.name)
for coworker in company.coworker_set.all():
print(coworker.first_name)
print(coworker.last_name)
This guarantees that you will iterate through all companies as well as through all coworkers, and you will only iterate through each one once (indeed, if you queried by coworkers you would see some companies multiple times and others none at all).
Related
Models:
class Regions(models.Model):
name = models.CharField(max_length=255, unique=True)
class Owners(models.Model):
name = models.CharField(max_length=255, null=False, unique=True)
url = models.URLField(null=True)
class Lands(models.Model):
region = models.ForeignKey(Regions, on_delete=models.CASCADE)
owner = models.ForeignKey(Owners, on_delete=models.PROTECT, null=True)
description = models.TextField(max_length=2000, null=True)
class LandChangeHistory(models.Model):
land = models.ForeignKey(Lands, on_delete=models.CASCADE, null=False, related_name='lands')
price = models.IntegerField()
size = models.IntegerField()
date_added = models.DateField(auto_now_add=True)
Queryset that works but i need it to be annotated in another queryset somehow:
lands_in_region = Lands.objects.values('region__name').annotate(count=Count('region_id'))
returns for example:
{'region__name': 'New York', 'count': 3}, {'region__name':
'Chicago', 'count': 2}
In the 2nd queryset i need count of lands available in region. But instead of real count, i always get count = 1. How to combine them? Im pretty sure i could do it in raw sql by joining two tables on field "region__id", but dont know how to do it in django orm.
f = LandFilter(request.GET, queryset=LandChangeHistory.objects.all()
.select_related('land', 'land__region', 'land__owner')
.annotate(usd_per_size=ExpressionWrapper(F('price') * 1.0 / F('size'), output_field=FloatField(max_length=3)))
.annotate(count=Count('land__region_id'))
)
For example. If it returns:
land1 | 100$ | 100m2 | New York
land2 | 105$ | 105m2 | New York
land3 | 102$ | 102m2 | Chicago
i need 1 more column, that counts for each land how many NewYork's and Chicago's are there
land1 | 100$ | 100m2 | New York | 2
land2 | 105$ | 105m2 | New York | 2
land3 | 102$ | 102m2 | Chicago | 1
This worked for me. Hope helps somebody.
First i tried to simply call Count() method right after filter, but that breaks query, since it tries to get data from DB immediately. But that was the correct way to think, so i added count annotate and selected it and it worked.
f = LandFilter(request.GET, queryset=LandChangeHistory.objects.all()
.select_related('land', 'land__region', 'land__owner')
.annotate(usd_per_size=ExpressionWrapper(F('price') * 1.0 / F('size'), output_field=FloatField(max_length=3)))
.annotate(count=Subquery(
Lands.objects.filter(region_id=OuterRef('land__region_id'))
.values('region_id')
.annotate(count=Count('pk'))
.values('count')))
)
I'm struggling with making an efficient queryset with the following model:
class Connection(models.Model):
from = models.ForeignKey(User, related_name='from_connections')
to = models.ForeignKey(User, related_name='to_connections')
status = models.SmallIntegerField()
What I'm trying to do is, for a specified user, fetch a list of all his connections (so where he is either from or to) and annotate the status where he is from but also annotate the reverse status where he is to.
So for the example:
from | to | status
------------------------
A | B | 1
B | A | 0
C | A | 2
A | D | 2
the results would be something like this:
user | status | reverse_status
-----------------------------------
B | 1 | 0
C | None | 2
D | 2 | None
The closest solution I've got to so far is something like this:
qs = Connection.objects.filter(from_id=a_id)
reverse_qs = Connection.objects.filter(from_id=OuterRef("to_id"), to_id=a_id)
qs = qs.annotate(reverse_status=Subquery(reverse_status.values("status")[:1]))
This almost gives me what I need but since the queryset is including only results where user A is from, the results obviously don't contain anything for user C (from the example table above).
I also tried exploring the route with using related_names like
User.objects.filter(Q(from_connections__to=a_id)|Q(to_connections__from=a_id).annotate...
but this approach didn't get me far.
Does anyone have any ideas how to solve this using Django ORM? Much appreciated.
When you create a model having 2 ForeignKeys, you have to specify a related_name, Example :
class Connection(models.Model):
from = models.ForeignKey(User, related_name = 'from_connection')
to = models.ForeignKey(User, related_name = 'to_connection')
status = models.SmallIntegerField()
That will help in backwards relationships, more details in the docs
I have a table with cryptocurrency prices:
id | price | pair_id | exchange_id | date
---+--------+---------+-------------+---------------------------
1 | 8232.7 | 1 | 1 | 2018-02-09 09:31:00.160837
2 | 8523.8 | 1 | 2 | 2018-02-09 09:31:01.353998
3 | 240.45 | 2 | 1 | 2018-02-09 09:31:02.524333
I want to get the latest prices of a single pair from different exchanges. In raw SQL, I do it like this:
SELECT b.price, b.date, k.price, k.date, AVG((b.price + k.price) / 2)
FROM converter_price b JOIN converter_price k
WHERE b.exchange_id=1 AND k.exchange_id=2 AND b.pair_id=1 AND k.pair_id=1
ORDER BY b.date DESC, k.date DESC LIMIT 1;
8320.1|2018-02-09 11:23:00.369810|8318.2|2018-02-09 11:23:06.467424|8245.05199328066
How to do such query in the Django ORM?
EDIT: Adding models.py. I understand that it's quite likely that I'll need to update it.
from django.db import models
# Create your models here.
class Pair(models.Model):
identifier = models.CharField('Pair identifier', max_length=6)
def __str__(self):
return self.identifier
class Exchange(models.Model):
name = models.CharField('Exchange name', max_length=20)
def __str__(self):
return self.name
class Price(models.Model):
pair = models.ForeignKey(Pair, on_delete=models.CASCADE)
exchange = models.ForeignKey(Exchange, on_delete=models.CASCADE)
price = models.FloatField(default=0)
date = models.DateTimeField(auto_now=True, db_index=True)
def __str__(self):
return '{} - {}: {} ({})'.format(
self.date, self.pair, self.price, self.exchange)
EDIT2: To clarify what I'm really after.
I want the latest price with pair_id=1 and exchange_id=1, and the latest price with pair_id=1 and exchange_id=2. With a single query, and without any subsequent processing in Python - of course I can get Price.objects.all() and then search for it myself, but that's not the right way to use the ORM.
The way to do this with raw SQL is joining the table to itself. Showing how to join a table to itself using the Django ORM would (probably) answer the question.
I'm getting duplication in my path (many to many) table, and would like it only to contain unique items.
models.py
class Image(models.Model):
path = models.CharField(max_length=128)
class User(models.Model):
username = models.CharField(max_length=32)
created = models.DateTimeField()
images = models.ManyToManyField(Image, through='ImageUser')
class ImageUser(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ForeignKey(Image, on_delete=models.CASCADE)
But, I seem to be able to create more than one image with the same path. I would like one unique image path to point to multiple users without having duplicate images in the Image table.
u = User.objects.create(username='AndyApple')
i = Image(path='img/andyapple.jpg')
i.save()
ui = ImageUser(user=u, image=i)
ui.save()
u2 = User.objects.create(username='BettyBanana')
ui = ImageUser(user=u2, image=i)
This seems to create two rows in the images table for the same image. The docs suggest this should not happen ManyToManyField.through
Thanks!
Are you sure your code adds duplicates to Image and not to ImageUser (which is how many-to-many table works)?
-------------------- --------------------------- ----------------------------
| Users | | ImageUser | | Image |
-------------------- --------------------------- ----------------------------
| id | username | | id | user_id | image_id | | id | path |
-------------------- --< --------------------------- >-- ----------------------------
| 1 | AndyApple | | 1 | 1 | 1 | | 1 | 'img/andyapple.jpg' |
-------------------- --------------------------- ----------------------------
| 2 | BettyBanana | | 2 | 2 | 1 |
-------------------- ---------------------------
But anyway, the problem is not right here, if you want:
"one unique image path to point to multiple users without having
duplicate images in the Image table."
then you have to define the field as unique, see code sample below. In this case if you will try to save the image with the pathy that is already in DB the exception will be raised. But note that in this case if two users upload different images, but with the same name, then the last uploaded image will be used for both users.
class Image(models.Model):
path = models.CharField(max_length=128, unique=True)
after a lot of research here and lots of reading here for me is still not clear in some cases how parent and child works:
+------------------------+ +------------------------+
| Room Model | +----------------+ | Room Model |
| <-----------+ +-------> |
+--+----------------+----+ | Supporter | +------------------------+
| | +----------------+ | |
| +------v-----+ | +------v-----+
| | | | | |
| | customer | | | customer |
| | | | | |
| +--+---------+ | +--+---------+
| | | |
| | | |
+--v+-----------v-+ +--v+-----------v-+
| | | |
| Device | | Device |
| | | |
+---+-----------+-+ +---+-----------+-+
| | | |
| | | |
| +-----v------------+ | +-----v------------+
| | | | | |
| | Issues / tickets | | | Issues / tickets |
| +------------------+ | +------------------+
| |
+v---------------+ +--------+ +v---------------+ +--------+
| pdf / pics | |Blob_key| | pdf / pics | |Blob_key|
| | | | | | | |
+----------------+-->--------+ +----------------+-->--------+
Here is my situation:
I have different rooms and the room ,model are the highest entity in the relationship, it does not change, but all the childs connected to it changes and some of them have more childs. So a room can have several customers at the same time and several devices which 1- some belong to the room and 2- some were allocated to the room:
class Room(MainModel):
# room_number is ID or key of room which is the parent of all entities
room_number = ndb.StringProperty()
# I don't know if this is the correct way to do this.
devices = ndb.KeyProperty(kind='Device', repeated=True)
# maybe this should be in the Device model?
devices_allocated = ndb.KeyProperty(kind='Device.allocation', repeated=True)
based_supporters = ndb.KeyProperty(kind='Supporter')
supporter = ndb.KeyProperty(kind='Supporter')
customers = ndb.KeyProperty(kind='Customers', repeated="True")
# Or should I go for ndb.KeyProperty(kind='Issue', repeated="True")?
issues = ndb.KeyProperty(kind='Vehicle.issues', repeated="True")
Then I have supporters which takes care of the rooms contents(customers and devices) they can support more than 1 room and and consequently several customers and devices(childs of the rooms) and have have one base room, means they are sitting in a room monitoring other rooms...
class Supporter(MainModel):
# id is ID or key of supporter which is child of room but becomes child of another room if changed
id = ndb.StringProperty()
name = ndb.StringProperty()
#I wonder if is possible to get these entities from the room model... Like this
devices = ndb.KeyProperty(kind='Room.devices', repeated=True)
customers = ndb.KeyProperty(kind='Customer', repeated=True)
#room which supporter stays
base_room = ndb.KeyProperty(kind='Room')
#rooms which supporter supports
rooms = ndb.KeyProperty(kind='Room', repeated="True")
Then I have customers, which are in a room(parent) and can use a device(child of the room) which should be able to be replaced depending on the situation, customers can change rooms and therefore the device of the room:
class Customer(MainModel):
# id is ID or key of customer which is child of room but becomes child of another room if changed
id = ndb.StringProperty()
name = ndb.StringProperty()
# device customer is using which can change
device = ndb.KeyProperty(kind='Device')
supporter = ndb.KeyProperty(kind='Supporter')
#customer room which can change
room = ndb.KeyProperty(kind='Room')
Then I have the devices which are always in a room(parent) or going to another room(parent change) the devices have files, pics(children of device) which moves with the device and it gets to another room, exception of the issue(child of the device and indirect child of the room) which belongs to the device but should show the room number it was created.
class Device(MainModel):
# id is ID or key of device which is child of room but becomes child of another room if changed
id = ndb.StringProperty()
name = ndb.StringProperty()
customer = ndb.KeyProperty(kind='Customer')
supporter = ndb.KeyProperty(kind='Supporter')
# device base room
base_room = ndb.KeyProperty(kind='Room')
# device actual room which can change
room = ndb.KeyProperty(kind='Room')
issue = ndb.KeyProperty(kind="issue", repeated="True")
file = ndb.KeyProperty(kind="File", repeated="True")
pics = ndb.KeyProperty(kind="Pics", repeated="True")
Finally I have issues and tickets from the devices(parent of the tickets and issues)
class Issue(MainModel):
# id is ID or key of issue which is child of device
id = ndb.StringProperty()
title = ndb.StringProperty()
# This is the parent of the issue model
device = ndb.KeyProperty(kind='device')
# place the issue were created
room = ndb.KeyProperty(kind='Room')
# issue child's attachments not the same as device attachments
file = ndb.KeyProperty(kind="File", repeated="True")
pics = ndb.KeyProperty(kind="Pics", repeated="True")
Is this the correct way to apply the key property? looks very confusion to me... specially in this complex relationship design model
what are you thinking? I there another way of doing this? How would be the best or more efficient approach to change parent or child or both dynamically?
I will try it an I will keep updating here, please feel free to share opinion why and how could be better or not.
Regards