Many2many as 'composition' of two One2many - odoo

Suppose I have three objects A, B, C with relationships one A to many B and one A to many C. This naturally implies the existence of a many B to many C relationship, but the implication is clearly not recognized by the computer.
The questions are,
(i) How can this many2many be defined so that it respects the links as given through the already existing relationships?
(ii) Are there any special means of displaying said relationship in the form-view for each of objects B and C?
(iii) Is it possible that this is inherently the meaning of a many2many relationship and that I should just browse through the plethora of non-existent examples in the documentation?

You should be able to define a related fields.Many2many that uses relationships from B to C. See: Related Fields Documentation
For example:
Model_A:
b_ids = fields.One2many(comodel_name='B',
inverse_name='a_id')
c_ids = fields.One2many(comodel_name='C',
inverse_name='a_id')
Model_B:
a_id = fields.Many2one(comodel_name='A')
c_ids = fields.Many2many(comodel_name='C',
related='a_id.c_ids')
Model_C:
a_id = fields.Many2one(comodel_name='A')
b_ids = fields.Many2many(comodel_name='B',
related='a_id.b_ids')
Once you've defined the related fields, all the normal Many2many interactions will work (views, ORM, etc). You can add store=True to the field definition to store the relation in its own database table for easier searching and queries.

Related

Complex Django model relationships

I'm trying to build an SQL database for use in Django. I understand the model from an abstract perspective, but I don't know enough about how to build databases to know if I'm relating these objects correctly. I want to keep this abstract so I can implement the code myself.
Say I have three types of objects: A, B, and C. An A may hold one or more B. A B can hold multiple Bs or Cs in any combination, but it must have at least one (B or C) inside of it. A C merely holds some data and is a simple construct.
Currently I have:
from django.db import models
class A(models.Model):
# A attributes
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
# B attributes
class C(models.Model):
b = models.ForeignKey(B, on_delete=models.CASCADE)
# C attributes
I know that this just allows a Many-To-One relationship between C→B and B→A, but I don't know how to allow a B to refer to another B.
I would also like to be able to easily write a form where you start with an A with a requirement of at least one B inside of it, and you can add or remove Bs at will. Is this possible?
I think there's probably just a better way of setting up this data, but I don't see it since I'm very new to database organization.
If it helps, I'm designing a form to allow easy writing of workouts for swimming. The A is a workout, which has a title and an author. Each B is a set. Sets are composed of things like "2x50yards freestyle" or "8x100 IM on 2:00" — the Cs. But sometimes a set has sort of a sub-set, which is like a loop.
I highly recommend checking out the Django documentation on models which can be found here: Django Models
Moreover, to make a symmetrical Many to Many relationship, use:
class B(models.Model):
bs = models.ManyToManyField("self")
Additionally, I recommend making the relationship between A and B a many to many relationship instead of a foreign key. This will allow you to assign the B to many A's while still allowing 1 A to have many B's. The same logic should potentially be taken for B and C.
To answer your question about making B's required for A, I do not think this is possible. Check out this question for more information: Django 1.7: how to make ManyToManyField required?

How to represent a table with several one-to-many relationships

I have a table, A, whose rows have a many-to-one relationship with several other tables. The rows in the other tables--B, C, D--each have many As. Each A relates exclusively to another table. For example you never have an A that relates to both a B and C.
Currently, the A table is represented as a "flattened" sum-type where each row has a nullable AId, BId, CId, & DId. The "parent" of any given row in A is determined by which one of these FK's in non-null.
This has been working fine so far. However, I have several new features to be implemented down the road which will also have many As.
My question is: is there a more extensible design than simply adding more columns to store FK's to these other tables?
You have a type of MUCK (massive unified code key)table, but only worse.
Instead of a table like this:
MUCK(aid, bid, cid, did, value1)
It would work better like this:
MUCK(table, id, value1)
The table is a,b,c, or d (or so on) and the id is the id within that table.
However, I really suggest you do some research on why MUCK and EAV tables can be a nightmare as you add requirements and features.
Here is a question I recently answered and a link to another that discusses why EAV (and even more, MUCK) is not always the best idea.
How to store custom entity properties in a relational database

Why no many-to-many relationships?

