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.
Related
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))
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)
# …
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?
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.
I've got Django tables like the following (I've removed non-essential fields):
class Person(models.Model):
nameidx = models.IntegerField(primary_key=True)
name = models.CharField(max_length=300, verbose_name="Name")
class Owner(models.Model):
id = models.IntegerField(primary_key=True)
nameidx = models.IntegerField(null=True, blank=True) # is Person.nameidx
structidx = models.IntegerField() # is PlaceRef.structidx
class PlaceRef(models.Model):
id = models.IntegerField(primary_key=True)
structidx = models.IntegerField() # used for many things and not equivalent to placeidx
placeidx = models.IntegerField(null=True, blank=True) # is Place.placeidx
class Place(models.Model):
placeidx = models.IntegerField(primary_key=True)
county = models.CharField(max_length=36, null=True, blank=True)
name = models.CharField(max_length=300)
My question is as follows. If in my views.py file, I have a Person referenced by name and I want to find out all the Places they own as a QuerySet, what should I do?
I can get this far:
person = Person.objects.get(name=name)
owned_relations = Owner.objects.filter(nameidx=nameidx)
How do I get from here to Place? Should I use database methods?
I'm also not sure if I should be using ForeignKey for e.g. Owner.nameidx.
Thanks and apologies for this extremely basic question. I'm not sure how to learn the basics of database queries except by trying, failing, asking SO, trying again... :)
The whole point of foreign keys is for uses like yours. If you already know that Owner.nameidx refers to a Person, why not make it a foreign key (or a OneToOne field) to the Person table? Not only do you get the advantage of referential integrity - it makes it impossible to enter a value for nameidx that isn't a valid Person - the Django ORM will give you the ability to 'follow' the relationships easily:
owned_places = Place.objects.filter(placeref__owner__person=my_person)
will give you all the places owned by my_person.
Incidentally, you don't need to define the separate primary key fields - Django will do it for you, and make them autoincrement fields, which is almost always what you want.
If u could redesign.Then
In owner nameidx can be a foreign key to Person(nameidx)
Placeref(structidx) could be a foreign key to Owner(structidx) and
Place(placeidx) could be a foreign key Place ref(placeidx)
Then u could deduce the place value easily..