Using `select_related` with a table with composite primary key [Django] - django

Is there a way for Django to support composite primary key in combination of select_related or prefetch_related?
I have a Records table with a composite primary key (device_id, created) with a schema like this (simplified):
class Records(models.Model):
class Meta:
managed = False
unique_together = (("device", "created"),)
# The primary key is really (device_id, created)
device = models.ForeignKey("hardware.Device", primary_key=True, on_delete=models.CASCADE)
created = models.DateTimeField(db_index=True)
value = models.FloatField(default=0)
A record belongs to a Device, which is modeled like this (simplified):
class Device(models.Model):
car = models.ForeignKey("cars.Car", on_delete=models.CASCADE)
last_record_at = models.DateTimeField(null=True, blank=True)
I wish to run a query that will return a list of devices, but for each one also
contain the last record.
In theory, this would be something like that:
Device.objects.filter(...).select_related("car", "last_record")
But obviously "last_record" is not a foreign key, since Records contains a composite primary key which Django doesn't support.
What would be the best way to do this, other than rewriting the query in raw sql? Is there a reasonable way to override select_related to handle composite keys?

Related

Django filter queryset of a models that are related with a foreign key to other model?

I have a simple question.
I have two models(Waiter and Manager) and they both contains same foreign key to restaurant as:
class Managers(BaseUser):
restaurant = models.ForeignKey(Restaurants, on_delete=models.CASCADE)
class Waiters(BaseUser):
restaurant = models.ForeignKey(Restaurants, on_delete=models.CASCADE)
And restaurant model:
class Restaurants(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(max_length=250)
location = models.CharField(max_length=200)
rating = models.DecimalField(null=True, decimal_places=2, max_digits=5)
So I need to get all waiters that restaurant=managers__restaurant_id.
I think in SQL would be:
select *
From Waiters w
Left outer join Managers m
On w.restaurant_id = m.restaurant_id
Note*
I'm able to query that like below:
manager = Managers.objects.filter(id=request.usr.id)
queryset=Waiters.objects.filter(restaurant=manager.restaurant_id)
But is there any way that i could do it in one query.
But is there any way that i could do it in one query.
This is in one query, but it will work with a subquery is probably not that efficient.
You can however filter in a more compact way with:
Waiters.objects.filter(restaurant__managers=request.user.id)
We can look "through" relations by using double underscores (__). Here we thus are looking for Waiters objects for which it restaurant is related to a Managers object with the given id of request.user.id.
how about this??
queryset = Managers.objects.filter(id=request.usr.id and Waiters.objects.filter(restaurant=manager.restaurant_id))

ForeignKey updates aren't cascading on Django created tables in Postgres

This is the error that I get when I try to update the value in the "parent" table that the foreign key is looking at in a related table:
ERROR: update or delete on table "product" violates foreign key constraint "pd_family_product_guid_ada83db3_fk_product_guid" on table "pd_family"
DETAIL: Key (guid)=(902d30b8-26ba-ea11-a812-000d3a5bbb60) is still referenced from table "pd_family".
SQL state: 23503
This is what I have for my models:
class Product(models.Model):
guid = models.UUIDField(primary_key=True)
product = models.CharField(max_length=10)
year = models.IntegerField()
previous_product_name = models.CharField(max_length=10)
class StandardProductFieldsMixin(models.Model):
product_guid = models.ForeignKey('Product', on_delete=models.CASCADE, db_column='product_guid')
class Meta:
abstract = True
class ActiveFieldMixin(models.Model):
active = models.BooleanField()
class Meta:
abstract = True
class Family(StandardProductFieldsMixin, ActiveFieldMixin):
new_code = models.IntegerField(null=True)
code = models.DecimalField(max_digits=20, decimal_places=2)
title = models.CharField(max_length=255)
desc = models.TextField(null=True)
fam_desc = models.TextField(null=True)
When I try to change a value of guid in Product, my expectation is that it would automatically change it in Family as well. I'm trying to change it with something like:
UPDATE product
SET guid = '902D30B8-26BA-EA11-A812-000D3A5BBB6B'
WHERE guid = '902D30B8-26BA-EA11-A812-000D3A5BBB60'
I guess I was under the wrong impression. Do I need to do something additional in the model? Looked at the documentation for something like on_update, but not seeing that either an **options or as a parameter for models.ForeignKey.
From what I gather after reading about it for more than an hour, if I want this kind of functionality I just need to add it Postgres manual, by dropping the constraint and adding it back with ON UPDATE CASCADE.
Apparently I'm under the wrong impression:
https://stackoverflow.com/a/61648910/3123109
Sounds like Django model migrations implement neither ON DELETE nor ON UPDATE database CASCADES options. I guess I'd need to drop into the database and implement them there.

How are many-to-many relationship handled in Django?

I have a shirt which can contain multiple colors, and multiple colors which can have multiple shirts. Normally I would express it the following way:
In django I have the many-to-many (https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/)
Example:
publications = models.ManyToManyField(Publication)
--
Can I create the table "Item_colors" consisting of 2 columns (no "ID" primary key) and design the models according to my diagram using the composite key:
class Item_colors(models.Model):
class Meta:
unique_together = (('cloth_item_id', 'color_id'),)
cloth_item_id = models.ForeignKey(Cloth_item, on_delete=models.CASCADE)
color_id = models.ForeignKey(Color, on_delete=models.CASCADE)
How is the many-to-many relation handled in a DB context, and does it yield better performance?
EDIT: https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys no avoiding primary keys in favor of composite keys saving columns :( at least for now..
How is the many-to-many relation handled in a DB context, and does it yield better performance?
With a junction table in the middle, so with an item_colors table. But the table contains a primary key, as does every model in Django.
If you do not specify a through=… parameter [Django-doc] to define the model for the junction table yourself, Django will automatically create such model. This model then has two ForeignKeys to the two models it connects as discussed in the database representation section of the documentation:
Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship. By default, this table name is generated using the name of the many-to-many field and the name of the table for the model that contains it. Since some databases don’t support table names above a certain length, these table names will be automatically truncated and a uniqueness hash will be used, e.g. author_books_9cdf. You can manually provide the name of the join table using the db_table option.
But the table thus has a primary key. This might be useful if the same object occurs a second time in the relation.
You can access the through model in the Article-Publication example for example with:
Article.publications.through
You thus can define a through model yourself, for example with:
class Color(models.Model):
color = models.CharField(max_length=128)
class ClothItem(models.Model):
item_name = models.CharField(max_length=128)
colors = models.ManyToManyField(
Color,
related_name='cloth_items'
through='ClothItemColors'
)
class ClothItemColors(models.Model):
cloth_item = models.ForeignKey(ClothItem, on_delete=models.CASCADE)
color = models.ForeignKey(Color, on_delete=models.CASCADE)
class Meta:
db_table = 'item_colors'
constraints = [
models.UniqueConstraint(
fields=('cloth_item', 'color'),
name='unique_cloth_color'
)
]
often an explicit through model is used to store extra information, for example the quantity:
class ClothItemColors(models.Model):
cloth_item = models.ForeignKey(ClothItem, on_delete=models.CASCADE)
color = models.ForeignKey(Color, on_delete=models.CASCADE)
quantity = models.IntegerField(default=0)
# …

Django Composite Foreign Key

Is there a way to create a composite foreign in Django and use the key to create and update certain entries?
I have looked at the package django-composite-foreignkey. However, it doesn't provide a way to use the key for creating/updating
As an example:
I have
class tree(models.Model):
id = models.AutoField(primary_key=True)
tree_type = models.CharField(max_length=255)
tree_age = models.IntegerField()
class branch(models.Model):
tree_id = models.ForeignKey('tree',on_delete=models.CASCADE)
branch_id = models.IntegerField()
branch_length = models.FloatField()
Branch_width = models.FloatField()
class Meta:
unique_together(('tree_id','branch_id'),)
class leaf(models.Model):
tree_id = models.ForeignKey('tree',on_delete=models.CASCADE)
branch_id = models.ForeignKey('branch',on_delete=models.CASCADE)
leaf_id = models.IntegerField()
leaf_color = models.CharField(max_length=255)
leaf_length = models.FloatField()
leaf_width = models.FloatField()
worm_bites = models.IntegerField()
class Meta:
unique_together(('tree_id','branch_id','leaf_id')
And I want to create a new leaf say
#not working
leaf.objects.create(tree_id = 2, branch_id = 2, leaf_id = 1, leaf_color = 'brown'.....)
This doesn't work, as it will give me a ForeignKey error. I guess is because the branch_id is not a primary key in BRANCH.
So I am wondering if there is a way to use composite Primary Key / composite Foreign Key in Django?
Thanks!
When creating an object that has a foreign key, you have to pass an object (not just an id) that has been saved to the database previously. For instance:
branch = Branch.objects.get(id=1)
tree = branch.tree
Leaf.objects.create(tree=tree, branch=branch ...)
A few tipps on naming conventions: Classes should be camel cased: Branch, BigBranch and Leaf. Models should be singular. Foreign keys should be named after the model if possible, but without a trailing _id. The Django code actually is an excellent example of Python naming conventions.
As to the composite key business, you don't need composite foreign keys if you design your database accordingly. In this particular case, every item in the hierarchy should only point to the next-higher level. Imagine if you had a Forest. What would you do, add a third foreign key to Leaf? No need: Leaf points to Branch, Branch to Tree, and Tree to Forest, and you can always go through that chain to find which forest a leaf is in without saving a reference to the forest in Leaf.
The Django documentation (which is excellent in many ways) has very useful examples on how to define relationships.

Model with autoincremental non PK key

I'm writing a model where I use an auto incremental key, which is based on two foreign keys and is not the pk.
class Message(models.Model):
message_id = models.IntegerField()
user_1 = models.ForeignKey(User)
user_2 = models.ForeignKey(User)
class Meta:
unique_together = ("message_id", "user_1", "user_2")
As far as I know, an AutoField can't be used for this case.
What is the best way to achieve this. (It might be the case, that two new messages are created at the same time).
Django doesn't support composite primary keys, yet, so best way is pretty much the way you're doing it now.
Keep the automatic id column that Django generates and then add a unique index for the columns that actually are the primary key, the unique index will then take care of ensuring that there's no duplicates.