I am learning about databases and SQL for the first time. In the text I'm reading (Oracle 11g: SQL by Joan Casteel), it says that "many-to-many relationships can't exist in a relational database." I understand that we are to avoid them, and I understand how to create a bridging entity to eliminate them, but I am trying to fully understand the statement "can't exist."
Is it actually physically impossible to have a many-to-many relationship represented?
Or is it just very inefficient since it leads to a lot of data duplication?
It seems to me to be the latter case, and the bridging entity minimizes the duplicated data. But maybe I'm missing something? I haven't found a concrete reason (or better yet an example) that explains why to avoid the many-to-many relationship, either in the text or anywhere else I've searched. I've been searching all day and only finding the same information repeated: "don't do it, and use a bridging entity instead." But I like to ask why. :-)
Thanks!
Think about a simple relationship like the one between Authors and Books. An author can write many books. A book could have many authors. Now, without a bridge table to resolve the many-to-many relationship, what would the alternative be? You'd have to add multiple Author_ID columns to the Books table, one for each author. But how many do you add? 2? 3? 10? However many you choose, you'll probably end up with a lot of sparse rows where many of the Author_ID values are NULL and there's a good chance that you'll run across a case where you need "just one more." So then you're either constantly modifying the schema to try to accommodate or you're imposing some artificial restriction ("no book can have more than 3 authors") to force things to fit.
A true many-to-many relationship involving two tables is impossible to create in a relational database. I believe that is what they refer to when they say that it can't exist. In order to implement a many to many you need an intermediary table with basically 3 fields, an ID, an id attached to the first table and an id atached to the second table.
The reason for not wanting many-to-many relationships, is like you said they are incredibly inefficient and managing all the records tied to each side of the relationship can be tough, for instance if you delete a record on one side what happens to the records in the relational table and the table on the other side? Cascading deletes is a slippery slope, at least in my opinion.
Normally (pun intended) you would use a link table to establish many-to-many
Like described by Joe Stefanelli, let's say you had Authors and Books
SELECT * from Author
SELECT * from Books
you would create a JOIN table called AuthorBooks
Then,
SELECT *
FROM Author a
JOIN AuthorBooks ab
on a.AuthorId = ab.AuthorId
JOIN Books b
on ab.BookId = b.BookId
hope that helps.
it says that "many-to-many relationships can't exist in a relational database."
I suspect the author is just being controversial. Technically, in the SQL language, there is no means to explicitly declare a M-M relationship. It is an emergent result of declaring multiple 1-M relations to the table. However, it is a common approach to achieve the result of a M-M relationship and it is absolutely used frequently in databases designed on relational database management systems.
I haven't found a concrete reason (or better yet an example) that explains why to avoid the many-to-many relationship,
They should be used where they are appropriate to be used would be a more accurate way of saying this. There are times, such as the books and authors example given by Joe Stafanelli, where any other solution would be inefficient and introduce other data integrity problems. However, M-M relationships are more complicated to use. They add more work on the part of the GUI designer. Thus, they should only be used where it makes sense to use them. If you are highly confident that one entity should never be associated with more than one of some other entity, then by all means restrict it to a 1-M. For example, if you were tracking the status of a shipment, each shipment can have only a single status at any given time. It would over complicate the design and not make logical sense to allow a shipment to have multiple statuses.
Of course they can (and do) exist. That sounds to me like a soapbox statement. They are required for a great many business applications.
Done properly, they are not inefficient and do not have duplicate data either.
Take a look at FaceBook. How many many-to-many relationships exist between friends and friends of friends? That is a well-defined business need.
The statement that "many-to-many relationships can't exist in a relational database." is patently false.
Many-to-many relationships are in fact very useful, and also common. For example, consider a contact management system which allows you to put people in groups. One person can be in many groups, and each group can have many members.
Representation of these relations requires an extra table--perhaps that's what your book is really saying? In the example I just gave, you'd have a Person table (id, name, address etc) and a Group table (id, group name, etc). Neither contains information about who's in which group; to do that you have a third table (call it PersonGroup) in which each record contains a Person ID and a Group ID--that record represents the relation between the person and the group.
Need to find the members of a group? Your query might look like this (for the group with ID=1):
SELECT Person.firstName, Person.lastName
FROM Person JOIN PersonGroup JOIN Group
ON (PersonGroup.GroupID = 1 AND PersonGroup.PersonID = Person.ID);
It is correct. The Many to Many relationship is broken down into several One to Many relationships. So essentially, NO many to many relationship exists!
Well, of course M-M relationship does exist in relational databases and they also have capability of handling at some level through bridging tables, however as the degree of M-M relationship increases it also increases complexity which results in slow R-W cycles and latency.
It is recommended to avoid such complex M-M relationships in a Relational Database. Graph Databases are the best alternative and good at handling Many to Many relationship between objects and that's why social networking sites uses Graph databases for handling M-M relationship between User and Friends, Users and Events etc.
Let's invent a fictional relationship (many to many relationship) between books and sales table. Suppose you are buying books and for each book you buy needs to generate an invoice number for that book. Suppose also that the invoice number for a book can represent multiple sales to the same customer (not in reality but let's assume). We have a many to many relationship between books and sales entities.
Now if that's the case, how can we get information about only 1 book given that we have purchased 3 books since all books would in theory have the same invoice number? That introduces the main problem of using a many to many relationship I guess. Now if we add a bridging entity between Books and sales such that each book sold have only 1 invoice number, no matter how many books are purchases we can still correctly identify each books.
In a many-to-many relationship there is obvious redundancy as well as insert, update and delete anomaly which should be eliminated by converting it to 2 one-to-many relationship via a bridge table.
M:N relationships should not exist in database design. They are extremely inefficient and do not make for functional databases. Two tables (entities) with a many-to-many relationship (aircraft, airport; teacher, student) cannot both be children of each other, there would be no where to put foreign keys without an intersecting table. aircraft-> flight <- airport; teacher <- class -> student.
An intersection table provides a place for an entity that is dependent on two other tables, for example, a grade needs both a class and a student, a flight needs both an aircraft and an airport. Many-to-many relationships conceal data. Intersection tables reveal this data and create one-to-many relationships that can be more easily understood and worked with. So, the question arises, what table should the flight be in--aircraft or airport. Neither, they should be foreign keys in the intersection table, Flight.

Is it possible to implement an any-any relationship only using 2 tables?

I don't know whether my idea below is applicable:
I have 2 tables, namely A and B.
Each row in table A can be associated with zero or more rows of table B.
Each row in table B can also be associated with zero or more rows of table A.
Table A contains (among others) 2 columns AId (as a primary key) and BId (as a foreign key).
Table B also contains (among others) 2 columns BId (as a primary key) and AId (as a foreign key).
A cascade delete rule is also setup for each foreign key relationship in DB and model class.
It means deleting a row of A will also delete rows, associated with it, of B or deleting a row of B will delete rows, associated with it, of A.
Is it practically possible to do this scenario?
No, not if you are following normal form.
many to Many relationships are a hallmark of needing an intersection table.
More info:
So here's an example. question tagging. A tag can be on multiple questions, and a question can have multiple tags. this is a many to many realtionship. You COULD put multiple entries of tagIds in the Question entity's Tag Column. But you lose A LOT by doing this.
You will not have integrity, because it is VERY difficult to maintain whether or not a tag exists in your tag table as well as in the questions tag column.
This also violates normal form, because a single column cannot have multiple values.
You also cannot easily join on that column, since it has multiple values in it.
I'm assuming that by 'any-any' relationship you are referring to 'many-to-many'.
What you describe in your post is not a many-to-many relation. What you describe is two separate one-to-many relations.
You have a one-to-many relation from TableA to TableB via the AId column in TableB. And you have another one-to-many relation from TableB to TableA via the BId column in TableA. Having two one-to-many relationships in opposite direction is not the same thing as having a many-to-many relationship. Take Stefan's tagging example and consider three queries (QId1, QId2 and QId3) and three tags (TId1, TId2 and TId3). Try to express that all QId1, QId2 and QId3 are tagged each with all TId1, TId2 and TId3. You'll realize that you cannot, because you're trying to express 9 relations in only 6 available 'foreign key' fields. A true many-to-many relation requires up to MxN 'links' possible between two tables of size M and N, while your design allows for M+N (not surprising, since your design is M links in one of the 1-to-many relations and another N links in the other 1-to-many relation).
What you need is a join table. So you have an a and b table that each have a primary key. Then you create an ab table that only has 2 columns. Both are a foreign key. One goes to the a table and the other goes to the b table.
Google for "Database Normalization". You will find lots of examples.
It's possible, but you definitely don't want to do it that way.
You can use a comma separated string of identities in one of the tables. Looking up value from that table to the other is of course a major hassle. Looking up values from the the other table is a nightmare.
Using a cascading trigger with this method is of course out of the question. It might be possible by making an update trigger do the work, but the performance for that would be so bad that it's pointless.
To do this efficiently you absolutely need another table for the relations.

MSLinqToSQLGenerator generates different output than SQLMetal

I have a rather large DBML file and recently discovered that Visual Studio's MSLinqToSQLGenerator is generating different output than:
SqlMetal.exe All.dbml /code:All.designer.vb /namespace:LINQ2FSE /pluralize /provider:SQL2005
It seems to have dropped an arbitrary (and I think relatively small) set of associations from the generated VB code. But SQLMetal works fine. Shouldn't the output be the same?
After further research, I find that the difference seems to be associations on entities that involve properties that are also used on other associations on the same entity with a different number of columns. For example:
Entity A has columns id and name
Entity B has columns id, name and fkA (foreign key to A)
Entity C has columns id, name, fkA and fkB (nullable fkB)
Entity C has association C_A, which links fkA to A.id
it also has association C_B, which links fkA and fkB to B.fkA and B.id
The code for properties supporting C_B will not be generated by Visual Studio, but will be generated by SqlMetal.exe.
Is this kind of association allowed? Is there a reason the code is being generated differently?
It turns out (with the help of Microsoft) that SQLMetal generates different output than the IDE's MSLinqToSQLGenerator because the DBML file (generated by a tool I created) had some relationships defined where the parent could access the children, but the children did not define the parent association. Apparently you are required to define the association from the child to the parent (the foreign key relationship). If you only have an association from the parent to the children defined, and don't have the reverse association defined (or the reverse association has a different name) then the .NET source code for that association will not be generated in either direction. This is handled correctly for MSLinqToSQLGenerator, but SQLMetal does not perform as much validation, apparently, and will generate the association code anyway. Microsoft has reported this issue to development.